diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e268b32..ce540e7 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,26 +1,40 @@ -name: Frontend CI +name: Foundation CI on: push: branches: [main, staging, development] paths: - "frontend/**" + - "backend/**" + - "openapi/**" + - "compose.yaml" + - "compose.production.yaml" + - "scripts/**" + - "supabase/migrations/**" - ".github/workflows/ci.yml" + - ".github/workflows/release-images.yml" pull_request: branches: [main, staging, development] paths: - "frontend/**" + - "backend/**" + - "openapi/**" + - "compose.yaml" + - "compose.production.yaml" + - "scripts/**" + - "supabase/migrations/**" - ".github/workflows/ci.yml" + - ".github/workflows/release-images.yml" permissions: contents: read concurrency: - group: frontend-ci-${{ github.workflow }}-${{ github.ref }} + group: foundation-ci-${{ github.workflow }}-${{ github.ref }} cancel-in-progress: true jobs: - verify: + frontend: name: Frontend verification runs-on: ubuntu-latest timeout-minutes: 15 @@ -43,3 +57,57 @@ jobs: - name: Verify frontend run: npm run verify + + backend: + name: Backend verification + runs-on: ubuntu-latest + timeout-minutes: 15 + defaults: + run: + working-directory: backend + steps: + - name: Check out repository + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + + - name: Set up Go + uses: actions/setup-go@924ae3a1cded613372ab5595356fb5720e22ba16 + with: + go-version-file: backend/go.mod + cache: false + + - name: Test backend + run: go test ./... + + topology: + name: Container topology verification + runs-on: ubuntu-latest + timeout-minutes: 15 + steps: + - name: Check out repository + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + + - name: Validate Compose topology + run: docker compose -f compose.yaml config --quiet + + - name: Validate production topology + env: + JUNTLY_API_IMAGE: example.invalid/juntly-api@sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + JUNTLY_FRONTEND_IMAGE: example.invalid/juntly-frontend@sha256:bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb + DATABASE_URL: postgres://user:password@database.invalid/juntly + CLERK_SECRET_KEY: test-only + CLERK_AUTHORIZED_PARTIES: https://staging.example.invalid + JUNTLY_CONTACT_ENCRYPTION_KEY: MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTIzNDU2Nzg5MDE= + NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY: pk_test_only + STRIPE_SECRET_KEY: sk_test_ci_only + STRIPE_WEBHOOK_SECRET: whsec_ci_only + JUNTLY_PUBLIC_ORIGIN: https://staging.example.invalid + JUNTLY_PLATFORM_FEE_BPS: "1000" + run: docker compose -f compose.production.yaml config --quiet + + - name: Validate operational scripts + run: sh -n scripts/backup-postgres.sh scripts/restore-postgres.sh scripts/smoke.sh + + - name: Validate immutable-image release policy + run: | + python scripts/verify-release-workflow.py + python -m unittest scripts/verify-release-workflow_test.py diff --git a/.github/workflows/release-images.yml b/.github/workflows/release-images.yml new file mode 100644 index 0000000..a01af79 --- /dev/null +++ b/.github/workflows/release-images.yml @@ -0,0 +1,88 @@ +name: Publish immutable images + +on: + workflow_dispatch: + inputs: + ref: + description: Git ref to build + required: true + default: main + type: string + +permissions: + contents: read + packages: write + +concurrency: + group: publish-images-${{ inputs.ref }} + cancel-in-progress: false + +jobs: + build: + name: Build ${{ matrix.name }} image + runs-on: ubuntu-latest + timeout-minutes: 30 + strategy: + fail-fast: false + matrix: + include: + - name: api + image: juntly-api + context: ./backend + dockerfile: ./backend/Dockerfile + - name: frontend + image: juntly-frontend + context: ./frontend + dockerfile: ./frontend/Dockerfile + steps: + - name: Check out requested revision + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + ref: ${{ inputs.ref }} + persist-credentials: false + + - name: Resolve immutable identity + id: source + shell: bash + run: | + set -euo pipefail + source_sha=$(git rev-parse HEAD) + owner=$(printf '%s' "$GITHUB_REPOSITORY_OWNER" | tr '[:upper:]' '[:lower:]') + printf 'sha=%s\n' "$source_sha" >> "$GITHUB_OUTPUT" + printf 'owner=%s\n' "$owner" >> "$GITHUB_OUTPUT" + + - name: Set up Buildx + uses: docker/setup-buildx-action@37fe631027851001ddb9b187196cc803df7f5f0e # v4.3.0 + + - name: Authenticate to GHCR + uses: docker/login-action@dbcb813823bdd20940b903addbd779551569679f # v4.6.0 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ github.token }} + + - name: Build and publish immutable image + id: image + uses: docker/build-push-action@53b7df96c91f9c12dcc8a07bcb9ccacbed38856a # v7.3.0 + with: + context: ${{ matrix.context }} + file: ${{ matrix.dockerfile }} + platforms: linux/amd64 + push: true + provenance: false + sbom: false + tags: ghcr.io/${{ steps.source.outputs.owner }}/${{ matrix.image }}:${{ steps.source.outputs.sha }} + labels: org.opencontainers.image.revision=${{ steps.source.outputs.sha }} + cache-from: type=gha,scope=${{ matrix.name }} + cache-to: type=gha,mode=max,scope=${{ matrix.name }} + + - name: Record published digest + shell: bash + env: + IMAGE_DIGEST: ${{ steps.image.outputs.digest }} + IMAGE_NAME: ghcr.io/${{ steps.source.outputs.owner }}/${{ matrix.image }} + SOURCE_SHA: ${{ steps.source.outputs.sha }} + run: | + set -euo pipefail + test -n "$IMAGE_DIGEST" + printf '### %s\n\n- Source: `%s`\n- Digest: `%s@%s`\n' "$IMAGE_NAME" "$SOURCE_SHA" "$IMAGE_NAME" "$IMAGE_DIGEST" >> "$GITHUB_STEP_SUMMARY" diff --git a/.gitignore b/.gitignore index 0bf12fb..340beeb 100644 --- a/.gitignore +++ b/.gitignore @@ -16,17 +16,23 @@ dist/ build/ coverage/ *.tsbuildinfo +__pycache__/ +*.py[cod] # Go and test output bin/ *.test *.out +*.exe # Local data and generated service state supabase/.branches/ supabase/.temp/ supabase/.env .volumes/ +backups/ +*.dump +*.dump.sha256 # Agent/tool local state .codegraph/ diff --git a/README.md b/README.md index b4998cc..212da3e 100644 --- a/README.md +++ b/README.md @@ -8,25 +8,36 @@ Juntly is a Portuguese local-services marketplace designed to help people discov ## Current status -This repository is at the foundation stage. The initial delivery contains: +This repository contains the production-oriented marketplace MVP: - Durable product, architecture, security, UI, workflow, and decision context. - A localized responsive Next.js frontend shell under `frontend/` after the scaffold commit. - pt-PT default, English support, and Spanish-ready routing/messages. -- Frontend test, format, lint, type, build, audit, CI, and runtime-verification foundations. +- A source-level Clerk frontend identity foundation: localized sign-in/sign-up routes, session-aware navigation, and a server-enforced account route. +- A versioned OpenAPI health contract, generated TypeScript client, same-origin BFF, and narrow Go health API under `backend/`. +- Durable users, provider profiles, listings, discovery, private contact reveal, messaging, quotations, bookings, verified reviews, promotions, subscriptions, moderation, and bounded administration analytics. +- Supabase/PostgreSQL migrations and a local frontend/API Compose topology that connects to an explicitly supplied database. +- A digest-oriented production Compose topology, dependency readiness probe, hardened HTTP/runtime defaults, backup/restore scripts, smoke checks, and operational runbooks. +- Frontend test, format, lint, type, build, dependency-audit, CI, and runtime-verification foundations. -It does **not** yet implement accounts, provider profiles, listings, search, chat, quotations, bookings, reviews, payments, Go/OpenAPI, Clerk, Supabase, Redis, object storage, Docker, or production deployment. +Paid subscriptions and promotions intentionally remain pending until an external payment provider confirms them; free configured entries activate immediately. Production deployment still requires operator-supplied infrastructure, secrets, DNS/TLS, a managed PostgreSQL target, and a real Clerk test account for authenticated acceptance journeys. ## Repository layout ```text juntly/ ├── frontend/ Next.js frontend (created by the scaffold commit) +├── backend/ Go API health-tracer foundation +├── openapi/ versioned API contracts ├── context/ sanitized durable project reference +├── compose.yaml local frontend/API development topology +├── compose.production.yaml hardened immutable-image topology +├── supabase/ ordered PostgreSQL migrations +├── scripts/ backup, restore, and smoke operations └── AGENTS.md project operating rules ``` -`backend/` and `supabase/` will be created only when the approved API-foundation slice implements them. +Deployment and recovery procedures live in [`docs/operations/`](docs/operations/). ## Context diff --git a/backend/.dockerignore b/backend/.dockerignore new file mode 100644 index 0000000..7d39931 --- /dev/null +++ b/backend/.dockerignore @@ -0,0 +1,8 @@ +.git +.env +.env.* +!.env.example +*.key +*.pem +*.log +bin/ diff --git a/backend/.env.example b/backend/.env.example new file mode 100644 index 0000000..54659fa --- /dev/null +++ b/backend/.env.example @@ -0,0 +1,20 @@ +# Server-only Go API configuration. Keep real values in ignored backend/.env.local or the service environment. +DATABASE_URL= +CLERK_SECRET_KEY= +# Optional: server-only AES-256-GCM key for contact reveal. Base64-encoded 32 bytes. +JUNTLY_CONTACT_ENCRYPTION_KEY= +# Optional: enables networkless Clerk JWT verification when configured. +CLERK_JWT_KEY= +# Comma-separated exact allowed Clerk authorized-party origins. +CLERK_AUTHORIZED_PARTIES=http://localhost:4200 +# Optional Go duration, capped at 30s. Leave empty unless a local clock cannot synchronize. +CLERK_CLOCK_SKEW= +# Stripe payments remain disabled when all fields below are empty. If any field is set, +# all required fields must be present or API startup fails closed. +STRIPE_SECRET_KEY= +STRIPE_WEBHOOK_SECRET= +# Optional test-only API override. Production uses https://api.stripe.com. +STRIPE_API_BASE= +JUNTLY_PUBLIC_ORIGIN=https://somosvila.com +# Reviewed commission in basis points (1000 = 10%). Do not derive this in the browser. +JUNTLY_PLATFORM_FEE_BPS= diff --git a/backend/Dockerfile b/backend/Dockerfile new file mode 100644 index 0000000..fa19846 --- /dev/null +++ b/backend/Dockerfile @@ -0,0 +1,21 @@ +FROM golang:1.26.6-alpine AS build + +WORKDIR /src + +COPY go.mod ./ +RUN go mod download + +COPY . ./ +RUN CGO_ENABLED=0 GOOS=linux go build -trimpath -ldflags="-s -w -X main.version=0.1.0" -o /out/juntly-api ./cmd/api + +FROM alpine:3.22 + +RUN apk add --no-cache ca-certificates && addgroup -S juntly && adduser -S -G juntly juntly + +COPY --from=build /out/juntly-api /usr/local/bin/juntly-api + +USER juntly +EXPOSE 8080 +HEALTHCHECK --interval=15s --timeout=3s --start-period=10s --retries=5 CMD wget -q -O /dev/null http://127.0.0.1:8080/api/v1/ready || exit 1 + +ENTRYPOINT ["/usr/local/bin/juntly-api"] diff --git a/backend/cmd/api/config.go b/backend/cmd/api/config.go new file mode 100644 index 0000000..7343081 --- /dev/null +++ b/backend/cmd/api/config.go @@ -0,0 +1,83 @@ +package main + +import ( + "errors" + "strconv" + "strings" + "time" + + "github.com/SourceSenseiTheRealOne/juntly/backend/internal/authn" + "github.com/SourceSenseiTheRealOne/juntly/backend/internal/contactreveal" + "github.com/SourceSenseiTheRealOne/juntly/backend/internal/payments" +) + +var ErrInvalidRuntimeConfig = errors.New("invalid API runtime configuration") + +type runtimeConfig struct { + databaseURL string + verifier authn.Verifier + contactCipher contactreveal.Cipher + paymentGateway payments.Gateway + platformFeeBPS int +} + +func loadRuntimeConfig(lookup func(string) string) (runtimeConfig, error) { + databaseURL := strings.TrimSpace(lookup("DATABASE_URL")) + if databaseURL == "" { + return runtimeConfig{}, ErrInvalidRuntimeConfig + } + clockSkew, err := parseOptionalDuration(lookup("CLERK_CLOCK_SKEW")) + if err != nil { + return runtimeConfig{}, ErrInvalidRuntimeConfig + } + + verifier, err := authn.NewClerkVerifier(authn.ClerkVerifierConfig{ + SecretKey: lookup("CLERK_SECRET_KEY"), + JWTKey: lookup("CLERK_JWT_KEY"), + AuthorizedParties: strings.Split(lookup("CLERK_AUTHORIZED_PARTIES"), ","), + ClockSkew: clockSkew, + }) + if err != nil { + return runtimeConfig{}, ErrInvalidRuntimeConfig + } + var contactCipher contactreveal.Cipher + if encodedKey := strings.TrimSpace(lookup("JUNTLY_CONTACT_ENCRYPTION_KEY")); encodedKey != "" { + contactCipher, err = contactreveal.NewCipher(encodedKey) + if err != nil { + return runtimeConfig{}, ErrInvalidRuntimeConfig + } + } + var paymentGateway payments.Gateway + platformFeeBPS := 0 + stripeSecret := strings.TrimSpace(lookup("STRIPE_SECRET_KEY")) + stripeWebhook := strings.TrimSpace(lookup("STRIPE_WEBHOOK_SECRET")) + publicOrigin := strings.TrimSpace(lookup("JUNTLY_PUBLIC_ORIGIN")) + feeValue := strings.TrimSpace(lookup("JUNTLY_PLATFORM_FEE_BPS")) + if stripeSecret != "" || stripeWebhook != "" || publicOrigin != "" || feeValue != "" { + if stripeSecret == "" || stripeWebhook == "" || publicOrigin == "" || feeValue == "" { + return runtimeConfig{}, ErrInvalidRuntimeConfig + } + platformFeeBPS, err = strconv.Atoi(feeValue) + if err != nil || platformFeeBPS < 0 || platformFeeBPS >= 10_000 { + return runtimeConfig{}, ErrInvalidRuntimeConfig + } + apiBase := strings.TrimSpace(lookup("STRIPE_API_BASE")) + if apiBase == "" { + apiBase = "https://api.stripe.com" + } + paymentGateway, err = payments.NewStripeGateway(payments.StripeConfig{SecretKey: stripeSecret, WebhookSecret: stripeWebhook, APIBase: apiBase, PublicOrigin: publicOrigin, Now: time.Now}) + if err != nil { + return runtimeConfig{}, ErrInvalidRuntimeConfig + } + } + + return runtimeConfig{databaseURL: databaseURL, verifier: verifier, contactCipher: contactCipher, paymentGateway: paymentGateway, platformFeeBPS: platformFeeBPS}, nil +} + +func parseOptionalDuration(value string) (time.Duration, error) { + value = strings.TrimSpace(value) + if value == "" { + return 0, nil + } + return time.ParseDuration(value) +} diff --git a/backend/cmd/api/config_test.go b/backend/cmd/api/config_test.go new file mode 100644 index 0000000..0fc3948 --- /dev/null +++ b/backend/cmd/api/config_test.go @@ -0,0 +1,152 @@ +package main + +import ( + "testing" +) + +func TestLoadRuntimeConfigRejectsMissingDatabaseURL(t *testing.T) { + t.Parallel() + + _, err := loadRuntimeConfig(func(key string) string { + if key == "CLERK_SECRET_KEY" { + return "synthetic-secret" + } + if key == "CLERK_AUTHORIZED_PARTIES" { + return "http://localhost:4200" + } + return "" + }) + if err == nil { + t.Fatal("error = nil, want missing database URL rejection") + } +} + +func TestLoadRuntimeConfigRejectsMissingAuthorizedParties(t *testing.T) { + t.Parallel() + + _, err := loadRuntimeConfig(func(key string) string { + switch key { + case "DATABASE_URL": + return "postgresql://synthetic" + case "CLERK_SECRET_KEY": + return "synthetic-secret" + default: + return "" + } + }) + if err == nil { + t.Fatal("error = nil, want missing authorized-party rejection") + } +} + +func TestLoadRuntimeConfigBuildsVerifierWithoutPersistingCredentials(t *testing.T) { + t.Parallel() + + config, err := loadRuntimeConfig(func(key string) string { + switch key { + case "DATABASE_URL": + return "postgresql://synthetic" + case "CLERK_SECRET_KEY": + return "synthetic-secret" + case "CLERK_AUTHORIZED_PARTIES": + return "http://localhost:4200, https://app.example.test" + default: + return "" + } + }) + if err != nil { + t.Fatalf("load runtime config: %v", err) + } + if config.databaseURL != "postgresql://synthetic" { + t.Fatalf("database URL = %q", config.databaseURL) + } + if config.verifier == nil { + t.Fatal("verifier is nil") + } +} + +func TestLoadRuntimeConfigAcceptsBoundedClerkClockSkew(t *testing.T) { + t.Parallel() + + _, err := loadRuntimeConfig(func(key string) string { + switch key { + case "DATABASE_URL": + return "postgresql://synthetic" + case "CLERK_SECRET_KEY": + return "synthetic-secret" + case "CLERK_AUTHORIZED_PARTIES": + return "http://localhost:4200" + case "CLERK_CLOCK_SKEW": + return "30s" + default: + return "" + } + }) + if err != nil { + t.Fatalf("load runtime config: %v", err) + } +} + +func TestLoadRuntimeConfigRejectsInvalidClerkClockSkew(t *testing.T) { + t.Parallel() + + for _, value := range []string{"not-a-duration", "-1s", "30.000000001s"} { + value := value + t.Run(value, func(t *testing.T) { + t.Parallel() + _, err := loadRuntimeConfig(func(key string) string { + switch key { + case "DATABASE_URL": + return "postgresql://synthetic" + case "CLERK_SECRET_KEY": + return "synthetic-secret" + case "CLERK_AUTHORIZED_PARTIES": + return "http://localhost:4200" + case "CLERK_CLOCK_SKEW": + return value + default: + return "" + } + }) + if err == nil { + t.Fatal("error = nil, want invalid clock-skew rejection") + } + }) + } +} + +func TestLoadRuntimeConfigEnablesStripeOnlyWithCompleteServerConfiguration(t *testing.T) { + t.Parallel() + config, err := loadRuntimeConfig(func(key string) string { + return map[string]string{ + "DATABASE_URL": "postgresql://synthetic", + "CLERK_SECRET_KEY": "synthetic-secret", + "CLERK_AUTHORIZED_PARTIES": "http://localhost:4200", + "STRIPE_SECRET_KEY": "sk_test_synthetic", + "STRIPE_WEBHOOK_SECRET": "whsec_synthetic", + "JUNTLY_PUBLIC_ORIGIN": "https://vila.example", + "JUNTLY_PLATFORM_FEE_BPS": "1000", + }[key] + }) + if err != nil { + t.Fatalf("load runtime config: %v", err) + } + if config.paymentGateway == nil || config.platformFeeBPS != 1000 { + t.Fatalf("payment config = %#v/%d", config.paymentGateway, config.platformFeeBPS) + } +} + +func TestLoadRuntimeConfigRejectsPartialStripeConfiguration(t *testing.T) { + t.Parallel() + _, err := loadRuntimeConfig(func(key string) string { + return map[string]string{ + "DATABASE_URL": "postgresql://synthetic", + "CLERK_SECRET_KEY": "synthetic-secret", + "CLERK_AUTHORIZED_PARTIES": "http://localhost:4200", + "STRIPE_SECRET_KEY": "sk_test_synthetic", + }[key] + }) + if err == nil { + t.Fatal("partial Stripe configuration accepted") + } +} diff --git a/backend/cmd/api/contact_config_test.go b/backend/cmd/api/contact_config_test.go new file mode 100644 index 0000000..32ed14d --- /dev/null +++ b/backend/cmd/api/contact_config_test.go @@ -0,0 +1,45 @@ +package main + +import ( + "encoding/base64" + "testing" +) + +func TestLoadRuntimeConfigBuildsOptionalServerOnlyContactCipher(t *testing.T) { + t.Parallel() + key := make([]byte, 32) + config, err := loadRuntimeConfig(func(name string) string { + switch name { + case "DATABASE_URL": + return "postgresql://synthetic" + case "CLERK_SECRET_KEY": + return "synthetic-secret" + case "CLERK_AUTHORIZED_PARTIES": + return "http://localhost:4200" + case "JUNTLY_CONTACT_ENCRYPTION_KEY": + return base64.StdEncoding.EncodeToString(key) + default: + return "" + } + }) + if err != nil || config.contactCipher == nil { + t.Fatalf("config/cipher = %#v/%v", config, err) + } + _, err = loadRuntimeConfig(func(name string) string { + switch name { + case "DATABASE_URL": + return "postgresql://synthetic" + case "CLERK_SECRET_KEY": + return "synthetic-secret" + case "CLERK_AUTHORIZED_PARTIES": + return "http://localhost:4200" + case "JUNTLY_CONTACT_ENCRYPTION_KEY": + return "invalid" + default: + return "" + } + }) + if err == nil { + t.Fatal("invalid contact key accepted") + } +} diff --git a/backend/cmd/api/main.go b/backend/cmd/api/main.go new file mode 100644 index 0000000..d1c13d6 --- /dev/null +++ b/backend/cmd/api/main.go @@ -0,0 +1,145 @@ +package main + +import ( + "context" + "database/sql" + "errors" + "io" + "log/slog" + "net/http" + "os" + "os/signal" + "syscall" + "time" + + "entgo.io/ent/dialect" + entsql "entgo.io/ent/dialect/sql" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent" + "github.com/SourceSenseiTheRealOne/juntly/backend/internal/accounts" + "github.com/SourceSenseiTheRealOne/juntly/backend/internal/administration" + "github.com/SourceSenseiTheRealOne/juntly/backend/internal/bookings" + "github.com/SourceSenseiTheRealOne/juntly/backend/internal/contactreveal" + "github.com/SourceSenseiTheRealOne/juntly/backend/internal/discovery" + "github.com/SourceSenseiTheRealOne/juntly/backend/internal/entitlements" + "github.com/SourceSenseiTheRealOne/juntly/backend/internal/health" + "github.com/SourceSenseiTheRealOne/juntly/backend/internal/httpapi" + "github.com/SourceSenseiTheRealOne/juntly/backend/internal/listingmedia" + "github.com/SourceSenseiTheRealOne/juntly/backend/internal/listings" + "github.com/SourceSenseiTheRealOne/juntly/backend/internal/messaging" + "github.com/SourceSenseiTheRealOne/juntly/backend/internal/moderation" + "github.com/SourceSenseiTheRealOne/juntly/backend/internal/payments" + "github.com/SourceSenseiTheRealOne/juntly/backend/internal/provideraccess" + "github.com/SourceSenseiTheRealOne/juntly/backend/internal/providers" + "github.com/SourceSenseiTheRealOne/juntly/backend/internal/quotations" + "github.com/SourceSenseiTheRealOne/juntly/backend/internal/readiness" + "github.com/SourceSenseiTheRealOne/juntly/backend/internal/reference" + "github.com/SourceSenseiTheRealOne/juntly/backend/internal/reviews" + "github.com/SourceSenseiTheRealOne/juntly/backend/internal/users" + _ "github.com/jackc/pgx/v5/stdlib" +) + +var version = "0.1.0" + +func main() { + if err := run(); err != nil { + slog.Error("api stopped", "error", err) + os.Exit(1) + } +} + +func run() error { + config, err := loadRuntimeConfig(os.Getenv) + if err != nil { + return err + } + handler, closer, err := newAPIHandler(config) + if err != nil { + return err + } + defer func() { + _ = closer.Close() + }() + + addr := os.Getenv("JUNTLY_API_ADDR") + if addr == "" { + addr = ":8080" + } + + server := &http.Server{ + Addr: addr, + Handler: handler, + ReadHeaderTimeout: 5 * time.Second, + ReadTimeout: 15 * time.Second, + WriteTimeout: 30 * time.Second, + IdleTimeout: 60 * time.Second, + MaxHeaderBytes: 1 << 20, + } + + ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM) + defer stop() + + errs := make(chan error, 1) + go func() { + slog.Info("api listening", "addr", addr) + errs <- server.ListenAndServe() + }() + + select { + case <-ctx.Done(): + shutdownCtx, cancel := context.WithTimeout(context.Background(), 5*time.Second) + defer cancel() + if err := server.Shutdown(shutdownCtx); err != nil { + return err + } + err := <-errs + if errors.Is(err, http.ErrServerClosed) { + return nil + } + return err + case err := <-errs: + if errors.Is(err, http.ErrServerClosed) { + return nil + } + return err + } +} + +func newAPIHandler(config runtimeConfig) (http.Handler, io.Closer, error) { + database, err := sql.Open("pgx", config.databaseURL) + if err != nil { + return nil, nil, err + } + database.SetMaxOpenConns(25) + database.SetMaxIdleConns(10) + database.SetConnMaxLifetime(30 * time.Minute) + database.SetConnMaxIdleTime(5 * time.Minute) + client := ent.NewClient(ent.Driver(entsql.OpenDB(dialect.Postgres, database))) + + healthService := health.NewService(version, time.Now) + readinessService := readiness.NewService(database) + userService := users.NewService(users.NewEntRepository(client)) + accountService := accounts.NewService(userService, accounts.NewEntRepository(client)) + referenceRepository := reference.NewSQLRepository(database) + referenceService := reference.NewService(referenceRepository) + publicDiscovery := discovery.NewService(discovery.NewSQLRepository(database)) + providerAuthorizer := provideraccess.NewService(userService, accountService) + contactChannels := contactreveal.NewProviderChannelService(providerAuthorizer, contactreveal.NewSQLChannelStore(database), config.contactCipher) + contactReveal := contactreveal.NewRevealService(userService, contactreveal.NewSQLRevealStore(database), config.contactCipher, time.Now) + messagingService := messaging.NewService(userService, messaging.NewSQLStore(database)) + quotationService := quotations.NewService(userService, quotations.NewSQLStore(database), time.Now) + bookingService := bookings.NewService(userService, bookings.NewSQLStore(database)) + reviewService := reviews.NewService(userService, reviews.NewSQLStore(database)) + entitlementService := entitlements.NewService(userService, entitlements.NewSQLStore(database)) + administrationService := administration.NewService(userService, administration.NewSQLStore(database)) + providerService := providers.NewService(providerAuthorizer, providers.NewEntRepository(client), referenceRepository) + listingRepository := listings.NewEntRepository(client) + listingDrafts := listings.NewService(providerAuthorizer, listingRepository) + moderatorAuthorizer := moderation.NewService(userService, moderation.NewEntRepository(client)) + paymentService := payments.NewService(userService, moderatorAuthorizer, payments.NewSQLStore(database), config.paymentGateway, config.platformFeeBPS) + listingLifecycle := listings.NewLifecycleService(providerAuthorizer, moderatorAuthorizer, listingRepository) + listingMedia := listingmedia.NewService(providerAuthorizer, listingmedia.NewEntRepository(client), listingmedia.NewUnavailableStorage()) + ownerListings := listings.NewOwnerService(listingDrafts, listingLifecycle, listingMedia) + moderationQueue := moderation.NewQueueService(moderatorAuthorizer, listingRepository) + moderationReview := moderation.NewReviewService(moderationQueue, listingLifecycle) + return httpapi.NewRouter(healthService, readinessService, config.verifier, userService, accountService, referenceService, providerService, ownerListings, moderationReview, publicDiscovery, contactChannels, contactReveal, messagingService, quotationService, bookingService, reviewService, entitlementService, administrationService, paymentService), client, nil +} diff --git a/backend/ent/administrativearea.go b/backend/ent/administrativearea.go new file mode 100644 index 0000000..e9d0619 --- /dev/null +++ b/backend/ent/administrativearea.go @@ -0,0 +1,262 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "fmt" + "strings" + "time" + + "entgo.io/ent" + "entgo.io/ent/dialect/sql" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/administrativearea" + "github.com/google/uuid" +) + +// AdministrativeArea is the model entity for the AdministrativeArea schema. +type AdministrativeArea struct { + config `json:"-"` + // ID of the ent. + ID uuid.UUID `json:"id,omitempty"` + // Source holds the value of the "source" field. + Source string `json:"source,omitempty"` + // SourceVersion holds the value of the "source_version" field. + SourceVersion string `json:"source_version,omitempty"` + // ExternalCode holds the value of the "external_code" field. + ExternalCode string `json:"external_code,omitempty"` + // Kind holds the value of the "kind" field. + Kind string `json:"kind,omitempty"` + // Name holds the value of the "name" field. + Name string `json:"name,omitempty"` + // ParentID holds the value of the "parent_id" field. + ParentID *uuid.UUID `json:"parent_id,omitempty"` + // Active holds the value of the "active" field. + Active bool `json:"active,omitempty"` + // CreatedAt holds the value of the "created_at" field. + CreatedAt time.Time `json:"created_at,omitempty"` + // UpdatedAt holds the value of the "updated_at" field. + UpdatedAt time.Time `json:"updated_at,omitempty"` + // Edges holds the relations/edges for other nodes in the graph. + // The values are being populated by the AdministrativeAreaQuery when eager-loading is set. + Edges AdministrativeAreaEdges `json:"edges"` + selectValues sql.SelectValues +} + +// AdministrativeAreaEdges holds the relations/edges for other nodes in the graph. +type AdministrativeAreaEdges struct { + // Children holds the value of the children edge. + Children []*AdministrativeArea `json:"children,omitempty"` + // Parent holds the value of the parent edge. + Parent *AdministrativeArea `json:"parent,omitempty"` + // Localities holds the value of the localities edge. + Localities []*Locality `json:"localities,omitempty"` + // loadedTypes holds the information for reporting if a + // type was loaded (or requested) in eager-loading or not. + loadedTypes [3]bool +} + +// ChildrenOrErr returns the Children value or an error if the edge +// was not loaded in eager-loading. +func (e AdministrativeAreaEdges) ChildrenOrErr() ([]*AdministrativeArea, error) { + if e.loadedTypes[0] { + return e.Children, nil + } + return nil, &NotLoadedError{edge: "children"} +} + +// ParentOrErr returns the Parent value or an error if the edge +// was not loaded in eager-loading, or loaded but was not found. +func (e AdministrativeAreaEdges) ParentOrErr() (*AdministrativeArea, error) { + if e.Parent != nil { + return e.Parent, nil + } else if e.loadedTypes[1] { + return nil, &NotFoundError{label: administrativearea.Label} + } + return nil, &NotLoadedError{edge: "parent"} +} + +// LocalitiesOrErr returns the Localities value or an error if the edge +// was not loaded in eager-loading. +func (e AdministrativeAreaEdges) LocalitiesOrErr() ([]*Locality, error) { + if e.loadedTypes[2] { + return e.Localities, nil + } + return nil, &NotLoadedError{edge: "localities"} +} + +// scanValues returns the types for scanning values from sql.Rows. +func (*AdministrativeArea) scanValues(columns []string) ([]any, error) { + values := make([]any, len(columns)) + for i := range columns { + switch columns[i] { + case administrativearea.FieldParentID: + values[i] = &sql.NullScanner{S: new(uuid.UUID)} + case administrativearea.FieldActive: + values[i] = new(sql.NullBool) + case administrativearea.FieldSource, administrativearea.FieldSourceVersion, administrativearea.FieldExternalCode, administrativearea.FieldKind, administrativearea.FieldName: + values[i] = new(sql.NullString) + case administrativearea.FieldCreatedAt, administrativearea.FieldUpdatedAt: + values[i] = new(sql.NullTime) + case administrativearea.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 AdministrativeArea fields. +func (_m *AdministrativeArea) 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 administrativearea.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 administrativearea.FieldSource: + if value, ok := values[i].(*sql.NullString); !ok { + return fmt.Errorf("unexpected type %T for field source", values[i]) + } else if value.Valid { + _m.Source = value.String + } + case administrativearea.FieldSourceVersion: + if value, ok := values[i].(*sql.NullString); !ok { + return fmt.Errorf("unexpected type %T for field source_version", values[i]) + } else if value.Valid { + _m.SourceVersion = value.String + } + case administrativearea.FieldExternalCode: + if value, ok := values[i].(*sql.NullString); !ok { + return fmt.Errorf("unexpected type %T for field external_code", values[i]) + } else if value.Valid { + _m.ExternalCode = value.String + } + case administrativearea.FieldKind: + if value, ok := values[i].(*sql.NullString); !ok { + return fmt.Errorf("unexpected type %T for field kind", values[i]) + } else if value.Valid { + _m.Kind = value.String + } + case administrativearea.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 administrativearea.FieldParentID: + if value, ok := values[i].(*sql.NullScanner); !ok { + return fmt.Errorf("unexpected type %T for field parent_id", values[i]) + } else if value.Valid { + _m.ParentID = new(uuid.UUID) + *_m.ParentID = *value.S.(*uuid.UUID) + } + case administrativearea.FieldActive: + if value, ok := values[i].(*sql.NullBool); !ok { + return fmt.Errorf("unexpected type %T for field active", values[i]) + } else if value.Valid { + _m.Active = value.Bool + } + case administrativearea.FieldCreatedAt: + if value, ok := values[i].(*sql.NullTime); !ok { + return fmt.Errorf("unexpected type %T for field created_at", values[i]) + } else if value.Valid { + _m.CreatedAt = value.Time + } + case administrativearea.FieldUpdatedAt: + if value, ok := values[i].(*sql.NullTime); !ok { + return fmt.Errorf("unexpected type %T for field updated_at", values[i]) + } else if value.Valid { + _m.UpdatedAt = value.Time + } + default: + _m.selectValues.Set(columns[i], values[i]) + } + } + return nil +} + +// Value returns the ent.Value that was dynamically selected and assigned to the AdministrativeArea. +// This includes values selected through modifiers, order, etc. +func (_m *AdministrativeArea) Value(name string) (ent.Value, error) { + return _m.selectValues.Get(name) +} + +// QueryChildren queries the "children" edge of the AdministrativeArea entity. +func (_m *AdministrativeArea) QueryChildren() *AdministrativeAreaQuery { + return NewAdministrativeAreaClient(_m.config).QueryChildren(_m) +} + +// QueryParent queries the "parent" edge of the AdministrativeArea entity. +func (_m *AdministrativeArea) QueryParent() *AdministrativeAreaQuery { + return NewAdministrativeAreaClient(_m.config).QueryParent(_m) +} + +// QueryLocalities queries the "localities" edge of the AdministrativeArea entity. +func (_m *AdministrativeArea) QueryLocalities() *LocalityQuery { + return NewAdministrativeAreaClient(_m.config).QueryLocalities(_m) +} + +// Update returns a builder for updating this AdministrativeArea. +// Note that you need to call AdministrativeArea.Unwrap() before calling this method if this AdministrativeArea +// was returned from a transaction, and the transaction was committed or rolled back. +func (_m *AdministrativeArea) Update() *AdministrativeAreaUpdateOne { + return NewAdministrativeAreaClient(_m.config).UpdateOne(_m) +} + +// Unwrap unwraps the AdministrativeArea 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 *AdministrativeArea) Unwrap() *AdministrativeArea { + _tx, ok := _m.config.driver.(*txDriver) + if !ok { + panic("ent: AdministrativeArea is not a transactional entity") + } + _m.config.driver = _tx.drv + return _m +} + +// String implements the fmt.Stringer. +func (_m *AdministrativeArea) String() string { + var builder strings.Builder + builder.WriteString("AdministrativeArea(") + builder.WriteString(fmt.Sprintf("id=%v, ", _m.ID)) + builder.WriteString("source=") + builder.WriteString(_m.Source) + builder.WriteString(", ") + builder.WriteString("source_version=") + builder.WriteString(_m.SourceVersion) + builder.WriteString(", ") + builder.WriteString("external_code=") + builder.WriteString(_m.ExternalCode) + builder.WriteString(", ") + builder.WriteString("kind=") + builder.WriteString(_m.Kind) + builder.WriteString(", ") + builder.WriteString("name=") + builder.WriteString(_m.Name) + builder.WriteString(", ") + if v := _m.ParentID; v != nil { + builder.WriteString("parent_id=") + builder.WriteString(fmt.Sprintf("%v", *v)) + } + builder.WriteString(", ") + builder.WriteString("active=") + builder.WriteString(fmt.Sprintf("%v", _m.Active)) + builder.WriteString(", ") + builder.WriteString("created_at=") + builder.WriteString(_m.CreatedAt.Format(time.ANSIC)) + builder.WriteString(", ") + builder.WriteString("updated_at=") + builder.WriteString(_m.UpdatedAt.Format(time.ANSIC)) + builder.WriteByte(')') + return builder.String() +} + +// AdministrativeAreas is a parsable slice of AdministrativeArea. +type AdministrativeAreas []*AdministrativeArea diff --git a/backend/ent/administrativearea/administrativearea.go b/backend/ent/administrativearea/administrativearea.go new file mode 100644 index 0000000..14afc03 --- /dev/null +++ b/backend/ent/administrativearea/administrativearea.go @@ -0,0 +1,215 @@ +// Code generated by ent, DO NOT EDIT. + +package administrativearea + +import ( + "time" + + "entgo.io/ent/dialect/sql" + "entgo.io/ent/dialect/sql/sqlgraph" + "github.com/google/uuid" +) + +const ( + // Label holds the string label denoting the administrativearea type in the database. + Label = "administrative_area" + // FieldID holds the string denoting the id field in the database. + FieldID = "id" + // FieldSource holds the string denoting the source field in the database. + FieldSource = "source" + // FieldSourceVersion holds the string denoting the source_version field in the database. + FieldSourceVersion = "source_version" + // FieldExternalCode holds the string denoting the external_code field in the database. + FieldExternalCode = "external_code" + // FieldKind holds the string denoting the kind field in the database. + FieldKind = "kind" + // FieldName holds the string denoting the name field in the database. + FieldName = "name" + // FieldParentID holds the string denoting the parent_id field in the database. + FieldParentID = "parent_id" + // FieldActive holds the string denoting the active field in the database. + FieldActive = "active" + // FieldCreatedAt holds the string denoting the created_at field in the database. + FieldCreatedAt = "created_at" + // FieldUpdatedAt holds the string denoting the updated_at field in the database. + FieldUpdatedAt = "updated_at" + // EdgeChildren holds the string denoting the children edge name in mutations. + EdgeChildren = "children" + // EdgeParent holds the string denoting the parent edge name in mutations. + EdgeParent = "parent" + // EdgeLocalities holds the string denoting the localities edge name in mutations. + EdgeLocalities = "localities" + // Table holds the table name of the administrativearea in the database. + Table = "administrative_areas" + // ChildrenTable is the table that holds the children relation/edge. + ChildrenTable = "administrative_areas" + // ChildrenColumn is the table column denoting the children relation/edge. + ChildrenColumn = "parent_id" + // ParentTable is the table that holds the parent relation/edge. + ParentTable = "administrative_areas" + // ParentColumn is the table column denoting the parent relation/edge. + ParentColumn = "parent_id" + // LocalitiesTable is the table that holds the localities relation/edge. + LocalitiesTable = "localities" + // LocalitiesInverseTable is the table name for the Locality entity. + // It exists in this package in order to avoid circular dependency with the "locality" package. + LocalitiesInverseTable = "localities" + // LocalitiesColumn is the table column denoting the localities relation/edge. + LocalitiesColumn = "parent_parish_id" +) + +// Columns holds all SQL columns for administrativearea fields. +var Columns = []string{ + FieldID, + FieldSource, + FieldSourceVersion, + FieldExternalCode, + FieldKind, + FieldName, + FieldParentID, + FieldActive, + FieldCreatedAt, + FieldUpdatedAt, +} + +// 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 ( + // SourceValidator is a validator for the "source" field. It is called by the builders before save. + SourceValidator func(string) error + // SourceVersionValidator is a validator for the "source_version" field. It is called by the builders before save. + SourceVersionValidator func(string) error + // ExternalCodeValidator is a validator for the "external_code" field. It is called by the builders before save. + ExternalCodeValidator func(string) error + // KindValidator is a validator for the "kind" field. It is called by the builders before save. + KindValidator func(string) error + // NameValidator is a validator for the "name" field. It is called by the builders before save. + NameValidator func(string) error + // DefaultActive holds the default value on creation for the "active" field. + DefaultActive bool + // DefaultCreatedAt holds the default value on creation for the "created_at" field. + DefaultCreatedAt func() time.Time + // DefaultUpdatedAt holds the default value on creation for the "updated_at" field. + DefaultUpdatedAt func() time.Time + // UpdateDefaultUpdatedAt holds the default value on update for the "updated_at" field. + UpdateDefaultUpdatedAt func() time.Time + // DefaultID holds the default value on creation for the "id" field. + DefaultID func() uuid.UUID +) + +// OrderOption defines the ordering options for the AdministrativeArea 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() +} + +// BySource orders the results by the source field. +func BySource(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldSource, opts...).ToFunc() +} + +// BySourceVersion orders the results by the source_version field. +func BySourceVersion(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldSourceVersion, opts...).ToFunc() +} + +// ByExternalCode orders the results by the external_code field. +func ByExternalCode(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldExternalCode, opts...).ToFunc() +} + +// ByKind orders the results by the kind field. +func ByKind(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldKind, opts...).ToFunc() +} + +// ByName orders the results by the name field. +func ByName(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldName, opts...).ToFunc() +} + +// ByParentID orders the results by the parent_id field. +func ByParentID(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldParentID, opts...).ToFunc() +} + +// ByActive orders the results by the active field. +func ByActive(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldActive, opts...).ToFunc() +} + +// ByCreatedAt orders the results by the created_at field. +func ByCreatedAt(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldCreatedAt, opts...).ToFunc() +} + +// ByUpdatedAt orders the results by the updated_at field. +func ByUpdatedAt(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldUpdatedAt, opts...).ToFunc() +} + +// ByChildrenCount orders the results by children count. +func ByChildrenCount(opts ...sql.OrderTermOption) OrderOption { + return func(s *sql.Selector) { + sqlgraph.OrderByNeighborsCount(s, newChildrenStep(), opts...) + } +} + +// ByChildren orders the results by children terms. +func ByChildren(term sql.OrderTerm, terms ...sql.OrderTerm) OrderOption { + return func(s *sql.Selector) { + sqlgraph.OrderByNeighborTerms(s, newChildrenStep(), append([]sql.OrderTerm{term}, terms...)...) + } +} + +// ByParentField orders the results by parent field. +func ByParentField(field string, opts ...sql.OrderTermOption) OrderOption { + return func(s *sql.Selector) { + sqlgraph.OrderByNeighborTerms(s, newParentStep(), sql.OrderByField(field, opts...)) + } +} + +// ByLocalitiesCount orders the results by localities count. +func ByLocalitiesCount(opts ...sql.OrderTermOption) OrderOption { + return func(s *sql.Selector) { + sqlgraph.OrderByNeighborsCount(s, newLocalitiesStep(), opts...) + } +} + +// ByLocalities orders the results by localities terms. +func ByLocalities(term sql.OrderTerm, terms ...sql.OrderTerm) OrderOption { + return func(s *sql.Selector) { + sqlgraph.OrderByNeighborTerms(s, newLocalitiesStep(), append([]sql.OrderTerm{term}, terms...)...) + } +} +func newChildrenStep() *sqlgraph.Step { + return sqlgraph.NewStep( + sqlgraph.From(Table, FieldID), + sqlgraph.To(Table, FieldID), + sqlgraph.Edge(sqlgraph.O2M, false, ChildrenTable, ChildrenColumn), + ) +} +func newParentStep() *sqlgraph.Step { + return sqlgraph.NewStep( + sqlgraph.From(Table, FieldID), + sqlgraph.To(Table, FieldID), + sqlgraph.Edge(sqlgraph.M2O, true, ParentTable, ParentColumn), + ) +} +func newLocalitiesStep() *sqlgraph.Step { + return sqlgraph.NewStep( + sqlgraph.From(Table, FieldID), + sqlgraph.To(LocalitiesInverseTable, FieldID), + sqlgraph.Edge(sqlgraph.O2M, false, LocalitiesTable, LocalitiesColumn), + ) +} diff --git a/backend/ent/administrativearea/where.go b/backend/ent/administrativearea/where.go new file mode 100644 index 0000000..d082bc5 --- /dev/null +++ b/backend/ent/administrativearea/where.go @@ -0,0 +1,631 @@ +// Code generated by ent, DO NOT EDIT. + +package administrativearea + +import ( + "time" + + "entgo.io/ent/dialect/sql" + "entgo.io/ent/dialect/sql/sqlgraph" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/predicate" + "github.com/google/uuid" +) + +// ID filters vertices based on their ID field. +func ID(id uuid.UUID) predicate.AdministrativeArea { + return predicate.AdministrativeArea(sql.FieldEQ(FieldID, id)) +} + +// IDEQ applies the EQ predicate on the ID field. +func IDEQ(id uuid.UUID) predicate.AdministrativeArea { + return predicate.AdministrativeArea(sql.FieldEQ(FieldID, id)) +} + +// IDNEQ applies the NEQ predicate on the ID field. +func IDNEQ(id uuid.UUID) predicate.AdministrativeArea { + return predicate.AdministrativeArea(sql.FieldNEQ(FieldID, id)) +} + +// IDIn applies the In predicate on the ID field. +func IDIn(ids ...uuid.UUID) predicate.AdministrativeArea { + return predicate.AdministrativeArea(sql.FieldIn(FieldID, ids...)) +} + +// IDNotIn applies the NotIn predicate on the ID field. +func IDNotIn(ids ...uuid.UUID) predicate.AdministrativeArea { + return predicate.AdministrativeArea(sql.FieldNotIn(FieldID, ids...)) +} + +// IDGT applies the GT predicate on the ID field. +func IDGT(id uuid.UUID) predicate.AdministrativeArea { + return predicate.AdministrativeArea(sql.FieldGT(FieldID, id)) +} + +// IDGTE applies the GTE predicate on the ID field. +func IDGTE(id uuid.UUID) predicate.AdministrativeArea { + return predicate.AdministrativeArea(sql.FieldGTE(FieldID, id)) +} + +// IDLT applies the LT predicate on the ID field. +func IDLT(id uuid.UUID) predicate.AdministrativeArea { + return predicate.AdministrativeArea(sql.FieldLT(FieldID, id)) +} + +// IDLTE applies the LTE predicate on the ID field. +func IDLTE(id uuid.UUID) predicate.AdministrativeArea { + return predicate.AdministrativeArea(sql.FieldLTE(FieldID, id)) +} + +// Source applies equality check predicate on the "source" field. It's identical to SourceEQ. +func Source(v string) predicate.AdministrativeArea { + return predicate.AdministrativeArea(sql.FieldEQ(FieldSource, v)) +} + +// SourceVersion applies equality check predicate on the "source_version" field. It's identical to SourceVersionEQ. +func SourceVersion(v string) predicate.AdministrativeArea { + return predicate.AdministrativeArea(sql.FieldEQ(FieldSourceVersion, v)) +} + +// ExternalCode applies equality check predicate on the "external_code" field. It's identical to ExternalCodeEQ. +func ExternalCode(v string) predicate.AdministrativeArea { + return predicate.AdministrativeArea(sql.FieldEQ(FieldExternalCode, v)) +} + +// Kind applies equality check predicate on the "kind" field. It's identical to KindEQ. +func Kind(v string) predicate.AdministrativeArea { + return predicate.AdministrativeArea(sql.FieldEQ(FieldKind, v)) +} + +// Name applies equality check predicate on the "name" field. It's identical to NameEQ. +func Name(v string) predicate.AdministrativeArea { + return predicate.AdministrativeArea(sql.FieldEQ(FieldName, v)) +} + +// ParentID applies equality check predicate on the "parent_id" field. It's identical to ParentIDEQ. +func ParentID(v uuid.UUID) predicate.AdministrativeArea { + return predicate.AdministrativeArea(sql.FieldEQ(FieldParentID, v)) +} + +// Active applies equality check predicate on the "active" field. It's identical to ActiveEQ. +func Active(v bool) predicate.AdministrativeArea { + return predicate.AdministrativeArea(sql.FieldEQ(FieldActive, v)) +} + +// CreatedAt applies equality check predicate on the "created_at" field. It's identical to CreatedAtEQ. +func CreatedAt(v time.Time) predicate.AdministrativeArea { + return predicate.AdministrativeArea(sql.FieldEQ(FieldCreatedAt, v)) +} + +// UpdatedAt applies equality check predicate on the "updated_at" field. It's identical to UpdatedAtEQ. +func UpdatedAt(v time.Time) predicate.AdministrativeArea { + return predicate.AdministrativeArea(sql.FieldEQ(FieldUpdatedAt, v)) +} + +// SourceEQ applies the EQ predicate on the "source" field. +func SourceEQ(v string) predicate.AdministrativeArea { + return predicate.AdministrativeArea(sql.FieldEQ(FieldSource, v)) +} + +// SourceNEQ applies the NEQ predicate on the "source" field. +func SourceNEQ(v string) predicate.AdministrativeArea { + return predicate.AdministrativeArea(sql.FieldNEQ(FieldSource, v)) +} + +// SourceIn applies the In predicate on the "source" field. +func SourceIn(vs ...string) predicate.AdministrativeArea { + return predicate.AdministrativeArea(sql.FieldIn(FieldSource, vs...)) +} + +// SourceNotIn applies the NotIn predicate on the "source" field. +func SourceNotIn(vs ...string) predicate.AdministrativeArea { + return predicate.AdministrativeArea(sql.FieldNotIn(FieldSource, vs...)) +} + +// SourceGT applies the GT predicate on the "source" field. +func SourceGT(v string) predicate.AdministrativeArea { + return predicate.AdministrativeArea(sql.FieldGT(FieldSource, v)) +} + +// SourceGTE applies the GTE predicate on the "source" field. +func SourceGTE(v string) predicate.AdministrativeArea { + return predicate.AdministrativeArea(sql.FieldGTE(FieldSource, v)) +} + +// SourceLT applies the LT predicate on the "source" field. +func SourceLT(v string) predicate.AdministrativeArea { + return predicate.AdministrativeArea(sql.FieldLT(FieldSource, v)) +} + +// SourceLTE applies the LTE predicate on the "source" field. +func SourceLTE(v string) predicate.AdministrativeArea { + return predicate.AdministrativeArea(sql.FieldLTE(FieldSource, v)) +} + +// SourceContains applies the Contains predicate on the "source" field. +func SourceContains(v string) predicate.AdministrativeArea { + return predicate.AdministrativeArea(sql.FieldContains(FieldSource, v)) +} + +// SourceHasPrefix applies the HasPrefix predicate on the "source" field. +func SourceHasPrefix(v string) predicate.AdministrativeArea { + return predicate.AdministrativeArea(sql.FieldHasPrefix(FieldSource, v)) +} + +// SourceHasSuffix applies the HasSuffix predicate on the "source" field. +func SourceHasSuffix(v string) predicate.AdministrativeArea { + return predicate.AdministrativeArea(sql.FieldHasSuffix(FieldSource, v)) +} + +// SourceEqualFold applies the EqualFold predicate on the "source" field. +func SourceEqualFold(v string) predicate.AdministrativeArea { + return predicate.AdministrativeArea(sql.FieldEqualFold(FieldSource, v)) +} + +// SourceContainsFold applies the ContainsFold predicate on the "source" field. +func SourceContainsFold(v string) predicate.AdministrativeArea { + return predicate.AdministrativeArea(sql.FieldContainsFold(FieldSource, v)) +} + +// SourceVersionEQ applies the EQ predicate on the "source_version" field. +func SourceVersionEQ(v string) predicate.AdministrativeArea { + return predicate.AdministrativeArea(sql.FieldEQ(FieldSourceVersion, v)) +} + +// SourceVersionNEQ applies the NEQ predicate on the "source_version" field. +func SourceVersionNEQ(v string) predicate.AdministrativeArea { + return predicate.AdministrativeArea(sql.FieldNEQ(FieldSourceVersion, v)) +} + +// SourceVersionIn applies the In predicate on the "source_version" field. +func SourceVersionIn(vs ...string) predicate.AdministrativeArea { + return predicate.AdministrativeArea(sql.FieldIn(FieldSourceVersion, vs...)) +} + +// SourceVersionNotIn applies the NotIn predicate on the "source_version" field. +func SourceVersionNotIn(vs ...string) predicate.AdministrativeArea { + return predicate.AdministrativeArea(sql.FieldNotIn(FieldSourceVersion, vs...)) +} + +// SourceVersionGT applies the GT predicate on the "source_version" field. +func SourceVersionGT(v string) predicate.AdministrativeArea { + return predicate.AdministrativeArea(sql.FieldGT(FieldSourceVersion, v)) +} + +// SourceVersionGTE applies the GTE predicate on the "source_version" field. +func SourceVersionGTE(v string) predicate.AdministrativeArea { + return predicate.AdministrativeArea(sql.FieldGTE(FieldSourceVersion, v)) +} + +// SourceVersionLT applies the LT predicate on the "source_version" field. +func SourceVersionLT(v string) predicate.AdministrativeArea { + return predicate.AdministrativeArea(sql.FieldLT(FieldSourceVersion, v)) +} + +// SourceVersionLTE applies the LTE predicate on the "source_version" field. +func SourceVersionLTE(v string) predicate.AdministrativeArea { + return predicate.AdministrativeArea(sql.FieldLTE(FieldSourceVersion, v)) +} + +// SourceVersionContains applies the Contains predicate on the "source_version" field. +func SourceVersionContains(v string) predicate.AdministrativeArea { + return predicate.AdministrativeArea(sql.FieldContains(FieldSourceVersion, v)) +} + +// SourceVersionHasPrefix applies the HasPrefix predicate on the "source_version" field. +func SourceVersionHasPrefix(v string) predicate.AdministrativeArea { + return predicate.AdministrativeArea(sql.FieldHasPrefix(FieldSourceVersion, v)) +} + +// SourceVersionHasSuffix applies the HasSuffix predicate on the "source_version" field. +func SourceVersionHasSuffix(v string) predicate.AdministrativeArea { + return predicate.AdministrativeArea(sql.FieldHasSuffix(FieldSourceVersion, v)) +} + +// SourceVersionEqualFold applies the EqualFold predicate on the "source_version" field. +func SourceVersionEqualFold(v string) predicate.AdministrativeArea { + return predicate.AdministrativeArea(sql.FieldEqualFold(FieldSourceVersion, v)) +} + +// SourceVersionContainsFold applies the ContainsFold predicate on the "source_version" field. +func SourceVersionContainsFold(v string) predicate.AdministrativeArea { + return predicate.AdministrativeArea(sql.FieldContainsFold(FieldSourceVersion, v)) +} + +// ExternalCodeEQ applies the EQ predicate on the "external_code" field. +func ExternalCodeEQ(v string) predicate.AdministrativeArea { + return predicate.AdministrativeArea(sql.FieldEQ(FieldExternalCode, v)) +} + +// ExternalCodeNEQ applies the NEQ predicate on the "external_code" field. +func ExternalCodeNEQ(v string) predicate.AdministrativeArea { + return predicate.AdministrativeArea(sql.FieldNEQ(FieldExternalCode, v)) +} + +// ExternalCodeIn applies the In predicate on the "external_code" field. +func ExternalCodeIn(vs ...string) predicate.AdministrativeArea { + return predicate.AdministrativeArea(sql.FieldIn(FieldExternalCode, vs...)) +} + +// ExternalCodeNotIn applies the NotIn predicate on the "external_code" field. +func ExternalCodeNotIn(vs ...string) predicate.AdministrativeArea { + return predicate.AdministrativeArea(sql.FieldNotIn(FieldExternalCode, vs...)) +} + +// ExternalCodeGT applies the GT predicate on the "external_code" field. +func ExternalCodeGT(v string) predicate.AdministrativeArea { + return predicate.AdministrativeArea(sql.FieldGT(FieldExternalCode, v)) +} + +// ExternalCodeGTE applies the GTE predicate on the "external_code" field. +func ExternalCodeGTE(v string) predicate.AdministrativeArea { + return predicate.AdministrativeArea(sql.FieldGTE(FieldExternalCode, v)) +} + +// ExternalCodeLT applies the LT predicate on the "external_code" field. +func ExternalCodeLT(v string) predicate.AdministrativeArea { + return predicate.AdministrativeArea(sql.FieldLT(FieldExternalCode, v)) +} + +// ExternalCodeLTE applies the LTE predicate on the "external_code" field. +func ExternalCodeLTE(v string) predicate.AdministrativeArea { + return predicate.AdministrativeArea(sql.FieldLTE(FieldExternalCode, v)) +} + +// ExternalCodeContains applies the Contains predicate on the "external_code" field. +func ExternalCodeContains(v string) predicate.AdministrativeArea { + return predicate.AdministrativeArea(sql.FieldContains(FieldExternalCode, v)) +} + +// ExternalCodeHasPrefix applies the HasPrefix predicate on the "external_code" field. +func ExternalCodeHasPrefix(v string) predicate.AdministrativeArea { + return predicate.AdministrativeArea(sql.FieldHasPrefix(FieldExternalCode, v)) +} + +// ExternalCodeHasSuffix applies the HasSuffix predicate on the "external_code" field. +func ExternalCodeHasSuffix(v string) predicate.AdministrativeArea { + return predicate.AdministrativeArea(sql.FieldHasSuffix(FieldExternalCode, v)) +} + +// ExternalCodeEqualFold applies the EqualFold predicate on the "external_code" field. +func ExternalCodeEqualFold(v string) predicate.AdministrativeArea { + return predicate.AdministrativeArea(sql.FieldEqualFold(FieldExternalCode, v)) +} + +// ExternalCodeContainsFold applies the ContainsFold predicate on the "external_code" field. +func ExternalCodeContainsFold(v string) predicate.AdministrativeArea { + return predicate.AdministrativeArea(sql.FieldContainsFold(FieldExternalCode, v)) +} + +// KindEQ applies the EQ predicate on the "kind" field. +func KindEQ(v string) predicate.AdministrativeArea { + return predicate.AdministrativeArea(sql.FieldEQ(FieldKind, v)) +} + +// KindNEQ applies the NEQ predicate on the "kind" field. +func KindNEQ(v string) predicate.AdministrativeArea { + return predicate.AdministrativeArea(sql.FieldNEQ(FieldKind, v)) +} + +// KindIn applies the In predicate on the "kind" field. +func KindIn(vs ...string) predicate.AdministrativeArea { + return predicate.AdministrativeArea(sql.FieldIn(FieldKind, vs...)) +} + +// KindNotIn applies the NotIn predicate on the "kind" field. +func KindNotIn(vs ...string) predicate.AdministrativeArea { + return predicate.AdministrativeArea(sql.FieldNotIn(FieldKind, vs...)) +} + +// KindGT applies the GT predicate on the "kind" field. +func KindGT(v string) predicate.AdministrativeArea { + return predicate.AdministrativeArea(sql.FieldGT(FieldKind, v)) +} + +// KindGTE applies the GTE predicate on the "kind" field. +func KindGTE(v string) predicate.AdministrativeArea { + return predicate.AdministrativeArea(sql.FieldGTE(FieldKind, v)) +} + +// KindLT applies the LT predicate on the "kind" field. +func KindLT(v string) predicate.AdministrativeArea { + return predicate.AdministrativeArea(sql.FieldLT(FieldKind, v)) +} + +// KindLTE applies the LTE predicate on the "kind" field. +func KindLTE(v string) predicate.AdministrativeArea { + return predicate.AdministrativeArea(sql.FieldLTE(FieldKind, v)) +} + +// KindContains applies the Contains predicate on the "kind" field. +func KindContains(v string) predicate.AdministrativeArea { + return predicate.AdministrativeArea(sql.FieldContains(FieldKind, v)) +} + +// KindHasPrefix applies the HasPrefix predicate on the "kind" field. +func KindHasPrefix(v string) predicate.AdministrativeArea { + return predicate.AdministrativeArea(sql.FieldHasPrefix(FieldKind, v)) +} + +// KindHasSuffix applies the HasSuffix predicate on the "kind" field. +func KindHasSuffix(v string) predicate.AdministrativeArea { + return predicate.AdministrativeArea(sql.FieldHasSuffix(FieldKind, v)) +} + +// KindEqualFold applies the EqualFold predicate on the "kind" field. +func KindEqualFold(v string) predicate.AdministrativeArea { + return predicate.AdministrativeArea(sql.FieldEqualFold(FieldKind, v)) +} + +// KindContainsFold applies the ContainsFold predicate on the "kind" field. +func KindContainsFold(v string) predicate.AdministrativeArea { + return predicate.AdministrativeArea(sql.FieldContainsFold(FieldKind, v)) +} + +// NameEQ applies the EQ predicate on the "name" field. +func NameEQ(v string) predicate.AdministrativeArea { + return predicate.AdministrativeArea(sql.FieldEQ(FieldName, v)) +} + +// NameNEQ applies the NEQ predicate on the "name" field. +func NameNEQ(v string) predicate.AdministrativeArea { + return predicate.AdministrativeArea(sql.FieldNEQ(FieldName, v)) +} + +// NameIn applies the In predicate on the "name" field. +func NameIn(vs ...string) predicate.AdministrativeArea { + return predicate.AdministrativeArea(sql.FieldIn(FieldName, vs...)) +} + +// NameNotIn applies the NotIn predicate on the "name" field. +func NameNotIn(vs ...string) predicate.AdministrativeArea { + return predicate.AdministrativeArea(sql.FieldNotIn(FieldName, vs...)) +} + +// NameGT applies the GT predicate on the "name" field. +func NameGT(v string) predicate.AdministrativeArea { + return predicate.AdministrativeArea(sql.FieldGT(FieldName, v)) +} + +// NameGTE applies the GTE predicate on the "name" field. +func NameGTE(v string) predicate.AdministrativeArea { + return predicate.AdministrativeArea(sql.FieldGTE(FieldName, v)) +} + +// NameLT applies the LT predicate on the "name" field. +func NameLT(v string) predicate.AdministrativeArea { + return predicate.AdministrativeArea(sql.FieldLT(FieldName, v)) +} + +// NameLTE applies the LTE predicate on the "name" field. +func NameLTE(v string) predicate.AdministrativeArea { + return predicate.AdministrativeArea(sql.FieldLTE(FieldName, v)) +} + +// NameContains applies the Contains predicate on the "name" field. +func NameContains(v string) predicate.AdministrativeArea { + return predicate.AdministrativeArea(sql.FieldContains(FieldName, v)) +} + +// NameHasPrefix applies the HasPrefix predicate on the "name" field. +func NameHasPrefix(v string) predicate.AdministrativeArea { + return predicate.AdministrativeArea(sql.FieldHasPrefix(FieldName, v)) +} + +// NameHasSuffix applies the HasSuffix predicate on the "name" field. +func NameHasSuffix(v string) predicate.AdministrativeArea { + return predicate.AdministrativeArea(sql.FieldHasSuffix(FieldName, v)) +} + +// NameEqualFold applies the EqualFold predicate on the "name" field. +func NameEqualFold(v string) predicate.AdministrativeArea { + return predicate.AdministrativeArea(sql.FieldEqualFold(FieldName, v)) +} + +// NameContainsFold applies the ContainsFold predicate on the "name" field. +func NameContainsFold(v string) predicate.AdministrativeArea { + return predicate.AdministrativeArea(sql.FieldContainsFold(FieldName, v)) +} + +// ParentIDEQ applies the EQ predicate on the "parent_id" field. +func ParentIDEQ(v uuid.UUID) predicate.AdministrativeArea { + return predicate.AdministrativeArea(sql.FieldEQ(FieldParentID, v)) +} + +// ParentIDNEQ applies the NEQ predicate on the "parent_id" field. +func ParentIDNEQ(v uuid.UUID) predicate.AdministrativeArea { + return predicate.AdministrativeArea(sql.FieldNEQ(FieldParentID, v)) +} + +// ParentIDIn applies the In predicate on the "parent_id" field. +func ParentIDIn(vs ...uuid.UUID) predicate.AdministrativeArea { + return predicate.AdministrativeArea(sql.FieldIn(FieldParentID, vs...)) +} + +// ParentIDNotIn applies the NotIn predicate on the "parent_id" field. +func ParentIDNotIn(vs ...uuid.UUID) predicate.AdministrativeArea { + return predicate.AdministrativeArea(sql.FieldNotIn(FieldParentID, vs...)) +} + +// ParentIDIsNil applies the IsNil predicate on the "parent_id" field. +func ParentIDIsNil() predicate.AdministrativeArea { + return predicate.AdministrativeArea(sql.FieldIsNull(FieldParentID)) +} + +// ParentIDNotNil applies the NotNil predicate on the "parent_id" field. +func ParentIDNotNil() predicate.AdministrativeArea { + return predicate.AdministrativeArea(sql.FieldNotNull(FieldParentID)) +} + +// ActiveEQ applies the EQ predicate on the "active" field. +func ActiveEQ(v bool) predicate.AdministrativeArea { + return predicate.AdministrativeArea(sql.FieldEQ(FieldActive, v)) +} + +// ActiveNEQ applies the NEQ predicate on the "active" field. +func ActiveNEQ(v bool) predicate.AdministrativeArea { + return predicate.AdministrativeArea(sql.FieldNEQ(FieldActive, v)) +} + +// CreatedAtEQ applies the EQ predicate on the "created_at" field. +func CreatedAtEQ(v time.Time) predicate.AdministrativeArea { + return predicate.AdministrativeArea(sql.FieldEQ(FieldCreatedAt, v)) +} + +// CreatedAtNEQ applies the NEQ predicate on the "created_at" field. +func CreatedAtNEQ(v time.Time) predicate.AdministrativeArea { + return predicate.AdministrativeArea(sql.FieldNEQ(FieldCreatedAt, v)) +} + +// CreatedAtIn applies the In predicate on the "created_at" field. +func CreatedAtIn(vs ...time.Time) predicate.AdministrativeArea { + return predicate.AdministrativeArea(sql.FieldIn(FieldCreatedAt, vs...)) +} + +// CreatedAtNotIn applies the NotIn predicate on the "created_at" field. +func CreatedAtNotIn(vs ...time.Time) predicate.AdministrativeArea { + return predicate.AdministrativeArea(sql.FieldNotIn(FieldCreatedAt, vs...)) +} + +// CreatedAtGT applies the GT predicate on the "created_at" field. +func CreatedAtGT(v time.Time) predicate.AdministrativeArea { + return predicate.AdministrativeArea(sql.FieldGT(FieldCreatedAt, v)) +} + +// CreatedAtGTE applies the GTE predicate on the "created_at" field. +func CreatedAtGTE(v time.Time) predicate.AdministrativeArea { + return predicate.AdministrativeArea(sql.FieldGTE(FieldCreatedAt, v)) +} + +// CreatedAtLT applies the LT predicate on the "created_at" field. +func CreatedAtLT(v time.Time) predicate.AdministrativeArea { + return predicate.AdministrativeArea(sql.FieldLT(FieldCreatedAt, v)) +} + +// CreatedAtLTE applies the LTE predicate on the "created_at" field. +func CreatedAtLTE(v time.Time) predicate.AdministrativeArea { + return predicate.AdministrativeArea(sql.FieldLTE(FieldCreatedAt, v)) +} + +// UpdatedAtEQ applies the EQ predicate on the "updated_at" field. +func UpdatedAtEQ(v time.Time) predicate.AdministrativeArea { + return predicate.AdministrativeArea(sql.FieldEQ(FieldUpdatedAt, v)) +} + +// UpdatedAtNEQ applies the NEQ predicate on the "updated_at" field. +func UpdatedAtNEQ(v time.Time) predicate.AdministrativeArea { + return predicate.AdministrativeArea(sql.FieldNEQ(FieldUpdatedAt, v)) +} + +// UpdatedAtIn applies the In predicate on the "updated_at" field. +func UpdatedAtIn(vs ...time.Time) predicate.AdministrativeArea { + return predicate.AdministrativeArea(sql.FieldIn(FieldUpdatedAt, vs...)) +} + +// UpdatedAtNotIn applies the NotIn predicate on the "updated_at" field. +func UpdatedAtNotIn(vs ...time.Time) predicate.AdministrativeArea { + return predicate.AdministrativeArea(sql.FieldNotIn(FieldUpdatedAt, vs...)) +} + +// UpdatedAtGT applies the GT predicate on the "updated_at" field. +func UpdatedAtGT(v time.Time) predicate.AdministrativeArea { + return predicate.AdministrativeArea(sql.FieldGT(FieldUpdatedAt, v)) +} + +// UpdatedAtGTE applies the GTE predicate on the "updated_at" field. +func UpdatedAtGTE(v time.Time) predicate.AdministrativeArea { + return predicate.AdministrativeArea(sql.FieldGTE(FieldUpdatedAt, v)) +} + +// UpdatedAtLT applies the LT predicate on the "updated_at" field. +func UpdatedAtLT(v time.Time) predicate.AdministrativeArea { + return predicate.AdministrativeArea(sql.FieldLT(FieldUpdatedAt, v)) +} + +// UpdatedAtLTE applies the LTE predicate on the "updated_at" field. +func UpdatedAtLTE(v time.Time) predicate.AdministrativeArea { + return predicate.AdministrativeArea(sql.FieldLTE(FieldUpdatedAt, v)) +} + +// HasChildren applies the HasEdge predicate on the "children" edge. +func HasChildren() predicate.AdministrativeArea { + return predicate.AdministrativeArea(func(s *sql.Selector) { + step := sqlgraph.NewStep( + sqlgraph.From(Table, FieldID), + sqlgraph.Edge(sqlgraph.O2M, false, ChildrenTable, ChildrenColumn), + ) + sqlgraph.HasNeighbors(s, step) + }) +} + +// HasChildrenWith applies the HasEdge predicate on the "children" edge with a given conditions (other predicates). +func HasChildrenWith(preds ...predicate.AdministrativeArea) predicate.AdministrativeArea { + return predicate.AdministrativeArea(func(s *sql.Selector) { + step := newChildrenStep() + sqlgraph.HasNeighborsWith(s, step, func(s *sql.Selector) { + for _, p := range preds { + p(s) + } + }) + }) +} + +// HasParent applies the HasEdge predicate on the "parent" edge. +func HasParent() predicate.AdministrativeArea { + return predicate.AdministrativeArea(func(s *sql.Selector) { + step := sqlgraph.NewStep( + sqlgraph.From(Table, FieldID), + sqlgraph.Edge(sqlgraph.M2O, true, ParentTable, ParentColumn), + ) + sqlgraph.HasNeighbors(s, step) + }) +} + +// HasParentWith applies the HasEdge predicate on the "parent" edge with a given conditions (other predicates). +func HasParentWith(preds ...predicate.AdministrativeArea) predicate.AdministrativeArea { + return predicate.AdministrativeArea(func(s *sql.Selector) { + step := newParentStep() + sqlgraph.HasNeighborsWith(s, step, func(s *sql.Selector) { + for _, p := range preds { + p(s) + } + }) + }) +} + +// HasLocalities applies the HasEdge predicate on the "localities" edge. +func HasLocalities() predicate.AdministrativeArea { + return predicate.AdministrativeArea(func(s *sql.Selector) { + step := sqlgraph.NewStep( + sqlgraph.From(Table, FieldID), + sqlgraph.Edge(sqlgraph.O2M, false, LocalitiesTable, LocalitiesColumn), + ) + sqlgraph.HasNeighbors(s, step) + }) +} + +// HasLocalitiesWith applies the HasEdge predicate on the "localities" edge with a given conditions (other predicates). +func HasLocalitiesWith(preds ...predicate.Locality) predicate.AdministrativeArea { + return predicate.AdministrativeArea(func(s *sql.Selector) { + step := newLocalitiesStep() + 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.AdministrativeArea) predicate.AdministrativeArea { + return predicate.AdministrativeArea(sql.AndPredicates(predicates...)) +} + +// Or groups predicates with the OR operator between them. +func Or(predicates ...predicate.AdministrativeArea) predicate.AdministrativeArea { + return predicate.AdministrativeArea(sql.OrPredicates(predicates...)) +} + +// Not applies the not operator on the given predicate. +func Not(p predicate.AdministrativeArea) predicate.AdministrativeArea { + return predicate.AdministrativeArea(sql.NotPredicates(p)) +} diff --git a/backend/ent/administrativearea_create.go b/backend/ent/administrativearea_create.go new file mode 100644 index 0000000..1ed359e --- /dev/null +++ b/backend/ent/administrativearea_create.go @@ -0,0 +1,465 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "context" + "errors" + "fmt" + "time" + + "entgo.io/ent/dialect/sql/sqlgraph" + "entgo.io/ent/schema/field" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/administrativearea" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/locality" + "github.com/google/uuid" +) + +// AdministrativeAreaCreate is the builder for creating a AdministrativeArea entity. +type AdministrativeAreaCreate struct { + config + mutation *AdministrativeAreaMutation + hooks []Hook +} + +// SetSource sets the "source" field. +func (_c *AdministrativeAreaCreate) SetSource(v string) *AdministrativeAreaCreate { + _c.mutation.SetSource(v) + return _c +} + +// SetSourceVersion sets the "source_version" field. +func (_c *AdministrativeAreaCreate) SetSourceVersion(v string) *AdministrativeAreaCreate { + _c.mutation.SetSourceVersion(v) + return _c +} + +// SetExternalCode sets the "external_code" field. +func (_c *AdministrativeAreaCreate) SetExternalCode(v string) *AdministrativeAreaCreate { + _c.mutation.SetExternalCode(v) + return _c +} + +// SetKind sets the "kind" field. +func (_c *AdministrativeAreaCreate) SetKind(v string) *AdministrativeAreaCreate { + _c.mutation.SetKind(v) + return _c +} + +// SetName sets the "name" field. +func (_c *AdministrativeAreaCreate) SetName(v string) *AdministrativeAreaCreate { + _c.mutation.SetName(v) + return _c +} + +// SetParentID sets the "parent_id" field. +func (_c *AdministrativeAreaCreate) SetParentID(v uuid.UUID) *AdministrativeAreaCreate { + _c.mutation.SetParentID(v) + return _c +} + +// SetNillableParentID sets the "parent_id" field if the given value is not nil. +func (_c *AdministrativeAreaCreate) SetNillableParentID(v *uuid.UUID) *AdministrativeAreaCreate { + if v != nil { + _c.SetParentID(*v) + } + return _c +} + +// SetActive sets the "active" field. +func (_c *AdministrativeAreaCreate) SetActive(v bool) *AdministrativeAreaCreate { + _c.mutation.SetActive(v) + return _c +} + +// SetNillableActive sets the "active" field if the given value is not nil. +func (_c *AdministrativeAreaCreate) SetNillableActive(v *bool) *AdministrativeAreaCreate { + if v != nil { + _c.SetActive(*v) + } + return _c +} + +// SetCreatedAt sets the "created_at" field. +func (_c *AdministrativeAreaCreate) SetCreatedAt(v time.Time) *AdministrativeAreaCreate { + _c.mutation.SetCreatedAt(v) + return _c +} + +// SetNillableCreatedAt sets the "created_at" field if the given value is not nil. +func (_c *AdministrativeAreaCreate) SetNillableCreatedAt(v *time.Time) *AdministrativeAreaCreate { + if v != nil { + _c.SetCreatedAt(*v) + } + return _c +} + +// SetUpdatedAt sets the "updated_at" field. +func (_c *AdministrativeAreaCreate) SetUpdatedAt(v time.Time) *AdministrativeAreaCreate { + _c.mutation.SetUpdatedAt(v) + return _c +} + +// SetNillableUpdatedAt sets the "updated_at" field if the given value is not nil. +func (_c *AdministrativeAreaCreate) SetNillableUpdatedAt(v *time.Time) *AdministrativeAreaCreate { + if v != nil { + _c.SetUpdatedAt(*v) + } + return _c +} + +// SetID sets the "id" field. +func (_c *AdministrativeAreaCreate) SetID(v uuid.UUID) *AdministrativeAreaCreate { + _c.mutation.SetID(v) + return _c +} + +// SetNillableID sets the "id" field if the given value is not nil. +func (_c *AdministrativeAreaCreate) SetNillableID(v *uuid.UUID) *AdministrativeAreaCreate { + if v != nil { + _c.SetID(*v) + } + return _c +} + +// AddChildIDs adds the "children" edge to the AdministrativeArea entity by IDs. +func (_c *AdministrativeAreaCreate) AddChildIDs(ids ...uuid.UUID) *AdministrativeAreaCreate { + _c.mutation.AddChildIDs(ids...) + return _c +} + +// AddChildren adds the "children" edges to the AdministrativeArea entity. +func (_c *AdministrativeAreaCreate) AddChildren(v ...*AdministrativeArea) *AdministrativeAreaCreate { + ids := make([]uuid.UUID, len(v)) + for i := range v { + ids[i] = v[i].ID + } + return _c.AddChildIDs(ids...) +} + +// SetParent sets the "parent" edge to the AdministrativeArea entity. +func (_c *AdministrativeAreaCreate) SetParent(v *AdministrativeArea) *AdministrativeAreaCreate { + return _c.SetParentID(v.ID) +} + +// AddLocalityIDs adds the "localities" edge to the Locality entity by IDs. +func (_c *AdministrativeAreaCreate) AddLocalityIDs(ids ...uuid.UUID) *AdministrativeAreaCreate { + _c.mutation.AddLocalityIDs(ids...) + return _c +} + +// AddLocalities adds the "localities" edges to the Locality entity. +func (_c *AdministrativeAreaCreate) AddLocalities(v ...*Locality) *AdministrativeAreaCreate { + ids := make([]uuid.UUID, len(v)) + for i := range v { + ids[i] = v[i].ID + } + return _c.AddLocalityIDs(ids...) +} + +// Mutation returns the AdministrativeAreaMutation object of the builder. +func (_c *AdministrativeAreaCreate) Mutation() *AdministrativeAreaMutation { + return _c.mutation +} + +// Save creates the AdministrativeArea in the database. +func (_c *AdministrativeAreaCreate) Save(ctx context.Context) (*AdministrativeArea, error) { + _c.defaults() + return withHooks(ctx, _c.sqlSave, _c.mutation, _c.hooks) +} + +// SaveX calls Save and panics if Save returns an error. +func (_c *AdministrativeAreaCreate) SaveX(ctx context.Context) *AdministrativeArea { + v, err := _c.Save(ctx) + if err != nil { + panic(err) + } + return v +} + +// Exec executes the query. +func (_c *AdministrativeAreaCreate) Exec(ctx context.Context) error { + _, err := _c.Save(ctx) + return err +} + +// ExecX is like Exec, but panics if an error occurs. +func (_c *AdministrativeAreaCreate) 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 *AdministrativeAreaCreate) defaults() { + if _, ok := _c.mutation.Active(); !ok { + v := administrativearea.DefaultActive + _c.mutation.SetActive(v) + } + if _, ok := _c.mutation.CreatedAt(); !ok { + v := administrativearea.DefaultCreatedAt() + _c.mutation.SetCreatedAt(v) + } + if _, ok := _c.mutation.UpdatedAt(); !ok { + v := administrativearea.DefaultUpdatedAt() + _c.mutation.SetUpdatedAt(v) + } + if _, ok := _c.mutation.ID(); !ok { + v := administrativearea.DefaultID() + _c.mutation.SetID(v) + } +} + +// check runs all checks and user-defined validators on the builder. +func (_c *AdministrativeAreaCreate) check() error { + if _, ok := _c.mutation.Source(); !ok { + return &ValidationError{Name: "source", err: errors.New(`ent: missing required field "AdministrativeArea.source"`)} + } + if v, ok := _c.mutation.Source(); ok { + if err := administrativearea.SourceValidator(v); err != nil { + return &ValidationError{Name: "source", err: fmt.Errorf(`ent: validator failed for field "AdministrativeArea.source": %w`, err)} + } + } + if _, ok := _c.mutation.SourceVersion(); !ok { + return &ValidationError{Name: "source_version", err: errors.New(`ent: missing required field "AdministrativeArea.source_version"`)} + } + if v, ok := _c.mutation.SourceVersion(); ok { + if err := administrativearea.SourceVersionValidator(v); err != nil { + return &ValidationError{Name: "source_version", err: fmt.Errorf(`ent: validator failed for field "AdministrativeArea.source_version": %w`, err)} + } + } + if _, ok := _c.mutation.ExternalCode(); !ok { + return &ValidationError{Name: "external_code", err: errors.New(`ent: missing required field "AdministrativeArea.external_code"`)} + } + if v, ok := _c.mutation.ExternalCode(); ok { + if err := administrativearea.ExternalCodeValidator(v); err != nil { + return &ValidationError{Name: "external_code", err: fmt.Errorf(`ent: validator failed for field "AdministrativeArea.external_code": %w`, err)} + } + } + if _, ok := _c.mutation.Kind(); !ok { + return &ValidationError{Name: "kind", err: errors.New(`ent: missing required field "AdministrativeArea.kind"`)} + } + if v, ok := _c.mutation.Kind(); ok { + if err := administrativearea.KindValidator(v); err != nil { + return &ValidationError{Name: "kind", err: fmt.Errorf(`ent: validator failed for field "AdministrativeArea.kind": %w`, err)} + } + } + if _, ok := _c.mutation.Name(); !ok { + return &ValidationError{Name: "name", err: errors.New(`ent: missing required field "AdministrativeArea.name"`)} + } + if v, ok := _c.mutation.Name(); ok { + if err := administrativearea.NameValidator(v); err != nil { + return &ValidationError{Name: "name", err: fmt.Errorf(`ent: validator failed for field "AdministrativeArea.name": %w`, err)} + } + } + if _, ok := _c.mutation.Active(); !ok { + return &ValidationError{Name: "active", err: errors.New(`ent: missing required field "AdministrativeArea.active"`)} + } + if _, ok := _c.mutation.CreatedAt(); !ok { + return &ValidationError{Name: "created_at", err: errors.New(`ent: missing required field "AdministrativeArea.created_at"`)} + } + if _, ok := _c.mutation.UpdatedAt(); !ok { + return &ValidationError{Name: "updated_at", err: errors.New(`ent: missing required field "AdministrativeArea.updated_at"`)} + } + return nil +} + +func (_c *AdministrativeAreaCreate) sqlSave(ctx context.Context) (*AdministrativeArea, 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 *AdministrativeAreaCreate) createSpec() (*AdministrativeArea, *sqlgraph.CreateSpec) { + var ( + _node = &AdministrativeArea{config: _c.config} + _spec = sqlgraph.NewCreateSpec(administrativearea.Table, sqlgraph.NewFieldSpec(administrativearea.FieldID, field.TypeUUID)) + ) + if id, ok := _c.mutation.ID(); ok { + _node.ID = id + _spec.ID.Value = &id + } + if value, ok := _c.mutation.Source(); ok { + _spec.SetField(administrativearea.FieldSource, field.TypeString, value) + _node.Source = value + } + if value, ok := _c.mutation.SourceVersion(); ok { + _spec.SetField(administrativearea.FieldSourceVersion, field.TypeString, value) + _node.SourceVersion = value + } + if value, ok := _c.mutation.ExternalCode(); ok { + _spec.SetField(administrativearea.FieldExternalCode, field.TypeString, value) + _node.ExternalCode = value + } + if value, ok := _c.mutation.Kind(); ok { + _spec.SetField(administrativearea.FieldKind, field.TypeString, value) + _node.Kind = value + } + if value, ok := _c.mutation.Name(); ok { + _spec.SetField(administrativearea.FieldName, field.TypeString, value) + _node.Name = value + } + if value, ok := _c.mutation.Active(); ok { + _spec.SetField(administrativearea.FieldActive, field.TypeBool, value) + _node.Active = value + } + if value, ok := _c.mutation.CreatedAt(); ok { + _spec.SetField(administrativearea.FieldCreatedAt, field.TypeTime, value) + _node.CreatedAt = value + } + if value, ok := _c.mutation.UpdatedAt(); ok { + _spec.SetField(administrativearea.FieldUpdatedAt, field.TypeTime, value) + _node.UpdatedAt = value + } + if nodes := _c.mutation.ChildrenIDs(); len(nodes) > 0 { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.O2M, + Inverse: false, + Table: administrativearea.ChildrenTable, + Columns: []string{administrativearea.ChildrenColumn}, + Bidi: true, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(administrativearea.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.ParentIDs(); len(nodes) > 0 { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.M2O, + Inverse: true, + Table: administrativearea.ParentTable, + Columns: []string{administrativearea.ParentColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(administrativearea.FieldID, field.TypeUUID), + }, + } + for _, k := range nodes { + edge.Target.Nodes = append(edge.Target.Nodes, k) + } + _node.ParentID = &nodes[0] + _spec.Edges = append(_spec.Edges, edge) + } + if nodes := _c.mutation.LocalitiesIDs(); len(nodes) > 0 { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.O2M, + Inverse: false, + Table: administrativearea.LocalitiesTable, + Columns: []string{administrativearea.LocalitiesColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(locality.FieldID, field.TypeUUID), + }, + } + for _, k := range nodes { + edge.Target.Nodes = append(edge.Target.Nodes, k) + } + _spec.Edges = append(_spec.Edges, edge) + } + return _node, _spec +} + +// AdministrativeAreaCreateBulk is the builder for creating many AdministrativeArea entities in bulk. +type AdministrativeAreaCreateBulk struct { + config + err error + builders []*AdministrativeAreaCreate +} + +// Save creates the AdministrativeArea entities in the database. +func (_c *AdministrativeAreaCreateBulk) Save(ctx context.Context) ([]*AdministrativeArea, error) { + if _c.err != nil { + return nil, _c.err + } + specs := make([]*sqlgraph.CreateSpec, len(_c.builders)) + nodes := make([]*AdministrativeArea, 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.(*AdministrativeAreaMutation) + 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 *AdministrativeAreaCreateBulk) SaveX(ctx context.Context) []*AdministrativeArea { + v, err := _c.Save(ctx) + if err != nil { + panic(err) + } + return v +} + +// Exec executes the query. +func (_c *AdministrativeAreaCreateBulk) Exec(ctx context.Context) error { + _, err := _c.Save(ctx) + return err +} + +// ExecX is like Exec, but panics if an error occurs. +func (_c *AdministrativeAreaCreateBulk) ExecX(ctx context.Context) { + if err := _c.Exec(ctx); err != nil { + panic(err) + } +} diff --git a/backend/ent/administrativearea_delete.go b/backend/ent/administrativearea_delete.go new file mode 100644 index 0000000..8498266 --- /dev/null +++ b/backend/ent/administrativearea_delete.go @@ -0,0 +1,88 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "context" + + "entgo.io/ent/dialect/sql" + "entgo.io/ent/dialect/sql/sqlgraph" + "entgo.io/ent/schema/field" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/administrativearea" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/predicate" +) + +// AdministrativeAreaDelete is the builder for deleting a AdministrativeArea entity. +type AdministrativeAreaDelete struct { + config + hooks []Hook + mutation *AdministrativeAreaMutation +} + +// Where appends a list predicates to the AdministrativeAreaDelete builder. +func (_d *AdministrativeAreaDelete) Where(ps ...predicate.AdministrativeArea) *AdministrativeAreaDelete { + _d.mutation.Where(ps...) + return _d +} + +// Exec executes the deletion query and returns how many vertices were deleted. +func (_d *AdministrativeAreaDelete) 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 *AdministrativeAreaDelete) ExecX(ctx context.Context) int { + n, err := _d.Exec(ctx) + if err != nil { + panic(err) + } + return n +} + +func (_d *AdministrativeAreaDelete) sqlExec(ctx context.Context) (int, error) { + _spec := sqlgraph.NewDeleteSpec(administrativearea.Table, sqlgraph.NewFieldSpec(administrativearea.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 +} + +// AdministrativeAreaDeleteOne is the builder for deleting a single AdministrativeArea entity. +type AdministrativeAreaDeleteOne struct { + _d *AdministrativeAreaDelete +} + +// Where appends a list predicates to the AdministrativeAreaDelete builder. +func (_d *AdministrativeAreaDeleteOne) Where(ps ...predicate.AdministrativeArea) *AdministrativeAreaDeleteOne { + _d._d.mutation.Where(ps...) + return _d +} + +// Exec executes the deletion query. +func (_d *AdministrativeAreaDeleteOne) Exec(ctx context.Context) error { + n, err := _d._d.Exec(ctx) + switch { + case err != nil: + return err + case n == 0: + return &NotFoundError{administrativearea.Label} + default: + return nil + } +} + +// ExecX is like Exec, but panics if an error occurs. +func (_d *AdministrativeAreaDeleteOne) ExecX(ctx context.Context) { + if err := _d.Exec(ctx); err != nil { + panic(err) + } +} diff --git a/backend/ent/administrativearea_query.go b/backend/ent/administrativearea_query.go new file mode 100644 index 0000000..e421048 --- /dev/null +++ b/backend/ent/administrativearea_query.go @@ -0,0 +1,760 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "context" + "database/sql/driver" + "fmt" + "math" + + "entgo.io/ent" + "entgo.io/ent/dialect/sql" + "entgo.io/ent/dialect/sql/sqlgraph" + "entgo.io/ent/schema/field" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/administrativearea" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/locality" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/predicate" + "github.com/google/uuid" +) + +// AdministrativeAreaQuery is the builder for querying AdministrativeArea entities. +type AdministrativeAreaQuery struct { + config + ctx *QueryContext + order []administrativearea.OrderOption + inters []Interceptor + predicates []predicate.AdministrativeArea + withChildren *AdministrativeAreaQuery + withParent *AdministrativeAreaQuery + withLocalities *LocalityQuery + // intermediate query (i.e. traversal path). + sql *sql.Selector + path func(context.Context) (*sql.Selector, error) +} + +// Where adds a new predicate for the AdministrativeAreaQuery builder. +func (_q *AdministrativeAreaQuery) Where(ps ...predicate.AdministrativeArea) *AdministrativeAreaQuery { + _q.predicates = append(_q.predicates, ps...) + return _q +} + +// Limit the number of records to be returned by this query. +func (_q *AdministrativeAreaQuery) Limit(limit int) *AdministrativeAreaQuery { + _q.ctx.Limit = &limit + return _q +} + +// Offset to start from. +func (_q *AdministrativeAreaQuery) Offset(offset int) *AdministrativeAreaQuery { + _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 *AdministrativeAreaQuery) Unique(unique bool) *AdministrativeAreaQuery { + _q.ctx.Unique = &unique + return _q +} + +// Order specifies how the records should be ordered. +func (_q *AdministrativeAreaQuery) Order(o ...administrativearea.OrderOption) *AdministrativeAreaQuery { + _q.order = append(_q.order, o...) + return _q +} + +// QueryChildren chains the current query on the "children" edge. +func (_q *AdministrativeAreaQuery) QueryChildren() *AdministrativeAreaQuery { + query := (&AdministrativeAreaClient{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(administrativearea.Table, administrativearea.FieldID, selector), + sqlgraph.To(administrativearea.Table, administrativearea.FieldID), + sqlgraph.Edge(sqlgraph.O2M, false, administrativearea.ChildrenTable, administrativearea.ChildrenColumn), + ) + fromU = sqlgraph.SetNeighbors(_q.driver.Dialect(), step) + return fromU, nil + } + return query +} + +// QueryParent chains the current query on the "parent" edge. +func (_q *AdministrativeAreaQuery) QueryParent() *AdministrativeAreaQuery { + query := (&AdministrativeAreaClient{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(administrativearea.Table, administrativearea.FieldID, selector), + sqlgraph.To(administrativearea.Table, administrativearea.FieldID), + sqlgraph.Edge(sqlgraph.M2O, true, administrativearea.ParentTable, administrativearea.ParentColumn), + ) + fromU = sqlgraph.SetNeighbors(_q.driver.Dialect(), step) + return fromU, nil + } + return query +} + +// QueryLocalities chains the current query on the "localities" edge. +func (_q *AdministrativeAreaQuery) QueryLocalities() *LocalityQuery { + query := (&LocalityClient{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(administrativearea.Table, administrativearea.FieldID, selector), + sqlgraph.To(locality.Table, locality.FieldID), + sqlgraph.Edge(sqlgraph.O2M, false, administrativearea.LocalitiesTable, administrativearea.LocalitiesColumn), + ) + fromU = sqlgraph.SetNeighbors(_q.driver.Dialect(), step) + return fromU, nil + } + return query +} + +// First returns the first AdministrativeArea entity from the query. +// Returns a *NotFoundError when no AdministrativeArea was found. +func (_q *AdministrativeAreaQuery) First(ctx context.Context) (*AdministrativeArea, 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{administrativearea.Label} + } + return nodes[0], nil +} + +// FirstX is like First, but panics if an error occurs. +func (_q *AdministrativeAreaQuery) FirstX(ctx context.Context) *AdministrativeArea { + node, err := _q.First(ctx) + if err != nil && !IsNotFound(err) { + panic(err) + } + return node +} + +// FirstID returns the first AdministrativeArea ID from the query. +// Returns a *NotFoundError when no AdministrativeArea ID was found. +func (_q *AdministrativeAreaQuery) 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{administrativearea.Label} + return + } + return ids[0], nil +} + +// FirstIDX is like FirstID, but panics if an error occurs. +func (_q *AdministrativeAreaQuery) FirstIDX(ctx context.Context) uuid.UUID { + id, err := _q.FirstID(ctx) + if err != nil && !IsNotFound(err) { + panic(err) + } + return id +} + +// Only returns a single AdministrativeArea entity found by the query, ensuring it only returns one. +// Returns a *NotSingularError when more than one AdministrativeArea entity is found. +// Returns a *NotFoundError when no AdministrativeArea entities are found. +func (_q *AdministrativeAreaQuery) Only(ctx context.Context) (*AdministrativeArea, 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{administrativearea.Label} + default: + return nil, &NotSingularError{administrativearea.Label} + } +} + +// OnlyX is like Only, but panics if an error occurs. +func (_q *AdministrativeAreaQuery) OnlyX(ctx context.Context) *AdministrativeArea { + node, err := _q.Only(ctx) + if err != nil { + panic(err) + } + return node +} + +// OnlyID is like Only, but returns the only AdministrativeArea ID in the query. +// Returns a *NotSingularError when more than one AdministrativeArea ID is found. +// Returns a *NotFoundError when no entities are found. +func (_q *AdministrativeAreaQuery) 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{administrativearea.Label} + default: + err = &NotSingularError{administrativearea.Label} + } + return +} + +// OnlyIDX is like OnlyID, but panics if an error occurs. +func (_q *AdministrativeAreaQuery) 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 AdministrativeAreas. +func (_q *AdministrativeAreaQuery) All(ctx context.Context) ([]*AdministrativeArea, error) { + ctx = setContextOp(ctx, _q.ctx, ent.OpQueryAll) + if err := _q.prepareQuery(ctx); err != nil { + return nil, err + } + qr := querierAll[[]*AdministrativeArea, *AdministrativeAreaQuery]() + return withInterceptors[[]*AdministrativeArea](ctx, _q, qr, _q.inters) +} + +// AllX is like All, but panics if an error occurs. +func (_q *AdministrativeAreaQuery) AllX(ctx context.Context) []*AdministrativeArea { + nodes, err := _q.All(ctx) + if err != nil { + panic(err) + } + return nodes +} + +// IDs executes the query and returns a list of AdministrativeArea IDs. +func (_q *AdministrativeAreaQuery) 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(administrativearea.FieldID).Scan(ctx, &ids); err != nil { + return nil, err + } + return ids, nil +} + +// IDsX is like IDs, but panics if an error occurs. +func (_q *AdministrativeAreaQuery) 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 *AdministrativeAreaQuery) 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[*AdministrativeAreaQuery](), _q.inters) +} + +// CountX is like Count, but panics if an error occurs. +func (_q *AdministrativeAreaQuery) 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 *AdministrativeAreaQuery) 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 *AdministrativeAreaQuery) ExistX(ctx context.Context) bool { + exist, err := _q.Exist(ctx) + if err != nil { + panic(err) + } + return exist +} + +// Clone returns a duplicate of the AdministrativeAreaQuery 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 *AdministrativeAreaQuery) Clone() *AdministrativeAreaQuery { + if _q == nil { + return nil + } + return &AdministrativeAreaQuery{ + config: _q.config, + ctx: _q.ctx.Clone(), + order: append([]administrativearea.OrderOption{}, _q.order...), + inters: append([]Interceptor{}, _q.inters...), + predicates: append([]predicate.AdministrativeArea{}, _q.predicates...), + withChildren: _q.withChildren.Clone(), + withParent: _q.withParent.Clone(), + withLocalities: _q.withLocalities.Clone(), + // clone intermediate query. + sql: _q.sql.Clone(), + path: _q.path, + } +} + +// WithChildren tells the query-builder to eager-load the nodes that are connected to +// the "children" edge. The optional arguments are used to configure the query builder of the edge. +func (_q *AdministrativeAreaQuery) WithChildren(opts ...func(*AdministrativeAreaQuery)) *AdministrativeAreaQuery { + query := (&AdministrativeAreaClient{config: _q.config}).Query() + for _, opt := range opts { + opt(query) + } + _q.withChildren = query + return _q +} + +// WithParent tells the query-builder to eager-load the nodes that are connected to +// the "parent" edge. The optional arguments are used to configure the query builder of the edge. +func (_q *AdministrativeAreaQuery) WithParent(opts ...func(*AdministrativeAreaQuery)) *AdministrativeAreaQuery { + query := (&AdministrativeAreaClient{config: _q.config}).Query() + for _, opt := range opts { + opt(query) + } + _q.withParent = query + return _q +} + +// WithLocalities tells the query-builder to eager-load the nodes that are connected to +// the "localities" edge. The optional arguments are used to configure the query builder of the edge. +func (_q *AdministrativeAreaQuery) WithLocalities(opts ...func(*LocalityQuery)) *AdministrativeAreaQuery { + query := (&LocalityClient{config: _q.config}).Query() + for _, opt := range opts { + opt(query) + } + _q.withLocalities = 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 { +// Source string `json:"source,omitempty"` +// Count int `json:"count,omitempty"` +// } +// +// client.AdministrativeArea.Query(). +// GroupBy(administrativearea.FieldSource). +// Aggregate(ent.Count()). +// Scan(ctx, &v) +func (_q *AdministrativeAreaQuery) GroupBy(field string, fields ...string) *AdministrativeAreaGroupBy { + _q.ctx.Fields = append([]string{field}, fields...) + grbuild := &AdministrativeAreaGroupBy{build: _q} + grbuild.flds = &_q.ctx.Fields + grbuild.label = administrativearea.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 { +// Source string `json:"source,omitempty"` +// } +// +// client.AdministrativeArea.Query(). +// Select(administrativearea.FieldSource). +// Scan(ctx, &v) +func (_q *AdministrativeAreaQuery) Select(fields ...string) *AdministrativeAreaSelect { + _q.ctx.Fields = append(_q.ctx.Fields, fields...) + sbuild := &AdministrativeAreaSelect{AdministrativeAreaQuery: _q} + sbuild.label = administrativearea.Label + sbuild.flds, sbuild.scan = &_q.ctx.Fields, sbuild.Scan + return sbuild +} + +// Aggregate returns a AdministrativeAreaSelect configured with the given aggregations. +func (_q *AdministrativeAreaQuery) Aggregate(fns ...AggregateFunc) *AdministrativeAreaSelect { + return _q.Select().Aggregate(fns...) +} + +func (_q *AdministrativeAreaQuery) 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 !administrativearea.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 *AdministrativeAreaQuery) sqlAll(ctx context.Context, hooks ...queryHook) ([]*AdministrativeArea, error) { + var ( + nodes = []*AdministrativeArea{} + _spec = _q.querySpec() + loadedTypes = [3]bool{ + _q.withChildren != nil, + _q.withParent != nil, + _q.withLocalities != nil, + } + ) + _spec.ScanValues = func(columns []string) ([]any, error) { + return (*AdministrativeArea).scanValues(nil, columns) + } + _spec.Assign = func(columns []string, values []any) error { + node := &AdministrativeArea{config: _q.config} + nodes = append(nodes, node) + node.Edges.loadedTypes = loadedTypes + return node.assignValues(columns, values) + } + 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.withChildren; query != nil { + if err := _q.loadChildren(ctx, query, nodes, + func(n *AdministrativeArea) { n.Edges.Children = []*AdministrativeArea{} }, + func(n *AdministrativeArea, e *AdministrativeArea) { n.Edges.Children = append(n.Edges.Children, e) }); err != nil { + return nil, err + } + } + if query := _q.withParent; query != nil { + if err := _q.loadParent(ctx, query, nodes, nil, + func(n *AdministrativeArea, e *AdministrativeArea) { n.Edges.Parent = e }); err != nil { + return nil, err + } + } + if query := _q.withLocalities; query != nil { + if err := _q.loadLocalities(ctx, query, nodes, + func(n *AdministrativeArea) { n.Edges.Localities = []*Locality{} }, + func(n *AdministrativeArea, e *Locality) { n.Edges.Localities = append(n.Edges.Localities, e) }); err != nil { + return nil, err + } + } + return nodes, nil +} + +func (_q *AdministrativeAreaQuery) loadChildren(ctx context.Context, query *AdministrativeAreaQuery, nodes []*AdministrativeArea, init func(*AdministrativeArea), assign func(*AdministrativeArea, *AdministrativeArea)) error { + fks := make([]driver.Value, 0, len(nodes)) + nodeids := make(map[uuid.UUID]*AdministrativeArea) + 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(administrativearea.FieldParentID) + } + query.Where(predicate.AdministrativeArea(func(s *sql.Selector) { + s.Where(sql.InValues(s.C(administrativearea.ChildrenColumn), fks...)) + })) + neighbors, err := query.All(ctx) + if err != nil { + return err + } + for _, n := range neighbors { + fk := n.ParentID + if fk == nil { + return fmt.Errorf(`foreign-key "parent_id" is nil for node %v`, n.ID) + } + node, ok := nodeids[*fk] + if !ok { + return fmt.Errorf(`unexpected referenced foreign-key "parent_id" returned %v for node %v`, *fk, n.ID) + } + assign(node, n) + } + return nil +} +func (_q *AdministrativeAreaQuery) loadParent(ctx context.Context, query *AdministrativeAreaQuery, nodes []*AdministrativeArea, init func(*AdministrativeArea), assign func(*AdministrativeArea, *AdministrativeArea)) error { + ids := make([]uuid.UUID, 0, len(nodes)) + nodeids := make(map[uuid.UUID][]*AdministrativeArea) + for i := range nodes { + if nodes[i].ParentID == nil { + continue + } + fk := *nodes[i].ParentID + if _, ok := nodeids[fk]; !ok { + ids = append(ids, fk) + } + nodeids[fk] = append(nodeids[fk], nodes[i]) + } + if len(ids) == 0 { + return nil + } + query.Where(administrativearea.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 "parent_id" returned %v`, n.ID) + } + for i := range nodes { + assign(nodes[i], n) + } + } + return nil +} +func (_q *AdministrativeAreaQuery) loadLocalities(ctx context.Context, query *LocalityQuery, nodes []*AdministrativeArea, init func(*AdministrativeArea), assign func(*AdministrativeArea, *Locality)) error { + fks := make([]driver.Value, 0, len(nodes)) + nodeids := make(map[uuid.UUID]*AdministrativeArea) + 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(locality.FieldParentParishID) + } + query.Where(predicate.Locality(func(s *sql.Selector) { + s.Where(sql.InValues(s.C(administrativearea.LocalitiesColumn), fks...)) + })) + neighbors, err := query.All(ctx) + if err != nil { + return err + } + for _, n := range neighbors { + fk := n.ParentParishID + node, ok := nodeids[fk] + if !ok { + return fmt.Errorf(`unexpected referenced foreign-key "parent_parish_id" returned %v for node %v`, fk, n.ID) + } + assign(node, n) + } + return nil +} + +func (_q *AdministrativeAreaQuery) sqlCount(ctx context.Context) (int, error) { + _spec := _q.querySpec() + _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 *AdministrativeAreaQuery) querySpec() *sqlgraph.QuerySpec { + _spec := sqlgraph.NewQuerySpec(administrativearea.Table, administrativearea.Columns, sqlgraph.NewFieldSpec(administrativearea.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, administrativearea.FieldID) + for i := range fields { + if fields[i] != administrativearea.FieldID { + _spec.Node.Columns = append(_spec.Node.Columns, fields[i]) + } + } + if _q.withParent != nil { + _spec.Node.AddColumnOnce(administrativearea.FieldParentID) + } + } + 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 *AdministrativeAreaQuery) sqlQuery(ctx context.Context) *sql.Selector { + builder := sql.Dialect(_q.driver.Dialect()) + t1 := builder.Table(administrativearea.Table) + columns := _q.ctx.Fields + if len(columns) == 0 { + columns = administrativearea.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 _, 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 +} + +// AdministrativeAreaGroupBy is the group-by builder for AdministrativeArea entities. +type AdministrativeAreaGroupBy struct { + selector + build *AdministrativeAreaQuery +} + +// Aggregate adds the given aggregation functions to the group-by query. +func (_g *AdministrativeAreaGroupBy) Aggregate(fns ...AggregateFunc) *AdministrativeAreaGroupBy { + _g.fns = append(_g.fns, fns...) + return _g +} + +// Scan applies the selector query and scans the result into the given value. +func (_g *AdministrativeAreaGroupBy) 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[*AdministrativeAreaQuery, *AdministrativeAreaGroupBy](ctx, _g.build, _g, _g.build.inters, v) +} + +func (_g *AdministrativeAreaGroupBy) sqlScan(ctx context.Context, root *AdministrativeAreaQuery, 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) +} + +// AdministrativeAreaSelect is the builder for selecting fields of AdministrativeArea entities. +type AdministrativeAreaSelect struct { + *AdministrativeAreaQuery + selector +} + +// Aggregate adds the given aggregation functions to the selector query. +func (_s *AdministrativeAreaSelect) Aggregate(fns ...AggregateFunc) *AdministrativeAreaSelect { + _s.fns = append(_s.fns, fns...) + return _s +} + +// Scan applies the selector query and scans the result into the given value. +func (_s *AdministrativeAreaSelect) 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[*AdministrativeAreaQuery, *AdministrativeAreaSelect](ctx, _s.AdministrativeAreaQuery, _s, _s.inters, v) +} + +func (_s *AdministrativeAreaSelect) sqlScan(ctx context.Context, root *AdministrativeAreaQuery, 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/backend/ent/administrativearea_update.go b/backend/ent/administrativearea_update.go new file mode 100644 index 0000000..982b80f --- /dev/null +++ b/backend/ent/administrativearea_update.go @@ -0,0 +1,632 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "context" + "errors" + "fmt" + "time" + + "entgo.io/ent/dialect/sql" + "entgo.io/ent/dialect/sql/sqlgraph" + "entgo.io/ent/schema/field" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/administrativearea" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/locality" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/predicate" + "github.com/google/uuid" +) + +// AdministrativeAreaUpdate is the builder for updating AdministrativeArea entities. +type AdministrativeAreaUpdate struct { + config + hooks []Hook + mutation *AdministrativeAreaMutation +} + +// Where appends a list predicates to the AdministrativeAreaUpdate builder. +func (_u *AdministrativeAreaUpdate) Where(ps ...predicate.AdministrativeArea) *AdministrativeAreaUpdate { + _u.mutation.Where(ps...) + return _u +} + +// SetName sets the "name" field. +func (_u *AdministrativeAreaUpdate) SetName(v string) *AdministrativeAreaUpdate { + _u.mutation.SetName(v) + return _u +} + +// SetNillableName sets the "name" field if the given value is not nil. +func (_u *AdministrativeAreaUpdate) SetNillableName(v *string) *AdministrativeAreaUpdate { + if v != nil { + _u.SetName(*v) + } + return _u +} + +// SetActive sets the "active" field. +func (_u *AdministrativeAreaUpdate) SetActive(v bool) *AdministrativeAreaUpdate { + _u.mutation.SetActive(v) + return _u +} + +// SetNillableActive sets the "active" field if the given value is not nil. +func (_u *AdministrativeAreaUpdate) SetNillableActive(v *bool) *AdministrativeAreaUpdate { + if v != nil { + _u.SetActive(*v) + } + return _u +} + +// SetUpdatedAt sets the "updated_at" field. +func (_u *AdministrativeAreaUpdate) SetUpdatedAt(v time.Time) *AdministrativeAreaUpdate { + _u.mutation.SetUpdatedAt(v) + return _u +} + +// AddChildIDs adds the "children" edge to the AdministrativeArea entity by IDs. +func (_u *AdministrativeAreaUpdate) AddChildIDs(ids ...uuid.UUID) *AdministrativeAreaUpdate { + _u.mutation.AddChildIDs(ids...) + return _u +} + +// AddChildren adds the "children" edges to the AdministrativeArea entity. +func (_u *AdministrativeAreaUpdate) AddChildren(v ...*AdministrativeArea) *AdministrativeAreaUpdate { + ids := make([]uuid.UUID, len(v)) + for i := range v { + ids[i] = v[i].ID + } + return _u.AddChildIDs(ids...) +} + +// AddLocalityIDs adds the "localities" edge to the Locality entity by IDs. +func (_u *AdministrativeAreaUpdate) AddLocalityIDs(ids ...uuid.UUID) *AdministrativeAreaUpdate { + _u.mutation.AddLocalityIDs(ids...) + return _u +} + +// AddLocalities adds the "localities" edges to the Locality entity. +func (_u *AdministrativeAreaUpdate) AddLocalities(v ...*Locality) *AdministrativeAreaUpdate { + ids := make([]uuid.UUID, len(v)) + for i := range v { + ids[i] = v[i].ID + } + return _u.AddLocalityIDs(ids...) +} + +// Mutation returns the AdministrativeAreaMutation object of the builder. +func (_u *AdministrativeAreaUpdate) Mutation() *AdministrativeAreaMutation { + return _u.mutation +} + +// ClearChildren clears all "children" edges to the AdministrativeArea entity. +func (_u *AdministrativeAreaUpdate) ClearChildren() *AdministrativeAreaUpdate { + _u.mutation.ClearChildren() + return _u +} + +// RemoveChildIDs removes the "children" edge to AdministrativeArea entities by IDs. +func (_u *AdministrativeAreaUpdate) RemoveChildIDs(ids ...uuid.UUID) *AdministrativeAreaUpdate { + _u.mutation.RemoveChildIDs(ids...) + return _u +} + +// RemoveChildren removes "children" edges to AdministrativeArea entities. +func (_u *AdministrativeAreaUpdate) RemoveChildren(v ...*AdministrativeArea) *AdministrativeAreaUpdate { + ids := make([]uuid.UUID, len(v)) + for i := range v { + ids[i] = v[i].ID + } + return _u.RemoveChildIDs(ids...) +} + +// ClearLocalities clears all "localities" edges to the Locality entity. +func (_u *AdministrativeAreaUpdate) ClearLocalities() *AdministrativeAreaUpdate { + _u.mutation.ClearLocalities() + return _u +} + +// RemoveLocalityIDs removes the "localities" edge to Locality entities by IDs. +func (_u *AdministrativeAreaUpdate) RemoveLocalityIDs(ids ...uuid.UUID) *AdministrativeAreaUpdate { + _u.mutation.RemoveLocalityIDs(ids...) + return _u +} + +// RemoveLocalities removes "localities" edges to Locality entities. +func (_u *AdministrativeAreaUpdate) RemoveLocalities(v ...*Locality) *AdministrativeAreaUpdate { + ids := make([]uuid.UUID, len(v)) + for i := range v { + ids[i] = v[i].ID + } + return _u.RemoveLocalityIDs(ids...) +} + +// Save executes the query and returns the number of nodes affected by the update operation. +func (_u *AdministrativeAreaUpdate) Save(ctx context.Context) (int, error) { + _u.defaults() + return withHooks(ctx, _u.sqlSave, _u.mutation, _u.hooks) +} + +// SaveX is like Save, but panics if an error occurs. +func (_u *AdministrativeAreaUpdate) SaveX(ctx context.Context) int { + affected, err := _u.Save(ctx) + if err != nil { + panic(err) + } + return affected +} + +// Exec executes the query. +func (_u *AdministrativeAreaUpdate) Exec(ctx context.Context) error { + _, err := _u.Save(ctx) + return err +} + +// ExecX is like Exec, but panics if an error occurs. +func (_u *AdministrativeAreaUpdate) ExecX(ctx context.Context) { + if err := _u.Exec(ctx); err != nil { + panic(err) + } +} + +// defaults sets the default values of the builder before save. +func (_u *AdministrativeAreaUpdate) defaults() { + if _, ok := _u.mutation.UpdatedAt(); !ok { + v := administrativearea.UpdateDefaultUpdatedAt() + _u.mutation.SetUpdatedAt(v) + } +} + +// check runs all checks and user-defined validators on the builder. +func (_u *AdministrativeAreaUpdate) check() error { + if v, ok := _u.mutation.Name(); ok { + if err := administrativearea.NameValidator(v); err != nil { + return &ValidationError{Name: "name", err: fmt.Errorf(`ent: validator failed for field "AdministrativeArea.name": %w`, err)} + } + } + return nil +} + +func (_u *AdministrativeAreaUpdate) sqlSave(ctx context.Context) (_node int, err error) { + if err := _u.check(); err != nil { + return _node, err + } + _spec := sqlgraph.NewUpdateSpec(administrativearea.Table, administrativearea.Columns, sqlgraph.NewFieldSpec(administrativearea.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(administrativearea.FieldName, field.TypeString, value) + } + if value, ok := _u.mutation.Active(); ok { + _spec.SetField(administrativearea.FieldActive, field.TypeBool, value) + } + if value, ok := _u.mutation.UpdatedAt(); ok { + _spec.SetField(administrativearea.FieldUpdatedAt, field.TypeTime, value) + } + if _u.mutation.ChildrenCleared() { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.O2M, + Inverse: false, + Table: administrativearea.ChildrenTable, + Columns: []string{administrativearea.ChildrenColumn}, + Bidi: true, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(administrativearea.FieldID, field.TypeUUID), + }, + } + _spec.Edges.Clear = append(_spec.Edges.Clear, edge) + } + if nodes := _u.mutation.RemovedChildrenIDs(); len(nodes) > 0 && !_u.mutation.ChildrenCleared() { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.O2M, + Inverse: false, + Table: administrativearea.ChildrenTable, + Columns: []string{administrativearea.ChildrenColumn}, + Bidi: true, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(administrativearea.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.ChildrenIDs(); len(nodes) > 0 { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.O2M, + Inverse: false, + Table: administrativearea.ChildrenTable, + Columns: []string{administrativearea.ChildrenColumn}, + Bidi: true, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(administrativearea.FieldID, field.TypeUUID), + }, + } + for _, k := range nodes { + edge.Target.Nodes = append(edge.Target.Nodes, k) + } + _spec.Edges.Add = append(_spec.Edges.Add, edge) + } + if _u.mutation.LocalitiesCleared() { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.O2M, + Inverse: false, + Table: administrativearea.LocalitiesTable, + Columns: []string{administrativearea.LocalitiesColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(locality.FieldID, field.TypeUUID), + }, + } + _spec.Edges.Clear = append(_spec.Edges.Clear, edge) + } + if nodes := _u.mutation.RemovedLocalitiesIDs(); len(nodes) > 0 && !_u.mutation.LocalitiesCleared() { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.O2M, + Inverse: false, + Table: administrativearea.LocalitiesTable, + Columns: []string{administrativearea.LocalitiesColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(locality.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.LocalitiesIDs(); len(nodes) > 0 { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.O2M, + Inverse: false, + Table: administrativearea.LocalitiesTable, + Columns: []string{administrativearea.LocalitiesColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(locality.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{administrativearea.Label} + } else if sqlgraph.IsConstraintError(err) { + err = &ConstraintError{msg: err.Error(), wrap: err} + } + return 0, err + } + _u.mutation.done = true + return _node, nil +} + +// AdministrativeAreaUpdateOne is the builder for updating a single AdministrativeArea entity. +type AdministrativeAreaUpdateOne struct { + config + fields []string + hooks []Hook + mutation *AdministrativeAreaMutation +} + +// SetName sets the "name" field. +func (_u *AdministrativeAreaUpdateOne) SetName(v string) *AdministrativeAreaUpdateOne { + _u.mutation.SetName(v) + return _u +} + +// SetNillableName sets the "name" field if the given value is not nil. +func (_u *AdministrativeAreaUpdateOne) SetNillableName(v *string) *AdministrativeAreaUpdateOne { + if v != nil { + _u.SetName(*v) + } + return _u +} + +// SetActive sets the "active" field. +func (_u *AdministrativeAreaUpdateOne) SetActive(v bool) *AdministrativeAreaUpdateOne { + _u.mutation.SetActive(v) + return _u +} + +// SetNillableActive sets the "active" field if the given value is not nil. +func (_u *AdministrativeAreaUpdateOne) SetNillableActive(v *bool) *AdministrativeAreaUpdateOne { + if v != nil { + _u.SetActive(*v) + } + return _u +} + +// SetUpdatedAt sets the "updated_at" field. +func (_u *AdministrativeAreaUpdateOne) SetUpdatedAt(v time.Time) *AdministrativeAreaUpdateOne { + _u.mutation.SetUpdatedAt(v) + return _u +} + +// AddChildIDs adds the "children" edge to the AdministrativeArea entity by IDs. +func (_u *AdministrativeAreaUpdateOne) AddChildIDs(ids ...uuid.UUID) *AdministrativeAreaUpdateOne { + _u.mutation.AddChildIDs(ids...) + return _u +} + +// AddChildren adds the "children" edges to the AdministrativeArea entity. +func (_u *AdministrativeAreaUpdateOne) AddChildren(v ...*AdministrativeArea) *AdministrativeAreaUpdateOne { + ids := make([]uuid.UUID, len(v)) + for i := range v { + ids[i] = v[i].ID + } + return _u.AddChildIDs(ids...) +} + +// AddLocalityIDs adds the "localities" edge to the Locality entity by IDs. +func (_u *AdministrativeAreaUpdateOne) AddLocalityIDs(ids ...uuid.UUID) *AdministrativeAreaUpdateOne { + _u.mutation.AddLocalityIDs(ids...) + return _u +} + +// AddLocalities adds the "localities" edges to the Locality entity. +func (_u *AdministrativeAreaUpdateOne) AddLocalities(v ...*Locality) *AdministrativeAreaUpdateOne { + ids := make([]uuid.UUID, len(v)) + for i := range v { + ids[i] = v[i].ID + } + return _u.AddLocalityIDs(ids...) +} + +// Mutation returns the AdministrativeAreaMutation object of the builder. +func (_u *AdministrativeAreaUpdateOne) Mutation() *AdministrativeAreaMutation { + return _u.mutation +} + +// ClearChildren clears all "children" edges to the AdministrativeArea entity. +func (_u *AdministrativeAreaUpdateOne) ClearChildren() *AdministrativeAreaUpdateOne { + _u.mutation.ClearChildren() + return _u +} + +// RemoveChildIDs removes the "children" edge to AdministrativeArea entities by IDs. +func (_u *AdministrativeAreaUpdateOne) RemoveChildIDs(ids ...uuid.UUID) *AdministrativeAreaUpdateOne { + _u.mutation.RemoveChildIDs(ids...) + return _u +} + +// RemoveChildren removes "children" edges to AdministrativeArea entities. +func (_u *AdministrativeAreaUpdateOne) RemoveChildren(v ...*AdministrativeArea) *AdministrativeAreaUpdateOne { + ids := make([]uuid.UUID, len(v)) + for i := range v { + ids[i] = v[i].ID + } + return _u.RemoveChildIDs(ids...) +} + +// ClearLocalities clears all "localities" edges to the Locality entity. +func (_u *AdministrativeAreaUpdateOne) ClearLocalities() *AdministrativeAreaUpdateOne { + _u.mutation.ClearLocalities() + return _u +} + +// RemoveLocalityIDs removes the "localities" edge to Locality entities by IDs. +func (_u *AdministrativeAreaUpdateOne) RemoveLocalityIDs(ids ...uuid.UUID) *AdministrativeAreaUpdateOne { + _u.mutation.RemoveLocalityIDs(ids...) + return _u +} + +// RemoveLocalities removes "localities" edges to Locality entities. +func (_u *AdministrativeAreaUpdateOne) RemoveLocalities(v ...*Locality) *AdministrativeAreaUpdateOne { + ids := make([]uuid.UUID, len(v)) + for i := range v { + ids[i] = v[i].ID + } + return _u.RemoveLocalityIDs(ids...) +} + +// Where appends a list predicates to the AdministrativeAreaUpdate builder. +func (_u *AdministrativeAreaUpdateOne) Where(ps ...predicate.AdministrativeArea) *AdministrativeAreaUpdateOne { + _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 *AdministrativeAreaUpdateOne) Select(field string, fields ...string) *AdministrativeAreaUpdateOne { + _u.fields = append([]string{field}, fields...) + return _u +} + +// Save executes the query and returns the updated AdministrativeArea entity. +func (_u *AdministrativeAreaUpdateOne) Save(ctx context.Context) (*AdministrativeArea, error) { + _u.defaults() + return withHooks(ctx, _u.sqlSave, _u.mutation, _u.hooks) +} + +// SaveX is like Save, but panics if an error occurs. +func (_u *AdministrativeAreaUpdateOne) SaveX(ctx context.Context) *AdministrativeArea { + node, err := _u.Save(ctx) + if err != nil { + panic(err) + } + return node +} + +// Exec executes the query on the entity. +func (_u *AdministrativeAreaUpdateOne) Exec(ctx context.Context) error { + _, err := _u.Save(ctx) + return err +} + +// ExecX is like Exec, but panics if an error occurs. +func (_u *AdministrativeAreaUpdateOne) ExecX(ctx context.Context) { + if err := _u.Exec(ctx); err != nil { + panic(err) + } +} + +// defaults sets the default values of the builder before save. +func (_u *AdministrativeAreaUpdateOne) defaults() { + if _, ok := _u.mutation.UpdatedAt(); !ok { + v := administrativearea.UpdateDefaultUpdatedAt() + _u.mutation.SetUpdatedAt(v) + } +} + +// check runs all checks and user-defined validators on the builder. +func (_u *AdministrativeAreaUpdateOne) check() error { + if v, ok := _u.mutation.Name(); ok { + if err := administrativearea.NameValidator(v); err != nil { + return &ValidationError{Name: "name", err: fmt.Errorf(`ent: validator failed for field "AdministrativeArea.name": %w`, err)} + } + } + return nil +} + +func (_u *AdministrativeAreaUpdateOne) sqlSave(ctx context.Context) (_node *AdministrativeArea, err error) { + if err := _u.check(); err != nil { + return _node, err + } + _spec := sqlgraph.NewUpdateSpec(administrativearea.Table, administrativearea.Columns, sqlgraph.NewFieldSpec(administrativearea.FieldID, field.TypeUUID)) + id, ok := _u.mutation.ID() + if !ok { + return nil, &ValidationError{Name: "id", err: errors.New(`ent: missing "AdministrativeArea.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, administrativearea.FieldID) + for _, f := range fields { + if !administrativearea.ValidColumn(f) { + return nil, &ValidationError{Name: f, err: fmt.Errorf("ent: invalid field %q for query", f)} + } + if f != administrativearea.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(administrativearea.FieldName, field.TypeString, value) + } + if value, ok := _u.mutation.Active(); ok { + _spec.SetField(administrativearea.FieldActive, field.TypeBool, value) + } + if value, ok := _u.mutation.UpdatedAt(); ok { + _spec.SetField(administrativearea.FieldUpdatedAt, field.TypeTime, value) + } + if _u.mutation.ChildrenCleared() { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.O2M, + Inverse: false, + Table: administrativearea.ChildrenTable, + Columns: []string{administrativearea.ChildrenColumn}, + Bidi: true, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(administrativearea.FieldID, field.TypeUUID), + }, + } + _spec.Edges.Clear = append(_spec.Edges.Clear, edge) + } + if nodes := _u.mutation.RemovedChildrenIDs(); len(nodes) > 0 && !_u.mutation.ChildrenCleared() { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.O2M, + Inverse: false, + Table: administrativearea.ChildrenTable, + Columns: []string{administrativearea.ChildrenColumn}, + Bidi: true, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(administrativearea.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.ChildrenIDs(); len(nodes) > 0 { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.O2M, + Inverse: false, + Table: administrativearea.ChildrenTable, + Columns: []string{administrativearea.ChildrenColumn}, + Bidi: true, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(administrativearea.FieldID, field.TypeUUID), + }, + } + for _, k := range nodes { + edge.Target.Nodes = append(edge.Target.Nodes, k) + } + _spec.Edges.Add = append(_spec.Edges.Add, edge) + } + if _u.mutation.LocalitiesCleared() { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.O2M, + Inverse: false, + Table: administrativearea.LocalitiesTable, + Columns: []string{administrativearea.LocalitiesColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(locality.FieldID, field.TypeUUID), + }, + } + _spec.Edges.Clear = append(_spec.Edges.Clear, edge) + } + if nodes := _u.mutation.RemovedLocalitiesIDs(); len(nodes) > 0 && !_u.mutation.LocalitiesCleared() { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.O2M, + Inverse: false, + Table: administrativearea.LocalitiesTable, + Columns: []string{administrativearea.LocalitiesColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(locality.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.LocalitiesIDs(); len(nodes) > 0 { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.O2M, + Inverse: false, + Table: administrativearea.LocalitiesTable, + Columns: []string{administrativearea.LocalitiesColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(locality.FieldID, field.TypeUUID), + }, + } + for _, k := range nodes { + edge.Target.Nodes = append(edge.Target.Nodes, k) + } + _spec.Edges.Add = append(_spec.Edges.Add, edge) + } + _node = &AdministrativeArea{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{administrativearea.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/backend/ent/client.go b/backend/ent/client.go new file mode 100644 index 0000000..fe838f6 --- /dev/null +++ b/backend/ent/client.go @@ -0,0 +1,3128 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "context" + "errors" + "fmt" + "log" + "reflect" + + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/migrate" + "github.com/google/uuid" + + "entgo.io/ent" + "entgo.io/ent/dialect" + "entgo.io/ent/dialect/sql" + "entgo.io/ent/dialect/sql/sqlgraph" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/administrativearea" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/contactrevealdailylimit" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/contactrevealevent" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/internaluser" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/listing" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/listingevent" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/listingmedia" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/locality" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/platformrole" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/providercontactchannel" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/providerprofile" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/providerservicelocality" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/providerspokenlanguage" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/servicecategory" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/servicecategorytranslation" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/spokenlanguage" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/spokenlanguagetranslation" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/supportedlocale" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/useraccount" +) + +// 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 + // AdministrativeArea is the client for interacting with the AdministrativeArea builders. + AdministrativeArea *AdministrativeAreaClient + // ContactRevealDailyLimit is the client for interacting with the ContactRevealDailyLimit builders. + ContactRevealDailyLimit *ContactRevealDailyLimitClient + // ContactRevealEvent is the client for interacting with the ContactRevealEvent builders. + ContactRevealEvent *ContactRevealEventClient + // InternalUser is the client for interacting with the InternalUser builders. + InternalUser *InternalUserClient + // Listing is the client for interacting with the Listing builders. + Listing *ListingClient + // ListingEvent is the client for interacting with the ListingEvent builders. + ListingEvent *ListingEventClient + // ListingMedia is the client for interacting with the ListingMedia builders. + ListingMedia *ListingMediaClient + // Locality is the client for interacting with the Locality builders. + Locality *LocalityClient + // PlatformRole is the client for interacting with the PlatformRole builders. + PlatformRole *PlatformRoleClient + // ProviderContactChannel is the client for interacting with the ProviderContactChannel builders. + ProviderContactChannel *ProviderContactChannelClient + // ProviderProfile is the client for interacting with the ProviderProfile builders. + ProviderProfile *ProviderProfileClient + // ProviderServiceLocality is the client for interacting with the ProviderServiceLocality builders. + ProviderServiceLocality *ProviderServiceLocalityClient + // ProviderSpokenLanguage is the client for interacting with the ProviderSpokenLanguage builders. + ProviderSpokenLanguage *ProviderSpokenLanguageClient + // ServiceCategory is the client for interacting with the ServiceCategory builders. + ServiceCategory *ServiceCategoryClient + // ServiceCategoryTranslation is the client for interacting with the ServiceCategoryTranslation builders. + ServiceCategoryTranslation *ServiceCategoryTranslationClient + // SpokenLanguage is the client for interacting with the SpokenLanguage builders. + SpokenLanguage *SpokenLanguageClient + // SpokenLanguageTranslation is the client for interacting with the SpokenLanguageTranslation builders. + SpokenLanguageTranslation *SpokenLanguageTranslationClient + // SupportedLocale is the client for interacting with the SupportedLocale builders. + SupportedLocale *SupportedLocaleClient + // UserAccount is the client for interacting with the UserAccount builders. + UserAccount *UserAccountClient +} + +// 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.AdministrativeArea = NewAdministrativeAreaClient(c.config) + c.ContactRevealDailyLimit = NewContactRevealDailyLimitClient(c.config) + c.ContactRevealEvent = NewContactRevealEventClient(c.config) + c.InternalUser = NewInternalUserClient(c.config) + c.Listing = NewListingClient(c.config) + c.ListingEvent = NewListingEventClient(c.config) + c.ListingMedia = NewListingMediaClient(c.config) + c.Locality = NewLocalityClient(c.config) + c.PlatformRole = NewPlatformRoleClient(c.config) + c.ProviderContactChannel = NewProviderContactChannelClient(c.config) + c.ProviderProfile = NewProviderProfileClient(c.config) + c.ProviderServiceLocality = NewProviderServiceLocalityClient(c.config) + c.ProviderSpokenLanguage = NewProviderSpokenLanguageClient(c.config) + c.ServiceCategory = NewServiceCategoryClient(c.config) + c.ServiceCategoryTranslation = NewServiceCategoryTranslationClient(c.config) + c.SpokenLanguage = NewSpokenLanguageClient(c.config) + c.SpokenLanguageTranslation = NewSpokenLanguageTranslationClient(c.config) + c.SupportedLocale = NewSupportedLocaleClient(c.config) + c.UserAccount = NewUserAccountClient(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, + AdministrativeArea: NewAdministrativeAreaClient(cfg), + ContactRevealDailyLimit: NewContactRevealDailyLimitClient(cfg), + ContactRevealEvent: NewContactRevealEventClient(cfg), + InternalUser: NewInternalUserClient(cfg), + Listing: NewListingClient(cfg), + ListingEvent: NewListingEventClient(cfg), + ListingMedia: NewListingMediaClient(cfg), + Locality: NewLocalityClient(cfg), + PlatformRole: NewPlatformRoleClient(cfg), + ProviderContactChannel: NewProviderContactChannelClient(cfg), + ProviderProfile: NewProviderProfileClient(cfg), + ProviderServiceLocality: NewProviderServiceLocalityClient(cfg), + ProviderSpokenLanguage: NewProviderSpokenLanguageClient(cfg), + ServiceCategory: NewServiceCategoryClient(cfg), + ServiceCategoryTranslation: NewServiceCategoryTranslationClient(cfg), + SpokenLanguage: NewSpokenLanguageClient(cfg), + SpokenLanguageTranslation: NewSpokenLanguageTranslationClient(cfg), + SupportedLocale: NewSupportedLocaleClient(cfg), + UserAccount: NewUserAccountClient(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, + AdministrativeArea: NewAdministrativeAreaClient(cfg), + ContactRevealDailyLimit: NewContactRevealDailyLimitClient(cfg), + ContactRevealEvent: NewContactRevealEventClient(cfg), + InternalUser: NewInternalUserClient(cfg), + Listing: NewListingClient(cfg), + ListingEvent: NewListingEventClient(cfg), + ListingMedia: NewListingMediaClient(cfg), + Locality: NewLocalityClient(cfg), + PlatformRole: NewPlatformRoleClient(cfg), + ProviderContactChannel: NewProviderContactChannelClient(cfg), + ProviderProfile: NewProviderProfileClient(cfg), + ProviderServiceLocality: NewProviderServiceLocalityClient(cfg), + ProviderSpokenLanguage: NewProviderSpokenLanguageClient(cfg), + ServiceCategory: NewServiceCategoryClient(cfg), + ServiceCategoryTranslation: NewServiceCategoryTranslationClient(cfg), + SpokenLanguage: NewSpokenLanguageClient(cfg), + SpokenLanguageTranslation: NewSpokenLanguageTranslationClient(cfg), + SupportedLocale: NewSupportedLocaleClient(cfg), + UserAccount: NewUserAccountClient(cfg), + }, nil +} + +// Debug returns a new debug-client. It's used to get verbose logging on specific operations. +// +// client.Debug(). +// AdministrativeArea. +// 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) { + for _, n := range []interface{ Use(...Hook) }{ + c.AdministrativeArea, c.ContactRevealDailyLimit, c.ContactRevealEvent, + c.InternalUser, c.Listing, c.ListingEvent, c.ListingMedia, c.Locality, + c.PlatformRole, c.ProviderContactChannel, c.ProviderProfile, + c.ProviderServiceLocality, c.ProviderSpokenLanguage, c.ServiceCategory, + c.ServiceCategoryTranslation, c.SpokenLanguage, c.SpokenLanguageTranslation, + c.SupportedLocale, c.UserAccount, + } { + n.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) { + for _, n := range []interface{ Intercept(...Interceptor) }{ + c.AdministrativeArea, c.ContactRevealDailyLimit, c.ContactRevealEvent, + c.InternalUser, c.Listing, c.ListingEvent, c.ListingMedia, c.Locality, + c.PlatformRole, c.ProviderContactChannel, c.ProviderProfile, + c.ProviderServiceLocality, c.ProviderSpokenLanguage, c.ServiceCategory, + c.ServiceCategoryTranslation, c.SpokenLanguage, c.SpokenLanguageTranslation, + c.SupportedLocale, c.UserAccount, + } { + n.Intercept(interceptors...) + } +} + +// Mutate implements the ent.Mutator interface. +func (c *Client) Mutate(ctx context.Context, m Mutation) (Value, error) { + switch m := m.(type) { + case *AdministrativeAreaMutation: + return c.AdministrativeArea.mutate(ctx, m) + case *ContactRevealDailyLimitMutation: + return c.ContactRevealDailyLimit.mutate(ctx, m) + case *ContactRevealEventMutation: + return c.ContactRevealEvent.mutate(ctx, m) + case *InternalUserMutation: + return c.InternalUser.mutate(ctx, m) + case *ListingMutation: + return c.Listing.mutate(ctx, m) + case *ListingEventMutation: + return c.ListingEvent.mutate(ctx, m) + case *ListingMediaMutation: + return c.ListingMedia.mutate(ctx, m) + case *LocalityMutation: + return c.Locality.mutate(ctx, m) + case *PlatformRoleMutation: + return c.PlatformRole.mutate(ctx, m) + case *ProviderContactChannelMutation: + return c.ProviderContactChannel.mutate(ctx, m) + case *ProviderProfileMutation: + return c.ProviderProfile.mutate(ctx, m) + case *ProviderServiceLocalityMutation: + return c.ProviderServiceLocality.mutate(ctx, m) + case *ProviderSpokenLanguageMutation: + return c.ProviderSpokenLanguage.mutate(ctx, m) + case *ServiceCategoryMutation: + return c.ServiceCategory.mutate(ctx, m) + case *ServiceCategoryTranslationMutation: + return c.ServiceCategoryTranslation.mutate(ctx, m) + case *SpokenLanguageMutation: + return c.SpokenLanguage.mutate(ctx, m) + case *SpokenLanguageTranslationMutation: + return c.SpokenLanguageTranslation.mutate(ctx, m) + case *SupportedLocaleMutation: + return c.SupportedLocale.mutate(ctx, m) + case *UserAccountMutation: + return c.UserAccount.mutate(ctx, m) + default: + return nil, fmt.Errorf("ent: unknown mutation type %T", m) + } +} + +// AdministrativeAreaClient is a client for the AdministrativeArea schema. +type AdministrativeAreaClient struct { + config +} + +// NewAdministrativeAreaClient returns a client for the AdministrativeArea from the given config. +func NewAdministrativeAreaClient(c config) *AdministrativeAreaClient { + return &AdministrativeAreaClient{config: c} +} + +// Use adds a list of mutation hooks to the hooks stack. +// A call to `Use(f, g, h)` equals to `administrativearea.Hooks(f(g(h())))`. +func (c *AdministrativeAreaClient) Use(hooks ...Hook) { + c.hooks.AdministrativeArea = append(c.hooks.AdministrativeArea, hooks...) +} + +// Intercept adds a list of query interceptors to the interceptors stack. +// A call to `Intercept(f, g, h)` equals to `administrativearea.Intercept(f(g(h())))`. +func (c *AdministrativeAreaClient) Intercept(interceptors ...Interceptor) { + c.inters.AdministrativeArea = append(c.inters.AdministrativeArea, interceptors...) +} + +// Create returns a builder for creating a AdministrativeArea entity. +func (c *AdministrativeAreaClient) Create() *AdministrativeAreaCreate { + mutation := newAdministrativeAreaMutation(c.config, OpCreate) + return &AdministrativeAreaCreate{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// CreateBulk returns a builder for creating a bulk of AdministrativeArea entities. +func (c *AdministrativeAreaClient) CreateBulk(builders ...*AdministrativeAreaCreate) *AdministrativeAreaCreateBulk { + return &AdministrativeAreaCreateBulk{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 *AdministrativeAreaClient) MapCreateBulk(slice any, setFunc func(*AdministrativeAreaCreate, int)) *AdministrativeAreaCreateBulk { + rv := reflect.ValueOf(slice) + if rv.Kind() != reflect.Slice { + return &AdministrativeAreaCreateBulk{err: fmt.Errorf("calling to AdministrativeAreaClient.MapCreateBulk with wrong type %T, need slice", slice)} + } + builders := make([]*AdministrativeAreaCreate, rv.Len()) + for i := 0; i < rv.Len(); i++ { + builders[i] = c.Create() + setFunc(builders[i], i) + } + return &AdministrativeAreaCreateBulk{config: c.config, builders: builders} +} + +// Update returns an update builder for AdministrativeArea. +func (c *AdministrativeAreaClient) Update() *AdministrativeAreaUpdate { + mutation := newAdministrativeAreaMutation(c.config, OpUpdate) + return &AdministrativeAreaUpdate{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// UpdateOne returns an update builder for the given entity. +func (c *AdministrativeAreaClient) UpdateOne(_m *AdministrativeArea) *AdministrativeAreaUpdateOne { + mutation := newAdministrativeAreaMutation(c.config, OpUpdateOne, withAdministrativeArea(_m)) + return &AdministrativeAreaUpdateOne{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// UpdateOneID returns an update builder for the given id. +func (c *AdministrativeAreaClient) UpdateOneID(id uuid.UUID) *AdministrativeAreaUpdateOne { + mutation := newAdministrativeAreaMutation(c.config, OpUpdateOne, withAdministrativeAreaID(id)) + return &AdministrativeAreaUpdateOne{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// Delete returns a delete builder for AdministrativeArea. +func (c *AdministrativeAreaClient) Delete() *AdministrativeAreaDelete { + mutation := newAdministrativeAreaMutation(c.config, OpDelete) + return &AdministrativeAreaDelete{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// DeleteOne returns a builder for deleting the given entity. +func (c *AdministrativeAreaClient) DeleteOne(_m *AdministrativeArea) *AdministrativeAreaDeleteOne { + return c.DeleteOneID(_m.ID) +} + +// DeleteOneID returns a builder for deleting the given entity by its id. +func (c *AdministrativeAreaClient) DeleteOneID(id uuid.UUID) *AdministrativeAreaDeleteOne { + builder := c.Delete().Where(administrativearea.ID(id)) + builder.mutation.id = &id + builder.mutation.op = OpDeleteOne + return &AdministrativeAreaDeleteOne{builder} +} + +// Query returns a query builder for AdministrativeArea. +func (c *AdministrativeAreaClient) Query() *AdministrativeAreaQuery { + return &AdministrativeAreaQuery{ + config: c.config, + ctx: &QueryContext{Type: TypeAdministrativeArea}, + inters: c.Interceptors(), + } +} + +// Get returns a AdministrativeArea entity by its id. +func (c *AdministrativeAreaClient) Get(ctx context.Context, id uuid.UUID) (*AdministrativeArea, error) { + return c.Query().Where(administrativearea.ID(id)).Only(ctx) +} + +// GetX is like Get, but panics if an error occurs. +func (c *AdministrativeAreaClient) GetX(ctx context.Context, id uuid.UUID) *AdministrativeArea { + obj, err := c.Get(ctx, id) + if err != nil { + panic(err) + } + return obj +} + +// QueryChildren queries the children edge of a AdministrativeArea. +func (c *AdministrativeAreaClient) QueryChildren(_m *AdministrativeArea) *AdministrativeAreaQuery { + query := (&AdministrativeAreaClient{config: c.config}).Query() + query.path = func(context.Context) (fromV *sql.Selector, _ error) { + id := _m.ID + step := sqlgraph.NewStep( + sqlgraph.From(administrativearea.Table, administrativearea.FieldID, id), + sqlgraph.To(administrativearea.Table, administrativearea.FieldID), + sqlgraph.Edge(sqlgraph.O2M, false, administrativearea.ChildrenTable, administrativearea.ChildrenColumn), + ) + fromV = sqlgraph.Neighbors(_m.driver.Dialect(), step) + return fromV, nil + } + return query +} + +// QueryParent queries the parent edge of a AdministrativeArea. +func (c *AdministrativeAreaClient) QueryParent(_m *AdministrativeArea) *AdministrativeAreaQuery { + query := (&AdministrativeAreaClient{config: c.config}).Query() + query.path = func(context.Context) (fromV *sql.Selector, _ error) { + id := _m.ID + step := sqlgraph.NewStep( + sqlgraph.From(administrativearea.Table, administrativearea.FieldID, id), + sqlgraph.To(administrativearea.Table, administrativearea.FieldID), + sqlgraph.Edge(sqlgraph.M2O, true, administrativearea.ParentTable, administrativearea.ParentColumn), + ) + fromV = sqlgraph.Neighbors(_m.driver.Dialect(), step) + return fromV, nil + } + return query +} + +// QueryLocalities queries the localities edge of a AdministrativeArea. +func (c *AdministrativeAreaClient) QueryLocalities(_m *AdministrativeArea) *LocalityQuery { + query := (&LocalityClient{config: c.config}).Query() + query.path = func(context.Context) (fromV *sql.Selector, _ error) { + id := _m.ID + step := sqlgraph.NewStep( + sqlgraph.From(administrativearea.Table, administrativearea.FieldID, id), + sqlgraph.To(locality.Table, locality.FieldID), + sqlgraph.Edge(sqlgraph.O2M, false, administrativearea.LocalitiesTable, administrativearea.LocalitiesColumn), + ) + fromV = sqlgraph.Neighbors(_m.driver.Dialect(), step) + return fromV, nil + } + return query +} + +// Hooks returns the client hooks. +func (c *AdministrativeAreaClient) Hooks() []Hook { + return c.hooks.AdministrativeArea +} + +// Interceptors returns the client interceptors. +func (c *AdministrativeAreaClient) Interceptors() []Interceptor { + return c.inters.AdministrativeArea +} + +func (c *AdministrativeAreaClient) mutate(ctx context.Context, m *AdministrativeAreaMutation) (Value, error) { + switch m.Op() { + case OpCreate: + return (&AdministrativeAreaCreate{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx) + case OpUpdate: + return (&AdministrativeAreaUpdate{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx) + case OpUpdateOne: + return (&AdministrativeAreaUpdateOne{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx) + case OpDelete, OpDeleteOne: + return (&AdministrativeAreaDelete{config: c.config, hooks: c.Hooks(), mutation: m}).Exec(ctx) + default: + return nil, fmt.Errorf("ent: unknown AdministrativeArea mutation op: %q", m.Op()) + } +} + +// ContactRevealDailyLimitClient is a client for the ContactRevealDailyLimit schema. +type ContactRevealDailyLimitClient struct { + config +} + +// NewContactRevealDailyLimitClient returns a client for the ContactRevealDailyLimit from the given config. +func NewContactRevealDailyLimitClient(c config) *ContactRevealDailyLimitClient { + return &ContactRevealDailyLimitClient{config: c} +} + +// Use adds a list of mutation hooks to the hooks stack. +// A call to `Use(f, g, h)` equals to `contactrevealdailylimit.Hooks(f(g(h())))`. +func (c *ContactRevealDailyLimitClient) Use(hooks ...Hook) { + c.hooks.ContactRevealDailyLimit = append(c.hooks.ContactRevealDailyLimit, hooks...) +} + +// Intercept adds a list of query interceptors to the interceptors stack. +// A call to `Intercept(f, g, h)` equals to `contactrevealdailylimit.Intercept(f(g(h())))`. +func (c *ContactRevealDailyLimitClient) Intercept(interceptors ...Interceptor) { + c.inters.ContactRevealDailyLimit = append(c.inters.ContactRevealDailyLimit, interceptors...) +} + +// Create returns a builder for creating a ContactRevealDailyLimit entity. +func (c *ContactRevealDailyLimitClient) Create() *ContactRevealDailyLimitCreate { + mutation := newContactRevealDailyLimitMutation(c.config, OpCreate) + return &ContactRevealDailyLimitCreate{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// CreateBulk returns a builder for creating a bulk of ContactRevealDailyLimit entities. +func (c *ContactRevealDailyLimitClient) CreateBulk(builders ...*ContactRevealDailyLimitCreate) *ContactRevealDailyLimitCreateBulk { + return &ContactRevealDailyLimitCreateBulk{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 *ContactRevealDailyLimitClient) MapCreateBulk(slice any, setFunc func(*ContactRevealDailyLimitCreate, int)) *ContactRevealDailyLimitCreateBulk { + rv := reflect.ValueOf(slice) + if rv.Kind() != reflect.Slice { + return &ContactRevealDailyLimitCreateBulk{err: fmt.Errorf("calling to ContactRevealDailyLimitClient.MapCreateBulk with wrong type %T, need slice", slice)} + } + builders := make([]*ContactRevealDailyLimitCreate, rv.Len()) + for i := 0; i < rv.Len(); i++ { + builders[i] = c.Create() + setFunc(builders[i], i) + } + return &ContactRevealDailyLimitCreateBulk{config: c.config, builders: builders} +} + +// Update returns an update builder for ContactRevealDailyLimit. +func (c *ContactRevealDailyLimitClient) Update() *ContactRevealDailyLimitUpdate { + mutation := newContactRevealDailyLimitMutation(c.config, OpUpdate) + return &ContactRevealDailyLimitUpdate{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// UpdateOne returns an update builder for the given entity. +func (c *ContactRevealDailyLimitClient) UpdateOne(_m *ContactRevealDailyLimit) *ContactRevealDailyLimitUpdateOne { + mutation := newContactRevealDailyLimitMutation(c.config, OpUpdateOne, withContactRevealDailyLimit(_m)) + return &ContactRevealDailyLimitUpdateOne{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// UpdateOneID returns an update builder for the given id. +func (c *ContactRevealDailyLimitClient) UpdateOneID(id uuid.UUID) *ContactRevealDailyLimitUpdateOne { + mutation := newContactRevealDailyLimitMutation(c.config, OpUpdateOne, withContactRevealDailyLimitID(id)) + return &ContactRevealDailyLimitUpdateOne{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// Delete returns a delete builder for ContactRevealDailyLimit. +func (c *ContactRevealDailyLimitClient) Delete() *ContactRevealDailyLimitDelete { + mutation := newContactRevealDailyLimitMutation(c.config, OpDelete) + return &ContactRevealDailyLimitDelete{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// DeleteOne returns a builder for deleting the given entity. +func (c *ContactRevealDailyLimitClient) DeleteOne(_m *ContactRevealDailyLimit) *ContactRevealDailyLimitDeleteOne { + return c.DeleteOneID(_m.ID) +} + +// DeleteOneID returns a builder for deleting the given entity by its id. +func (c *ContactRevealDailyLimitClient) DeleteOneID(id uuid.UUID) *ContactRevealDailyLimitDeleteOne { + builder := c.Delete().Where(contactrevealdailylimit.ID(id)) + builder.mutation.id = &id + builder.mutation.op = OpDeleteOne + return &ContactRevealDailyLimitDeleteOne{builder} +} + +// Query returns a query builder for ContactRevealDailyLimit. +func (c *ContactRevealDailyLimitClient) Query() *ContactRevealDailyLimitQuery { + return &ContactRevealDailyLimitQuery{ + config: c.config, + ctx: &QueryContext{Type: TypeContactRevealDailyLimit}, + inters: c.Interceptors(), + } +} + +// Get returns a ContactRevealDailyLimit entity by its id. +func (c *ContactRevealDailyLimitClient) Get(ctx context.Context, id uuid.UUID) (*ContactRevealDailyLimit, error) { + return c.Query().Where(contactrevealdailylimit.ID(id)).Only(ctx) +} + +// GetX is like Get, but panics if an error occurs. +func (c *ContactRevealDailyLimitClient) GetX(ctx context.Context, id uuid.UUID) *ContactRevealDailyLimit { + obj, err := c.Get(ctx, id) + if err != nil { + panic(err) + } + return obj +} + +// Hooks returns the client hooks. +func (c *ContactRevealDailyLimitClient) Hooks() []Hook { + return c.hooks.ContactRevealDailyLimit +} + +// Interceptors returns the client interceptors. +func (c *ContactRevealDailyLimitClient) Interceptors() []Interceptor { + return c.inters.ContactRevealDailyLimit +} + +func (c *ContactRevealDailyLimitClient) mutate(ctx context.Context, m *ContactRevealDailyLimitMutation) (Value, error) { + switch m.Op() { + case OpCreate: + return (&ContactRevealDailyLimitCreate{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx) + case OpUpdate: + return (&ContactRevealDailyLimitUpdate{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx) + case OpUpdateOne: + return (&ContactRevealDailyLimitUpdateOne{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx) + case OpDelete, OpDeleteOne: + return (&ContactRevealDailyLimitDelete{config: c.config, hooks: c.Hooks(), mutation: m}).Exec(ctx) + default: + return nil, fmt.Errorf("ent: unknown ContactRevealDailyLimit mutation op: %q", m.Op()) + } +} + +// ContactRevealEventClient is a client for the ContactRevealEvent schema. +type ContactRevealEventClient struct { + config +} + +// NewContactRevealEventClient returns a client for the ContactRevealEvent from the given config. +func NewContactRevealEventClient(c config) *ContactRevealEventClient { + return &ContactRevealEventClient{config: c} +} + +// Use adds a list of mutation hooks to the hooks stack. +// A call to `Use(f, g, h)` equals to `contactrevealevent.Hooks(f(g(h())))`. +func (c *ContactRevealEventClient) Use(hooks ...Hook) { + c.hooks.ContactRevealEvent = append(c.hooks.ContactRevealEvent, hooks...) +} + +// Intercept adds a list of query interceptors to the interceptors stack. +// A call to `Intercept(f, g, h)` equals to `contactrevealevent.Intercept(f(g(h())))`. +func (c *ContactRevealEventClient) Intercept(interceptors ...Interceptor) { + c.inters.ContactRevealEvent = append(c.inters.ContactRevealEvent, interceptors...) +} + +// Create returns a builder for creating a ContactRevealEvent entity. +func (c *ContactRevealEventClient) Create() *ContactRevealEventCreate { + mutation := newContactRevealEventMutation(c.config, OpCreate) + return &ContactRevealEventCreate{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// CreateBulk returns a builder for creating a bulk of ContactRevealEvent entities. +func (c *ContactRevealEventClient) CreateBulk(builders ...*ContactRevealEventCreate) *ContactRevealEventCreateBulk { + return &ContactRevealEventCreateBulk{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 *ContactRevealEventClient) MapCreateBulk(slice any, setFunc func(*ContactRevealEventCreate, int)) *ContactRevealEventCreateBulk { + rv := reflect.ValueOf(slice) + if rv.Kind() != reflect.Slice { + return &ContactRevealEventCreateBulk{err: fmt.Errorf("calling to ContactRevealEventClient.MapCreateBulk with wrong type %T, need slice", slice)} + } + builders := make([]*ContactRevealEventCreate, rv.Len()) + for i := 0; i < rv.Len(); i++ { + builders[i] = c.Create() + setFunc(builders[i], i) + } + return &ContactRevealEventCreateBulk{config: c.config, builders: builders} +} + +// Update returns an update builder for ContactRevealEvent. +func (c *ContactRevealEventClient) Update() *ContactRevealEventUpdate { + mutation := newContactRevealEventMutation(c.config, OpUpdate) + return &ContactRevealEventUpdate{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// UpdateOne returns an update builder for the given entity. +func (c *ContactRevealEventClient) UpdateOne(_m *ContactRevealEvent) *ContactRevealEventUpdateOne { + mutation := newContactRevealEventMutation(c.config, OpUpdateOne, withContactRevealEvent(_m)) + return &ContactRevealEventUpdateOne{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// UpdateOneID returns an update builder for the given id. +func (c *ContactRevealEventClient) UpdateOneID(id uuid.UUID) *ContactRevealEventUpdateOne { + mutation := newContactRevealEventMutation(c.config, OpUpdateOne, withContactRevealEventID(id)) + return &ContactRevealEventUpdateOne{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// Delete returns a delete builder for ContactRevealEvent. +func (c *ContactRevealEventClient) Delete() *ContactRevealEventDelete { + mutation := newContactRevealEventMutation(c.config, OpDelete) + return &ContactRevealEventDelete{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// DeleteOne returns a builder for deleting the given entity. +func (c *ContactRevealEventClient) DeleteOne(_m *ContactRevealEvent) *ContactRevealEventDeleteOne { + return c.DeleteOneID(_m.ID) +} + +// DeleteOneID returns a builder for deleting the given entity by its id. +func (c *ContactRevealEventClient) DeleteOneID(id uuid.UUID) *ContactRevealEventDeleteOne { + builder := c.Delete().Where(contactrevealevent.ID(id)) + builder.mutation.id = &id + builder.mutation.op = OpDeleteOne + return &ContactRevealEventDeleteOne{builder} +} + +// Query returns a query builder for ContactRevealEvent. +func (c *ContactRevealEventClient) Query() *ContactRevealEventQuery { + return &ContactRevealEventQuery{ + config: c.config, + ctx: &QueryContext{Type: TypeContactRevealEvent}, + inters: c.Interceptors(), + } +} + +// Get returns a ContactRevealEvent entity by its id. +func (c *ContactRevealEventClient) Get(ctx context.Context, id uuid.UUID) (*ContactRevealEvent, error) { + return c.Query().Where(contactrevealevent.ID(id)).Only(ctx) +} + +// GetX is like Get, but panics if an error occurs. +func (c *ContactRevealEventClient) GetX(ctx context.Context, id uuid.UUID) *ContactRevealEvent { + obj, err := c.Get(ctx, id) + if err != nil { + panic(err) + } + return obj +} + +// Hooks returns the client hooks. +func (c *ContactRevealEventClient) Hooks() []Hook { + return c.hooks.ContactRevealEvent +} + +// Interceptors returns the client interceptors. +func (c *ContactRevealEventClient) Interceptors() []Interceptor { + return c.inters.ContactRevealEvent +} + +func (c *ContactRevealEventClient) mutate(ctx context.Context, m *ContactRevealEventMutation) (Value, error) { + switch m.Op() { + case OpCreate: + return (&ContactRevealEventCreate{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx) + case OpUpdate: + return (&ContactRevealEventUpdate{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx) + case OpUpdateOne: + return (&ContactRevealEventUpdateOne{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx) + case OpDelete, OpDeleteOne: + return (&ContactRevealEventDelete{config: c.config, hooks: c.Hooks(), mutation: m}).Exec(ctx) + default: + return nil, fmt.Errorf("ent: unknown ContactRevealEvent mutation op: %q", m.Op()) + } +} + +// InternalUserClient is a client for the InternalUser schema. +type InternalUserClient struct { + config +} + +// NewInternalUserClient returns a client for the InternalUser from the given config. +func NewInternalUserClient(c config) *InternalUserClient { + return &InternalUserClient{config: c} +} + +// Use adds a list of mutation hooks to the hooks stack. +// A call to `Use(f, g, h)` equals to `internaluser.Hooks(f(g(h())))`. +func (c *InternalUserClient) Use(hooks ...Hook) { + c.hooks.InternalUser = append(c.hooks.InternalUser, hooks...) +} + +// Intercept adds a list of query interceptors to the interceptors stack. +// A call to `Intercept(f, g, h)` equals to `internaluser.Intercept(f(g(h())))`. +func (c *InternalUserClient) Intercept(interceptors ...Interceptor) { + c.inters.InternalUser = append(c.inters.InternalUser, interceptors...) +} + +// Create returns a builder for creating a InternalUser entity. +func (c *InternalUserClient) Create() *InternalUserCreate { + mutation := newInternalUserMutation(c.config, OpCreate) + return &InternalUserCreate{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// CreateBulk returns a builder for creating a bulk of InternalUser entities. +func (c *InternalUserClient) CreateBulk(builders ...*InternalUserCreate) *InternalUserCreateBulk { + return &InternalUserCreateBulk{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 *InternalUserClient) MapCreateBulk(slice any, setFunc func(*InternalUserCreate, int)) *InternalUserCreateBulk { + rv := reflect.ValueOf(slice) + if rv.Kind() != reflect.Slice { + return &InternalUserCreateBulk{err: fmt.Errorf("calling to InternalUserClient.MapCreateBulk with wrong type %T, need slice", slice)} + } + builders := make([]*InternalUserCreate, rv.Len()) + for i := 0; i < rv.Len(); i++ { + builders[i] = c.Create() + setFunc(builders[i], i) + } + return &InternalUserCreateBulk{config: c.config, builders: builders} +} + +// Update returns an update builder for InternalUser. +func (c *InternalUserClient) Update() *InternalUserUpdate { + mutation := newInternalUserMutation(c.config, OpUpdate) + return &InternalUserUpdate{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// UpdateOne returns an update builder for the given entity. +func (c *InternalUserClient) UpdateOne(_m *InternalUser) *InternalUserUpdateOne { + mutation := newInternalUserMutation(c.config, OpUpdateOne, withInternalUser(_m)) + return &InternalUserUpdateOne{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// UpdateOneID returns an update builder for the given id. +func (c *InternalUserClient) UpdateOneID(id uuid.UUID) *InternalUserUpdateOne { + mutation := newInternalUserMutation(c.config, OpUpdateOne, withInternalUserID(id)) + return &InternalUserUpdateOne{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// Delete returns a delete builder for InternalUser. +func (c *InternalUserClient) Delete() *InternalUserDelete { + mutation := newInternalUserMutation(c.config, OpDelete) + return &InternalUserDelete{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// DeleteOne returns a builder for deleting the given entity. +func (c *InternalUserClient) DeleteOne(_m *InternalUser) *InternalUserDeleteOne { + return c.DeleteOneID(_m.ID) +} + +// DeleteOneID returns a builder for deleting the given entity by its id. +func (c *InternalUserClient) DeleteOneID(id uuid.UUID) *InternalUserDeleteOne { + builder := c.Delete().Where(internaluser.ID(id)) + builder.mutation.id = &id + builder.mutation.op = OpDeleteOne + return &InternalUserDeleteOne{builder} +} + +// Query returns a query builder for InternalUser. +func (c *InternalUserClient) Query() *InternalUserQuery { + return &InternalUserQuery{ + config: c.config, + ctx: &QueryContext{Type: TypeInternalUser}, + inters: c.Interceptors(), + } +} + +// Get returns a InternalUser entity by its id. +func (c *InternalUserClient) Get(ctx context.Context, id uuid.UUID) (*InternalUser, error) { + return c.Query().Where(internaluser.ID(id)).Only(ctx) +} + +// GetX is like Get, but panics if an error occurs. +func (c *InternalUserClient) GetX(ctx context.Context, id uuid.UUID) *InternalUser { + obj, err := c.Get(ctx, id) + if err != nil { + panic(err) + } + return obj +} + +// Hooks returns the client hooks. +func (c *InternalUserClient) Hooks() []Hook { + return c.hooks.InternalUser +} + +// Interceptors returns the client interceptors. +func (c *InternalUserClient) Interceptors() []Interceptor { + return c.inters.InternalUser +} + +func (c *InternalUserClient) mutate(ctx context.Context, m *InternalUserMutation) (Value, error) { + switch m.Op() { + case OpCreate: + return (&InternalUserCreate{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx) + case OpUpdate: + return (&InternalUserUpdate{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx) + case OpUpdateOne: + return (&InternalUserUpdateOne{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx) + case OpDelete, OpDeleteOne: + return (&InternalUserDelete{config: c.config, hooks: c.Hooks(), mutation: m}).Exec(ctx) + default: + return nil, fmt.Errorf("ent: unknown InternalUser mutation op: %q", m.Op()) + } +} + +// ListingClient is a client for the Listing schema. +type ListingClient struct { + config +} + +// NewListingClient returns a client for the Listing from the given config. +func NewListingClient(c config) *ListingClient { + return &ListingClient{config: c} +} + +// Use adds a list of mutation hooks to the hooks stack. +// A call to `Use(f, g, h)` equals to `listing.Hooks(f(g(h())))`. +func (c *ListingClient) Use(hooks ...Hook) { + c.hooks.Listing = append(c.hooks.Listing, hooks...) +} + +// Intercept adds a list of query interceptors to the interceptors stack. +// A call to `Intercept(f, g, h)` equals to `listing.Intercept(f(g(h())))`. +func (c *ListingClient) Intercept(interceptors ...Interceptor) { + c.inters.Listing = append(c.inters.Listing, interceptors...) +} + +// Create returns a builder for creating a Listing entity. +func (c *ListingClient) Create() *ListingCreate { + mutation := newListingMutation(c.config, OpCreate) + return &ListingCreate{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// CreateBulk returns a builder for creating a bulk of Listing entities. +func (c *ListingClient) CreateBulk(builders ...*ListingCreate) *ListingCreateBulk { + return &ListingCreateBulk{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 *ListingClient) MapCreateBulk(slice any, setFunc func(*ListingCreate, int)) *ListingCreateBulk { + rv := reflect.ValueOf(slice) + if rv.Kind() != reflect.Slice { + return &ListingCreateBulk{err: fmt.Errorf("calling to ListingClient.MapCreateBulk with wrong type %T, need slice", slice)} + } + builders := make([]*ListingCreate, rv.Len()) + for i := 0; i < rv.Len(); i++ { + builders[i] = c.Create() + setFunc(builders[i], i) + } + return &ListingCreateBulk{config: c.config, builders: builders} +} + +// Update returns an update builder for Listing. +func (c *ListingClient) Update() *ListingUpdate { + mutation := newListingMutation(c.config, OpUpdate) + return &ListingUpdate{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// UpdateOne returns an update builder for the given entity. +func (c *ListingClient) UpdateOne(_m *Listing) *ListingUpdateOne { + mutation := newListingMutation(c.config, OpUpdateOne, withListing(_m)) + return &ListingUpdateOne{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// UpdateOneID returns an update builder for the given id. +func (c *ListingClient) UpdateOneID(id uuid.UUID) *ListingUpdateOne { + mutation := newListingMutation(c.config, OpUpdateOne, withListingID(id)) + return &ListingUpdateOne{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// Delete returns a delete builder for Listing. +func (c *ListingClient) Delete() *ListingDelete { + mutation := newListingMutation(c.config, OpDelete) + return &ListingDelete{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// DeleteOne returns a builder for deleting the given entity. +func (c *ListingClient) DeleteOne(_m *Listing) *ListingDeleteOne { + return c.DeleteOneID(_m.ID) +} + +// DeleteOneID returns a builder for deleting the given entity by its id. +func (c *ListingClient) DeleteOneID(id uuid.UUID) *ListingDeleteOne { + builder := c.Delete().Where(listing.ID(id)) + builder.mutation.id = &id + builder.mutation.op = OpDeleteOne + return &ListingDeleteOne{builder} +} + +// Query returns a query builder for Listing. +func (c *ListingClient) Query() *ListingQuery { + return &ListingQuery{ + config: c.config, + ctx: &QueryContext{Type: TypeListing}, + inters: c.Interceptors(), + } +} + +// Get returns a Listing entity by its id. +func (c *ListingClient) Get(ctx context.Context, id uuid.UUID) (*Listing, error) { + return c.Query().Where(listing.ID(id)).Only(ctx) +} + +// GetX is like Get, but panics if an error occurs. +func (c *ListingClient) GetX(ctx context.Context, id uuid.UUID) *Listing { + obj, err := c.Get(ctx, id) + if err != nil { + panic(err) + } + return obj +} + +// Hooks returns the client hooks. +func (c *ListingClient) Hooks() []Hook { + return c.hooks.Listing +} + +// Interceptors returns the client interceptors. +func (c *ListingClient) Interceptors() []Interceptor { + return c.inters.Listing +} + +func (c *ListingClient) mutate(ctx context.Context, m *ListingMutation) (Value, error) { + switch m.Op() { + case OpCreate: + return (&ListingCreate{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx) + case OpUpdate: + return (&ListingUpdate{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx) + case OpUpdateOne: + return (&ListingUpdateOne{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx) + case OpDelete, OpDeleteOne: + return (&ListingDelete{config: c.config, hooks: c.Hooks(), mutation: m}).Exec(ctx) + default: + return nil, fmt.Errorf("ent: unknown Listing mutation op: %q", m.Op()) + } +} + +// ListingEventClient is a client for the ListingEvent schema. +type ListingEventClient struct { + config +} + +// NewListingEventClient returns a client for the ListingEvent from the given config. +func NewListingEventClient(c config) *ListingEventClient { + return &ListingEventClient{config: c} +} + +// Use adds a list of mutation hooks to the hooks stack. +// A call to `Use(f, g, h)` equals to `listingevent.Hooks(f(g(h())))`. +func (c *ListingEventClient) Use(hooks ...Hook) { + c.hooks.ListingEvent = append(c.hooks.ListingEvent, hooks...) +} + +// Intercept adds a list of query interceptors to the interceptors stack. +// A call to `Intercept(f, g, h)` equals to `listingevent.Intercept(f(g(h())))`. +func (c *ListingEventClient) Intercept(interceptors ...Interceptor) { + c.inters.ListingEvent = append(c.inters.ListingEvent, interceptors...) +} + +// Create returns a builder for creating a ListingEvent entity. +func (c *ListingEventClient) Create() *ListingEventCreate { + mutation := newListingEventMutation(c.config, OpCreate) + return &ListingEventCreate{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// CreateBulk returns a builder for creating a bulk of ListingEvent entities. +func (c *ListingEventClient) CreateBulk(builders ...*ListingEventCreate) *ListingEventCreateBulk { + return &ListingEventCreateBulk{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 *ListingEventClient) MapCreateBulk(slice any, setFunc func(*ListingEventCreate, int)) *ListingEventCreateBulk { + rv := reflect.ValueOf(slice) + if rv.Kind() != reflect.Slice { + return &ListingEventCreateBulk{err: fmt.Errorf("calling to ListingEventClient.MapCreateBulk with wrong type %T, need slice", slice)} + } + builders := make([]*ListingEventCreate, rv.Len()) + for i := 0; i < rv.Len(); i++ { + builders[i] = c.Create() + setFunc(builders[i], i) + } + return &ListingEventCreateBulk{config: c.config, builders: builders} +} + +// Update returns an update builder for ListingEvent. +func (c *ListingEventClient) Update() *ListingEventUpdate { + mutation := newListingEventMutation(c.config, OpUpdate) + return &ListingEventUpdate{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// UpdateOne returns an update builder for the given entity. +func (c *ListingEventClient) UpdateOne(_m *ListingEvent) *ListingEventUpdateOne { + mutation := newListingEventMutation(c.config, OpUpdateOne, withListingEvent(_m)) + return &ListingEventUpdateOne{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// UpdateOneID returns an update builder for the given id. +func (c *ListingEventClient) UpdateOneID(id uuid.UUID) *ListingEventUpdateOne { + mutation := newListingEventMutation(c.config, OpUpdateOne, withListingEventID(id)) + return &ListingEventUpdateOne{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// Delete returns a delete builder for ListingEvent. +func (c *ListingEventClient) Delete() *ListingEventDelete { + mutation := newListingEventMutation(c.config, OpDelete) + return &ListingEventDelete{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// DeleteOne returns a builder for deleting the given entity. +func (c *ListingEventClient) DeleteOne(_m *ListingEvent) *ListingEventDeleteOne { + return c.DeleteOneID(_m.ID) +} + +// DeleteOneID returns a builder for deleting the given entity by its id. +func (c *ListingEventClient) DeleteOneID(id uuid.UUID) *ListingEventDeleteOne { + builder := c.Delete().Where(listingevent.ID(id)) + builder.mutation.id = &id + builder.mutation.op = OpDeleteOne + return &ListingEventDeleteOne{builder} +} + +// Query returns a query builder for ListingEvent. +func (c *ListingEventClient) Query() *ListingEventQuery { + return &ListingEventQuery{ + config: c.config, + ctx: &QueryContext{Type: TypeListingEvent}, + inters: c.Interceptors(), + } +} + +// Get returns a ListingEvent entity by its id. +func (c *ListingEventClient) Get(ctx context.Context, id uuid.UUID) (*ListingEvent, error) { + return c.Query().Where(listingevent.ID(id)).Only(ctx) +} + +// GetX is like Get, but panics if an error occurs. +func (c *ListingEventClient) GetX(ctx context.Context, id uuid.UUID) *ListingEvent { + obj, err := c.Get(ctx, id) + if err != nil { + panic(err) + } + return obj +} + +// Hooks returns the client hooks. +func (c *ListingEventClient) Hooks() []Hook { + return c.hooks.ListingEvent +} + +// Interceptors returns the client interceptors. +func (c *ListingEventClient) Interceptors() []Interceptor { + return c.inters.ListingEvent +} + +func (c *ListingEventClient) mutate(ctx context.Context, m *ListingEventMutation) (Value, error) { + switch m.Op() { + case OpCreate: + return (&ListingEventCreate{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx) + case OpUpdate: + return (&ListingEventUpdate{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx) + case OpUpdateOne: + return (&ListingEventUpdateOne{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx) + case OpDelete, OpDeleteOne: + return (&ListingEventDelete{config: c.config, hooks: c.Hooks(), mutation: m}).Exec(ctx) + default: + return nil, fmt.Errorf("ent: unknown ListingEvent mutation op: %q", m.Op()) + } +} + +// ListingMediaClient is a client for the ListingMedia schema. +type ListingMediaClient struct { + config +} + +// NewListingMediaClient returns a client for the ListingMedia from the given config. +func NewListingMediaClient(c config) *ListingMediaClient { + return &ListingMediaClient{config: c} +} + +// Use adds a list of mutation hooks to the hooks stack. +// A call to `Use(f, g, h)` equals to `listingmedia.Hooks(f(g(h())))`. +func (c *ListingMediaClient) Use(hooks ...Hook) { + c.hooks.ListingMedia = append(c.hooks.ListingMedia, hooks...) +} + +// Intercept adds a list of query interceptors to the interceptors stack. +// A call to `Intercept(f, g, h)` equals to `listingmedia.Intercept(f(g(h())))`. +func (c *ListingMediaClient) Intercept(interceptors ...Interceptor) { + c.inters.ListingMedia = append(c.inters.ListingMedia, interceptors...) +} + +// Create returns a builder for creating a ListingMedia entity. +func (c *ListingMediaClient) Create() *ListingMediaCreate { + mutation := newListingMediaMutation(c.config, OpCreate) + return &ListingMediaCreate{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// CreateBulk returns a builder for creating a bulk of ListingMedia entities. +func (c *ListingMediaClient) CreateBulk(builders ...*ListingMediaCreate) *ListingMediaCreateBulk { + return &ListingMediaCreateBulk{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 *ListingMediaClient) MapCreateBulk(slice any, setFunc func(*ListingMediaCreate, int)) *ListingMediaCreateBulk { + rv := reflect.ValueOf(slice) + if rv.Kind() != reflect.Slice { + return &ListingMediaCreateBulk{err: fmt.Errorf("calling to ListingMediaClient.MapCreateBulk with wrong type %T, need slice", slice)} + } + builders := make([]*ListingMediaCreate, rv.Len()) + for i := 0; i < rv.Len(); i++ { + builders[i] = c.Create() + setFunc(builders[i], i) + } + return &ListingMediaCreateBulk{config: c.config, builders: builders} +} + +// Update returns an update builder for ListingMedia. +func (c *ListingMediaClient) Update() *ListingMediaUpdate { + mutation := newListingMediaMutation(c.config, OpUpdate) + return &ListingMediaUpdate{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// UpdateOne returns an update builder for the given entity. +func (c *ListingMediaClient) UpdateOne(_m *ListingMedia) *ListingMediaUpdateOne { + mutation := newListingMediaMutation(c.config, OpUpdateOne, withListingMedia(_m)) + return &ListingMediaUpdateOne{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// UpdateOneID returns an update builder for the given id. +func (c *ListingMediaClient) UpdateOneID(id uuid.UUID) *ListingMediaUpdateOne { + mutation := newListingMediaMutation(c.config, OpUpdateOne, withListingMediaID(id)) + return &ListingMediaUpdateOne{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// Delete returns a delete builder for ListingMedia. +func (c *ListingMediaClient) Delete() *ListingMediaDelete { + mutation := newListingMediaMutation(c.config, OpDelete) + return &ListingMediaDelete{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// DeleteOne returns a builder for deleting the given entity. +func (c *ListingMediaClient) DeleteOne(_m *ListingMedia) *ListingMediaDeleteOne { + return c.DeleteOneID(_m.ID) +} + +// DeleteOneID returns a builder for deleting the given entity by its id. +func (c *ListingMediaClient) DeleteOneID(id uuid.UUID) *ListingMediaDeleteOne { + builder := c.Delete().Where(listingmedia.ID(id)) + builder.mutation.id = &id + builder.mutation.op = OpDeleteOne + return &ListingMediaDeleteOne{builder} +} + +// Query returns a query builder for ListingMedia. +func (c *ListingMediaClient) Query() *ListingMediaQuery { + return &ListingMediaQuery{ + config: c.config, + ctx: &QueryContext{Type: TypeListingMedia}, + inters: c.Interceptors(), + } +} + +// Get returns a ListingMedia entity by its id. +func (c *ListingMediaClient) Get(ctx context.Context, id uuid.UUID) (*ListingMedia, error) { + return c.Query().Where(listingmedia.ID(id)).Only(ctx) +} + +// GetX is like Get, but panics if an error occurs. +func (c *ListingMediaClient) GetX(ctx context.Context, id uuid.UUID) *ListingMedia { + obj, err := c.Get(ctx, id) + if err != nil { + panic(err) + } + return obj +} + +// Hooks returns the client hooks. +func (c *ListingMediaClient) Hooks() []Hook { + return c.hooks.ListingMedia +} + +// Interceptors returns the client interceptors. +func (c *ListingMediaClient) Interceptors() []Interceptor { + return c.inters.ListingMedia +} + +func (c *ListingMediaClient) mutate(ctx context.Context, m *ListingMediaMutation) (Value, error) { + switch m.Op() { + case OpCreate: + return (&ListingMediaCreate{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx) + case OpUpdate: + return (&ListingMediaUpdate{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx) + case OpUpdateOne: + return (&ListingMediaUpdateOne{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx) + case OpDelete, OpDeleteOne: + return (&ListingMediaDelete{config: c.config, hooks: c.Hooks(), mutation: m}).Exec(ctx) + default: + return nil, fmt.Errorf("ent: unknown ListingMedia mutation op: %q", m.Op()) + } +} + +// LocalityClient is a client for the Locality schema. +type LocalityClient struct { + config +} + +// NewLocalityClient returns a client for the Locality from the given config. +func NewLocalityClient(c config) *LocalityClient { + return &LocalityClient{config: c} +} + +// Use adds a list of mutation hooks to the hooks stack. +// A call to `Use(f, g, h)` equals to `locality.Hooks(f(g(h())))`. +func (c *LocalityClient) Use(hooks ...Hook) { + c.hooks.Locality = append(c.hooks.Locality, hooks...) +} + +// Intercept adds a list of query interceptors to the interceptors stack. +// A call to `Intercept(f, g, h)` equals to `locality.Intercept(f(g(h())))`. +func (c *LocalityClient) Intercept(interceptors ...Interceptor) { + c.inters.Locality = append(c.inters.Locality, interceptors...) +} + +// Create returns a builder for creating a Locality entity. +func (c *LocalityClient) Create() *LocalityCreate { + mutation := newLocalityMutation(c.config, OpCreate) + return &LocalityCreate{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// CreateBulk returns a builder for creating a bulk of Locality entities. +func (c *LocalityClient) CreateBulk(builders ...*LocalityCreate) *LocalityCreateBulk { + return &LocalityCreateBulk{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 *LocalityClient) MapCreateBulk(slice any, setFunc func(*LocalityCreate, int)) *LocalityCreateBulk { + rv := reflect.ValueOf(slice) + if rv.Kind() != reflect.Slice { + return &LocalityCreateBulk{err: fmt.Errorf("calling to LocalityClient.MapCreateBulk with wrong type %T, need slice", slice)} + } + builders := make([]*LocalityCreate, rv.Len()) + for i := 0; i < rv.Len(); i++ { + builders[i] = c.Create() + setFunc(builders[i], i) + } + return &LocalityCreateBulk{config: c.config, builders: builders} +} + +// Update returns an update builder for Locality. +func (c *LocalityClient) Update() *LocalityUpdate { + mutation := newLocalityMutation(c.config, OpUpdate) + return &LocalityUpdate{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// UpdateOne returns an update builder for the given entity. +func (c *LocalityClient) UpdateOne(_m *Locality) *LocalityUpdateOne { + mutation := newLocalityMutation(c.config, OpUpdateOne, withLocality(_m)) + return &LocalityUpdateOne{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// UpdateOneID returns an update builder for the given id. +func (c *LocalityClient) UpdateOneID(id uuid.UUID) *LocalityUpdateOne { + mutation := newLocalityMutation(c.config, OpUpdateOne, withLocalityID(id)) + return &LocalityUpdateOne{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// Delete returns a delete builder for Locality. +func (c *LocalityClient) Delete() *LocalityDelete { + mutation := newLocalityMutation(c.config, OpDelete) + return &LocalityDelete{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// DeleteOne returns a builder for deleting the given entity. +func (c *LocalityClient) DeleteOne(_m *Locality) *LocalityDeleteOne { + return c.DeleteOneID(_m.ID) +} + +// DeleteOneID returns a builder for deleting the given entity by its id. +func (c *LocalityClient) DeleteOneID(id uuid.UUID) *LocalityDeleteOne { + builder := c.Delete().Where(locality.ID(id)) + builder.mutation.id = &id + builder.mutation.op = OpDeleteOne + return &LocalityDeleteOne{builder} +} + +// Query returns a query builder for Locality. +func (c *LocalityClient) Query() *LocalityQuery { + return &LocalityQuery{ + config: c.config, + ctx: &QueryContext{Type: TypeLocality}, + inters: c.Interceptors(), + } +} + +// Get returns a Locality entity by its id. +func (c *LocalityClient) Get(ctx context.Context, id uuid.UUID) (*Locality, error) { + return c.Query().Where(locality.ID(id)).Only(ctx) +} + +// GetX is like Get, but panics if an error occurs. +func (c *LocalityClient) GetX(ctx context.Context, id uuid.UUID) *Locality { + obj, err := c.Get(ctx, id) + if err != nil { + panic(err) + } + return obj +} + +// QueryParentParish queries the parent_parish edge of a Locality. +func (c *LocalityClient) QueryParentParish(_m *Locality) *AdministrativeAreaQuery { + query := (&AdministrativeAreaClient{config: c.config}).Query() + query.path = func(context.Context) (fromV *sql.Selector, _ error) { + id := _m.ID + step := sqlgraph.NewStep( + sqlgraph.From(locality.Table, locality.FieldID, id), + sqlgraph.To(administrativearea.Table, administrativearea.FieldID), + sqlgraph.Edge(sqlgraph.M2O, true, locality.ParentParishTable, locality.ParentParishColumn), + ) + fromV = sqlgraph.Neighbors(_m.driver.Dialect(), step) + return fromV, nil + } + return query +} + +// QueryProviderProfiles queries the provider_profiles edge of a Locality. +func (c *LocalityClient) QueryProviderProfiles(_m *Locality) *ProviderProfileQuery { + query := (&ProviderProfileClient{config: c.config}).Query() + query.path = func(context.Context) (fromV *sql.Selector, _ error) { + id := _m.ID + step := sqlgraph.NewStep( + sqlgraph.From(locality.Table, locality.FieldID, id), + sqlgraph.To(providerprofile.Table, providerprofile.FieldID), + sqlgraph.Edge(sqlgraph.M2M, true, locality.ProviderProfilesTable, locality.ProviderProfilesPrimaryKey...), + ) + fromV = sqlgraph.Neighbors(_m.driver.Dialect(), step) + return fromV, nil + } + return query +} + +// Hooks returns the client hooks. +func (c *LocalityClient) Hooks() []Hook { + return c.hooks.Locality +} + +// Interceptors returns the client interceptors. +func (c *LocalityClient) Interceptors() []Interceptor { + return c.inters.Locality +} + +func (c *LocalityClient) mutate(ctx context.Context, m *LocalityMutation) (Value, error) { + switch m.Op() { + case OpCreate: + return (&LocalityCreate{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx) + case OpUpdate: + return (&LocalityUpdate{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx) + case OpUpdateOne: + return (&LocalityUpdateOne{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx) + case OpDelete, OpDeleteOne: + return (&LocalityDelete{config: c.config, hooks: c.Hooks(), mutation: m}).Exec(ctx) + default: + return nil, fmt.Errorf("ent: unknown Locality mutation op: %q", m.Op()) + } +} + +// PlatformRoleClient is a client for the PlatformRole schema. +type PlatformRoleClient struct { + config +} + +// NewPlatformRoleClient returns a client for the PlatformRole from the given config. +func NewPlatformRoleClient(c config) *PlatformRoleClient { + return &PlatformRoleClient{config: c} +} + +// Use adds a list of mutation hooks to the hooks stack. +// A call to `Use(f, g, h)` equals to `platformrole.Hooks(f(g(h())))`. +func (c *PlatformRoleClient) Use(hooks ...Hook) { + c.hooks.PlatformRole = append(c.hooks.PlatformRole, hooks...) +} + +// Intercept adds a list of query interceptors to the interceptors stack. +// A call to `Intercept(f, g, h)` equals to `platformrole.Intercept(f(g(h())))`. +func (c *PlatformRoleClient) Intercept(interceptors ...Interceptor) { + c.inters.PlatformRole = append(c.inters.PlatformRole, interceptors...) +} + +// Create returns a builder for creating a PlatformRole entity. +func (c *PlatformRoleClient) Create() *PlatformRoleCreate { + mutation := newPlatformRoleMutation(c.config, OpCreate) + return &PlatformRoleCreate{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// CreateBulk returns a builder for creating a bulk of PlatformRole entities. +func (c *PlatformRoleClient) CreateBulk(builders ...*PlatformRoleCreate) *PlatformRoleCreateBulk { + return &PlatformRoleCreateBulk{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 *PlatformRoleClient) MapCreateBulk(slice any, setFunc func(*PlatformRoleCreate, int)) *PlatformRoleCreateBulk { + rv := reflect.ValueOf(slice) + if rv.Kind() != reflect.Slice { + return &PlatformRoleCreateBulk{err: fmt.Errorf("calling to PlatformRoleClient.MapCreateBulk with wrong type %T, need slice", slice)} + } + builders := make([]*PlatformRoleCreate, rv.Len()) + for i := 0; i < rv.Len(); i++ { + builders[i] = c.Create() + setFunc(builders[i], i) + } + return &PlatformRoleCreateBulk{config: c.config, builders: builders} +} + +// Update returns an update builder for PlatformRole. +func (c *PlatformRoleClient) Update() *PlatformRoleUpdate { + mutation := newPlatformRoleMutation(c.config, OpUpdate) + return &PlatformRoleUpdate{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// UpdateOne returns an update builder for the given entity. +func (c *PlatformRoleClient) UpdateOne(_m *PlatformRole) *PlatformRoleUpdateOne { + mutation := newPlatformRoleMutation(c.config, OpUpdateOne, withPlatformRole(_m)) + return &PlatformRoleUpdateOne{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// UpdateOneID returns an update builder for the given id. +func (c *PlatformRoleClient) UpdateOneID(id uuid.UUID) *PlatformRoleUpdateOne { + mutation := newPlatformRoleMutation(c.config, OpUpdateOne, withPlatformRoleID(id)) + return &PlatformRoleUpdateOne{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// Delete returns a delete builder for PlatformRole. +func (c *PlatformRoleClient) Delete() *PlatformRoleDelete { + mutation := newPlatformRoleMutation(c.config, OpDelete) + return &PlatformRoleDelete{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// DeleteOne returns a builder for deleting the given entity. +func (c *PlatformRoleClient) DeleteOne(_m *PlatformRole) *PlatformRoleDeleteOne { + return c.DeleteOneID(_m.ID) +} + +// DeleteOneID returns a builder for deleting the given entity by its id. +func (c *PlatformRoleClient) DeleteOneID(id uuid.UUID) *PlatformRoleDeleteOne { + builder := c.Delete().Where(platformrole.ID(id)) + builder.mutation.id = &id + builder.mutation.op = OpDeleteOne + return &PlatformRoleDeleteOne{builder} +} + +// Query returns a query builder for PlatformRole. +func (c *PlatformRoleClient) Query() *PlatformRoleQuery { + return &PlatformRoleQuery{ + config: c.config, + ctx: &QueryContext{Type: TypePlatformRole}, + inters: c.Interceptors(), + } +} + +// Get returns a PlatformRole entity by its id. +func (c *PlatformRoleClient) Get(ctx context.Context, id uuid.UUID) (*PlatformRole, error) { + return c.Query().Where(platformrole.ID(id)).Only(ctx) +} + +// GetX is like Get, but panics if an error occurs. +func (c *PlatformRoleClient) GetX(ctx context.Context, id uuid.UUID) *PlatformRole { + obj, err := c.Get(ctx, id) + if err != nil { + panic(err) + } + return obj +} + +// Hooks returns the client hooks. +func (c *PlatformRoleClient) Hooks() []Hook { + return c.hooks.PlatformRole +} + +// Interceptors returns the client interceptors. +func (c *PlatformRoleClient) Interceptors() []Interceptor { + return c.inters.PlatformRole +} + +func (c *PlatformRoleClient) mutate(ctx context.Context, m *PlatformRoleMutation) (Value, error) { + switch m.Op() { + case OpCreate: + return (&PlatformRoleCreate{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx) + case OpUpdate: + return (&PlatformRoleUpdate{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx) + case OpUpdateOne: + return (&PlatformRoleUpdateOne{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx) + case OpDelete, OpDeleteOne: + return (&PlatformRoleDelete{config: c.config, hooks: c.Hooks(), mutation: m}).Exec(ctx) + default: + return nil, fmt.Errorf("ent: unknown PlatformRole mutation op: %q", m.Op()) + } +} + +// ProviderContactChannelClient is a client for the ProviderContactChannel schema. +type ProviderContactChannelClient struct { + config +} + +// NewProviderContactChannelClient returns a client for the ProviderContactChannel from the given config. +func NewProviderContactChannelClient(c config) *ProviderContactChannelClient { + return &ProviderContactChannelClient{config: c} +} + +// Use adds a list of mutation hooks to the hooks stack. +// A call to `Use(f, g, h)` equals to `providercontactchannel.Hooks(f(g(h())))`. +func (c *ProviderContactChannelClient) Use(hooks ...Hook) { + c.hooks.ProviderContactChannel = append(c.hooks.ProviderContactChannel, hooks...) +} + +// Intercept adds a list of query interceptors to the interceptors stack. +// A call to `Intercept(f, g, h)` equals to `providercontactchannel.Intercept(f(g(h())))`. +func (c *ProviderContactChannelClient) Intercept(interceptors ...Interceptor) { + c.inters.ProviderContactChannel = append(c.inters.ProviderContactChannel, interceptors...) +} + +// Create returns a builder for creating a ProviderContactChannel entity. +func (c *ProviderContactChannelClient) Create() *ProviderContactChannelCreate { + mutation := newProviderContactChannelMutation(c.config, OpCreate) + return &ProviderContactChannelCreate{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// CreateBulk returns a builder for creating a bulk of ProviderContactChannel entities. +func (c *ProviderContactChannelClient) CreateBulk(builders ...*ProviderContactChannelCreate) *ProviderContactChannelCreateBulk { + return &ProviderContactChannelCreateBulk{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 *ProviderContactChannelClient) MapCreateBulk(slice any, setFunc func(*ProviderContactChannelCreate, int)) *ProviderContactChannelCreateBulk { + rv := reflect.ValueOf(slice) + if rv.Kind() != reflect.Slice { + return &ProviderContactChannelCreateBulk{err: fmt.Errorf("calling to ProviderContactChannelClient.MapCreateBulk with wrong type %T, need slice", slice)} + } + builders := make([]*ProviderContactChannelCreate, rv.Len()) + for i := 0; i < rv.Len(); i++ { + builders[i] = c.Create() + setFunc(builders[i], i) + } + return &ProviderContactChannelCreateBulk{config: c.config, builders: builders} +} + +// Update returns an update builder for ProviderContactChannel. +func (c *ProviderContactChannelClient) Update() *ProviderContactChannelUpdate { + mutation := newProviderContactChannelMutation(c.config, OpUpdate) + return &ProviderContactChannelUpdate{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// UpdateOne returns an update builder for the given entity. +func (c *ProviderContactChannelClient) UpdateOne(_m *ProviderContactChannel) *ProviderContactChannelUpdateOne { + mutation := newProviderContactChannelMutation(c.config, OpUpdateOne, withProviderContactChannel(_m)) + return &ProviderContactChannelUpdateOne{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// UpdateOneID returns an update builder for the given id. +func (c *ProviderContactChannelClient) UpdateOneID(id uuid.UUID) *ProviderContactChannelUpdateOne { + mutation := newProviderContactChannelMutation(c.config, OpUpdateOne, withProviderContactChannelID(id)) + return &ProviderContactChannelUpdateOne{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// Delete returns a delete builder for ProviderContactChannel. +func (c *ProviderContactChannelClient) Delete() *ProviderContactChannelDelete { + mutation := newProviderContactChannelMutation(c.config, OpDelete) + return &ProviderContactChannelDelete{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// DeleteOne returns a builder for deleting the given entity. +func (c *ProviderContactChannelClient) DeleteOne(_m *ProviderContactChannel) *ProviderContactChannelDeleteOne { + return c.DeleteOneID(_m.ID) +} + +// DeleteOneID returns a builder for deleting the given entity by its id. +func (c *ProviderContactChannelClient) DeleteOneID(id uuid.UUID) *ProviderContactChannelDeleteOne { + builder := c.Delete().Where(providercontactchannel.ID(id)) + builder.mutation.id = &id + builder.mutation.op = OpDeleteOne + return &ProviderContactChannelDeleteOne{builder} +} + +// Query returns a query builder for ProviderContactChannel. +func (c *ProviderContactChannelClient) Query() *ProviderContactChannelQuery { + return &ProviderContactChannelQuery{ + config: c.config, + ctx: &QueryContext{Type: TypeProviderContactChannel}, + inters: c.Interceptors(), + } +} + +// Get returns a ProviderContactChannel entity by its id. +func (c *ProviderContactChannelClient) Get(ctx context.Context, id uuid.UUID) (*ProviderContactChannel, error) { + return c.Query().Where(providercontactchannel.ID(id)).Only(ctx) +} + +// GetX is like Get, but panics if an error occurs. +func (c *ProviderContactChannelClient) GetX(ctx context.Context, id uuid.UUID) *ProviderContactChannel { + obj, err := c.Get(ctx, id) + if err != nil { + panic(err) + } + return obj +} + +// Hooks returns the client hooks. +func (c *ProviderContactChannelClient) Hooks() []Hook { + return c.hooks.ProviderContactChannel +} + +// Interceptors returns the client interceptors. +func (c *ProviderContactChannelClient) Interceptors() []Interceptor { + return c.inters.ProviderContactChannel +} + +func (c *ProviderContactChannelClient) mutate(ctx context.Context, m *ProviderContactChannelMutation) (Value, error) { + switch m.Op() { + case OpCreate: + return (&ProviderContactChannelCreate{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx) + case OpUpdate: + return (&ProviderContactChannelUpdate{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx) + case OpUpdateOne: + return (&ProviderContactChannelUpdateOne{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx) + case OpDelete, OpDeleteOne: + return (&ProviderContactChannelDelete{config: c.config, hooks: c.Hooks(), mutation: m}).Exec(ctx) + default: + return nil, fmt.Errorf("ent: unknown ProviderContactChannel mutation op: %q", m.Op()) + } +} + +// ProviderProfileClient is a client for the ProviderProfile schema. +type ProviderProfileClient struct { + config +} + +// NewProviderProfileClient returns a client for the ProviderProfile from the given config. +func NewProviderProfileClient(c config) *ProviderProfileClient { + return &ProviderProfileClient{config: c} +} + +// Use adds a list of mutation hooks to the hooks stack. +// A call to `Use(f, g, h)` equals to `providerprofile.Hooks(f(g(h())))`. +func (c *ProviderProfileClient) Use(hooks ...Hook) { + c.hooks.ProviderProfile = append(c.hooks.ProviderProfile, hooks...) +} + +// Intercept adds a list of query interceptors to the interceptors stack. +// A call to `Intercept(f, g, h)` equals to `providerprofile.Intercept(f(g(h())))`. +func (c *ProviderProfileClient) Intercept(interceptors ...Interceptor) { + c.inters.ProviderProfile = append(c.inters.ProviderProfile, interceptors...) +} + +// Create returns a builder for creating a ProviderProfile entity. +func (c *ProviderProfileClient) Create() *ProviderProfileCreate { + mutation := newProviderProfileMutation(c.config, OpCreate) + return &ProviderProfileCreate{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// CreateBulk returns a builder for creating a bulk of ProviderProfile entities. +func (c *ProviderProfileClient) CreateBulk(builders ...*ProviderProfileCreate) *ProviderProfileCreateBulk { + return &ProviderProfileCreateBulk{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 *ProviderProfileClient) MapCreateBulk(slice any, setFunc func(*ProviderProfileCreate, int)) *ProviderProfileCreateBulk { + rv := reflect.ValueOf(slice) + if rv.Kind() != reflect.Slice { + return &ProviderProfileCreateBulk{err: fmt.Errorf("calling to ProviderProfileClient.MapCreateBulk with wrong type %T, need slice", slice)} + } + builders := make([]*ProviderProfileCreate, rv.Len()) + for i := 0; i < rv.Len(); i++ { + builders[i] = c.Create() + setFunc(builders[i], i) + } + return &ProviderProfileCreateBulk{config: c.config, builders: builders} +} + +// Update returns an update builder for ProviderProfile. +func (c *ProviderProfileClient) Update() *ProviderProfileUpdate { + mutation := newProviderProfileMutation(c.config, OpUpdate) + return &ProviderProfileUpdate{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// UpdateOne returns an update builder for the given entity. +func (c *ProviderProfileClient) UpdateOne(_m *ProviderProfile) *ProviderProfileUpdateOne { + mutation := newProviderProfileMutation(c.config, OpUpdateOne, withProviderProfile(_m)) + return &ProviderProfileUpdateOne{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// UpdateOneID returns an update builder for the given id. +func (c *ProviderProfileClient) UpdateOneID(id uuid.UUID) *ProviderProfileUpdateOne { + mutation := newProviderProfileMutation(c.config, OpUpdateOne, withProviderProfileID(id)) + return &ProviderProfileUpdateOne{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// Delete returns a delete builder for ProviderProfile. +func (c *ProviderProfileClient) Delete() *ProviderProfileDelete { + mutation := newProviderProfileMutation(c.config, OpDelete) + return &ProviderProfileDelete{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// DeleteOne returns a builder for deleting the given entity. +func (c *ProviderProfileClient) DeleteOne(_m *ProviderProfile) *ProviderProfileDeleteOne { + return c.DeleteOneID(_m.ID) +} + +// DeleteOneID returns a builder for deleting the given entity by its id. +func (c *ProviderProfileClient) DeleteOneID(id uuid.UUID) *ProviderProfileDeleteOne { + builder := c.Delete().Where(providerprofile.ID(id)) + builder.mutation.id = &id + builder.mutation.op = OpDeleteOne + return &ProviderProfileDeleteOne{builder} +} + +// Query returns a query builder for ProviderProfile. +func (c *ProviderProfileClient) Query() *ProviderProfileQuery { + return &ProviderProfileQuery{ + config: c.config, + ctx: &QueryContext{Type: TypeProviderProfile}, + inters: c.Interceptors(), + } +} + +// Get returns a ProviderProfile entity by its id. +func (c *ProviderProfileClient) Get(ctx context.Context, id uuid.UUID) (*ProviderProfile, error) { + return c.Query().Where(providerprofile.ID(id)).Only(ctx) +} + +// GetX is like Get, but panics if an error occurs. +func (c *ProviderProfileClient) GetX(ctx context.Context, id uuid.UUID) *ProviderProfile { + obj, err := c.Get(ctx, id) + if err != nil { + panic(err) + } + return obj +} + +// QueryServiceLocalities queries the service_localities edge of a ProviderProfile. +func (c *ProviderProfileClient) QueryServiceLocalities(_m *ProviderProfile) *LocalityQuery { + query := (&LocalityClient{config: c.config}).Query() + query.path = func(context.Context) (fromV *sql.Selector, _ error) { + id := _m.ID + step := sqlgraph.NewStep( + sqlgraph.From(providerprofile.Table, providerprofile.FieldID, id), + sqlgraph.To(locality.Table, locality.FieldID), + sqlgraph.Edge(sqlgraph.M2M, false, providerprofile.ServiceLocalitiesTable, providerprofile.ServiceLocalitiesPrimaryKey...), + ) + fromV = sqlgraph.Neighbors(_m.driver.Dialect(), step) + return fromV, nil + } + return query +} + +// QuerySpokenLanguages queries the spoken_languages edge of a ProviderProfile. +func (c *ProviderProfileClient) QuerySpokenLanguages(_m *ProviderProfile) *SpokenLanguageQuery { + query := (&SpokenLanguageClient{config: c.config}).Query() + query.path = func(context.Context) (fromV *sql.Selector, _ error) { + id := _m.ID + step := sqlgraph.NewStep( + sqlgraph.From(providerprofile.Table, providerprofile.FieldID, id), + sqlgraph.To(spokenlanguage.Table, spokenlanguage.FieldID), + sqlgraph.Edge(sqlgraph.M2M, false, providerprofile.SpokenLanguagesTable, providerprofile.SpokenLanguagesPrimaryKey...), + ) + fromV = sqlgraph.Neighbors(_m.driver.Dialect(), step) + return fromV, nil + } + return query +} + +// QueryServiceLocalityLinks queries the service_locality_links edge of a ProviderProfile. +func (c *ProviderProfileClient) QueryServiceLocalityLinks(_m *ProviderProfile) *ProviderServiceLocalityQuery { + query := (&ProviderServiceLocalityClient{config: c.config}).Query() + query.path = func(context.Context) (fromV *sql.Selector, _ error) { + id := _m.ID + step := sqlgraph.NewStep( + sqlgraph.From(providerprofile.Table, providerprofile.FieldID, id), + sqlgraph.To(providerservicelocality.Table, providerservicelocality.ProfileColumn), + sqlgraph.Edge(sqlgraph.O2M, true, providerprofile.ServiceLocalityLinksTable, providerprofile.ServiceLocalityLinksColumn), + ) + fromV = sqlgraph.Neighbors(_m.driver.Dialect(), step) + return fromV, nil + } + return query +} + +// QuerySpokenLanguageLinks queries the spoken_language_links edge of a ProviderProfile. +func (c *ProviderProfileClient) QuerySpokenLanguageLinks(_m *ProviderProfile) *ProviderSpokenLanguageQuery { + query := (&ProviderSpokenLanguageClient{config: c.config}).Query() + query.path = func(context.Context) (fromV *sql.Selector, _ error) { + id := _m.ID + step := sqlgraph.NewStep( + sqlgraph.From(providerprofile.Table, providerprofile.FieldID, id), + sqlgraph.To(providerspokenlanguage.Table, providerspokenlanguage.ProfileColumn), + sqlgraph.Edge(sqlgraph.O2M, true, providerprofile.SpokenLanguageLinksTable, providerprofile.SpokenLanguageLinksColumn), + ) + fromV = sqlgraph.Neighbors(_m.driver.Dialect(), step) + return fromV, nil + } + return query +} + +// Hooks returns the client hooks. +func (c *ProviderProfileClient) Hooks() []Hook { + return c.hooks.ProviderProfile +} + +// Interceptors returns the client interceptors. +func (c *ProviderProfileClient) Interceptors() []Interceptor { + return c.inters.ProviderProfile +} + +func (c *ProviderProfileClient) mutate(ctx context.Context, m *ProviderProfileMutation) (Value, error) { + switch m.Op() { + case OpCreate: + return (&ProviderProfileCreate{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx) + case OpUpdate: + return (&ProviderProfileUpdate{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx) + case OpUpdateOne: + return (&ProviderProfileUpdateOne{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx) + case OpDelete, OpDeleteOne: + return (&ProviderProfileDelete{config: c.config, hooks: c.Hooks(), mutation: m}).Exec(ctx) + default: + return nil, fmt.Errorf("ent: unknown ProviderProfile mutation op: %q", m.Op()) + } +} + +// ProviderServiceLocalityClient is a client for the ProviderServiceLocality schema. +type ProviderServiceLocalityClient struct { + config +} + +// NewProviderServiceLocalityClient returns a client for the ProviderServiceLocality from the given config. +func NewProviderServiceLocalityClient(c config) *ProviderServiceLocalityClient { + return &ProviderServiceLocalityClient{config: c} +} + +// Use adds a list of mutation hooks to the hooks stack. +// A call to `Use(f, g, h)` equals to `providerservicelocality.Hooks(f(g(h())))`. +func (c *ProviderServiceLocalityClient) Use(hooks ...Hook) { + c.hooks.ProviderServiceLocality = append(c.hooks.ProviderServiceLocality, hooks...) +} + +// Intercept adds a list of query interceptors to the interceptors stack. +// A call to `Intercept(f, g, h)` equals to `providerservicelocality.Intercept(f(g(h())))`. +func (c *ProviderServiceLocalityClient) Intercept(interceptors ...Interceptor) { + c.inters.ProviderServiceLocality = append(c.inters.ProviderServiceLocality, interceptors...) +} + +// Create returns a builder for creating a ProviderServiceLocality entity. +func (c *ProviderServiceLocalityClient) Create() *ProviderServiceLocalityCreate { + mutation := newProviderServiceLocalityMutation(c.config, OpCreate) + return &ProviderServiceLocalityCreate{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// CreateBulk returns a builder for creating a bulk of ProviderServiceLocality entities. +func (c *ProviderServiceLocalityClient) CreateBulk(builders ...*ProviderServiceLocalityCreate) *ProviderServiceLocalityCreateBulk { + return &ProviderServiceLocalityCreateBulk{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 *ProviderServiceLocalityClient) MapCreateBulk(slice any, setFunc func(*ProviderServiceLocalityCreate, int)) *ProviderServiceLocalityCreateBulk { + rv := reflect.ValueOf(slice) + if rv.Kind() != reflect.Slice { + return &ProviderServiceLocalityCreateBulk{err: fmt.Errorf("calling to ProviderServiceLocalityClient.MapCreateBulk with wrong type %T, need slice", slice)} + } + builders := make([]*ProviderServiceLocalityCreate, rv.Len()) + for i := 0; i < rv.Len(); i++ { + builders[i] = c.Create() + setFunc(builders[i], i) + } + return &ProviderServiceLocalityCreateBulk{config: c.config, builders: builders} +} + +// Update returns an update builder for ProviderServiceLocality. +func (c *ProviderServiceLocalityClient) Update() *ProviderServiceLocalityUpdate { + mutation := newProviderServiceLocalityMutation(c.config, OpUpdate) + return &ProviderServiceLocalityUpdate{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// UpdateOne returns an update builder for the given entity. +func (c *ProviderServiceLocalityClient) UpdateOne(_m *ProviderServiceLocality) *ProviderServiceLocalityUpdateOne { + mutation := newProviderServiceLocalityMutation(c.config, OpUpdateOne) + mutation.profile = &_m.InternalUserID + mutation.locality = &_m.LocalityID + return &ProviderServiceLocalityUpdateOne{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// Delete returns a delete builder for ProviderServiceLocality. +func (c *ProviderServiceLocalityClient) Delete() *ProviderServiceLocalityDelete { + mutation := newProviderServiceLocalityMutation(c.config, OpDelete) + return &ProviderServiceLocalityDelete{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// Query returns a query builder for ProviderServiceLocality. +func (c *ProviderServiceLocalityClient) Query() *ProviderServiceLocalityQuery { + return &ProviderServiceLocalityQuery{ + config: c.config, + ctx: &QueryContext{Type: TypeProviderServiceLocality}, + inters: c.Interceptors(), + } +} + +// QueryProfile queries the profile edge of a ProviderServiceLocality. +func (c *ProviderServiceLocalityClient) QueryProfile(_m *ProviderServiceLocality) *ProviderProfileQuery { + return c.Query(). + Where(providerservicelocality.InternalUserID(_m.InternalUserID), providerservicelocality.LocalityID(_m.LocalityID)). + QueryProfile() +} + +// QueryLocality queries the locality edge of a ProviderServiceLocality. +func (c *ProviderServiceLocalityClient) QueryLocality(_m *ProviderServiceLocality) *LocalityQuery { + return c.Query(). + Where(providerservicelocality.InternalUserID(_m.InternalUserID), providerservicelocality.LocalityID(_m.LocalityID)). + QueryLocality() +} + +// Hooks returns the client hooks. +func (c *ProviderServiceLocalityClient) Hooks() []Hook { + return c.hooks.ProviderServiceLocality +} + +// Interceptors returns the client interceptors. +func (c *ProviderServiceLocalityClient) Interceptors() []Interceptor { + return c.inters.ProviderServiceLocality +} + +func (c *ProviderServiceLocalityClient) mutate(ctx context.Context, m *ProviderServiceLocalityMutation) (Value, error) { + switch m.Op() { + case OpCreate: + return (&ProviderServiceLocalityCreate{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx) + case OpUpdate: + return (&ProviderServiceLocalityUpdate{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx) + case OpUpdateOne: + return (&ProviderServiceLocalityUpdateOne{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx) + case OpDelete, OpDeleteOne: + return (&ProviderServiceLocalityDelete{config: c.config, hooks: c.Hooks(), mutation: m}).Exec(ctx) + default: + return nil, fmt.Errorf("ent: unknown ProviderServiceLocality mutation op: %q", m.Op()) + } +} + +// ProviderSpokenLanguageClient is a client for the ProviderSpokenLanguage schema. +type ProviderSpokenLanguageClient struct { + config +} + +// NewProviderSpokenLanguageClient returns a client for the ProviderSpokenLanguage from the given config. +func NewProviderSpokenLanguageClient(c config) *ProviderSpokenLanguageClient { + return &ProviderSpokenLanguageClient{config: c} +} + +// Use adds a list of mutation hooks to the hooks stack. +// A call to `Use(f, g, h)` equals to `providerspokenlanguage.Hooks(f(g(h())))`. +func (c *ProviderSpokenLanguageClient) Use(hooks ...Hook) { + c.hooks.ProviderSpokenLanguage = append(c.hooks.ProviderSpokenLanguage, hooks...) +} + +// Intercept adds a list of query interceptors to the interceptors stack. +// A call to `Intercept(f, g, h)` equals to `providerspokenlanguage.Intercept(f(g(h())))`. +func (c *ProviderSpokenLanguageClient) Intercept(interceptors ...Interceptor) { + c.inters.ProviderSpokenLanguage = append(c.inters.ProviderSpokenLanguage, interceptors...) +} + +// Create returns a builder for creating a ProviderSpokenLanguage entity. +func (c *ProviderSpokenLanguageClient) Create() *ProviderSpokenLanguageCreate { + mutation := newProviderSpokenLanguageMutation(c.config, OpCreate) + return &ProviderSpokenLanguageCreate{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// CreateBulk returns a builder for creating a bulk of ProviderSpokenLanguage entities. +func (c *ProviderSpokenLanguageClient) CreateBulk(builders ...*ProviderSpokenLanguageCreate) *ProviderSpokenLanguageCreateBulk { + return &ProviderSpokenLanguageCreateBulk{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 *ProviderSpokenLanguageClient) MapCreateBulk(slice any, setFunc func(*ProviderSpokenLanguageCreate, int)) *ProviderSpokenLanguageCreateBulk { + rv := reflect.ValueOf(slice) + if rv.Kind() != reflect.Slice { + return &ProviderSpokenLanguageCreateBulk{err: fmt.Errorf("calling to ProviderSpokenLanguageClient.MapCreateBulk with wrong type %T, need slice", slice)} + } + builders := make([]*ProviderSpokenLanguageCreate, rv.Len()) + for i := 0; i < rv.Len(); i++ { + builders[i] = c.Create() + setFunc(builders[i], i) + } + return &ProviderSpokenLanguageCreateBulk{config: c.config, builders: builders} +} + +// Update returns an update builder for ProviderSpokenLanguage. +func (c *ProviderSpokenLanguageClient) Update() *ProviderSpokenLanguageUpdate { + mutation := newProviderSpokenLanguageMutation(c.config, OpUpdate) + return &ProviderSpokenLanguageUpdate{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// UpdateOne returns an update builder for the given entity. +func (c *ProviderSpokenLanguageClient) UpdateOne(_m *ProviderSpokenLanguage) *ProviderSpokenLanguageUpdateOne { + mutation := newProviderSpokenLanguageMutation(c.config, OpUpdateOne) + mutation.profile = &_m.InternalUserID + mutation.language = &_m.LanguageCode + return &ProviderSpokenLanguageUpdateOne{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// Delete returns a delete builder for ProviderSpokenLanguage. +func (c *ProviderSpokenLanguageClient) Delete() *ProviderSpokenLanguageDelete { + mutation := newProviderSpokenLanguageMutation(c.config, OpDelete) + return &ProviderSpokenLanguageDelete{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// Query returns a query builder for ProviderSpokenLanguage. +func (c *ProviderSpokenLanguageClient) Query() *ProviderSpokenLanguageQuery { + return &ProviderSpokenLanguageQuery{ + config: c.config, + ctx: &QueryContext{Type: TypeProviderSpokenLanguage}, + inters: c.Interceptors(), + } +} + +// QueryProfile queries the profile edge of a ProviderSpokenLanguage. +func (c *ProviderSpokenLanguageClient) QueryProfile(_m *ProviderSpokenLanguage) *ProviderProfileQuery { + return c.Query(). + Where(providerspokenlanguage.InternalUserID(_m.InternalUserID), providerspokenlanguage.LanguageCode(_m.LanguageCode)). + QueryProfile() +} + +// QueryLanguage queries the language edge of a ProviderSpokenLanguage. +func (c *ProviderSpokenLanguageClient) QueryLanguage(_m *ProviderSpokenLanguage) *SpokenLanguageQuery { + return c.Query(). + Where(providerspokenlanguage.InternalUserID(_m.InternalUserID), providerspokenlanguage.LanguageCode(_m.LanguageCode)). + QueryLanguage() +} + +// Hooks returns the client hooks. +func (c *ProviderSpokenLanguageClient) Hooks() []Hook { + return c.hooks.ProviderSpokenLanguage +} + +// Interceptors returns the client interceptors. +func (c *ProviderSpokenLanguageClient) Interceptors() []Interceptor { + return c.inters.ProviderSpokenLanguage +} + +func (c *ProviderSpokenLanguageClient) mutate(ctx context.Context, m *ProviderSpokenLanguageMutation) (Value, error) { + switch m.Op() { + case OpCreate: + return (&ProviderSpokenLanguageCreate{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx) + case OpUpdate: + return (&ProviderSpokenLanguageUpdate{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx) + case OpUpdateOne: + return (&ProviderSpokenLanguageUpdateOne{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx) + case OpDelete, OpDeleteOne: + return (&ProviderSpokenLanguageDelete{config: c.config, hooks: c.Hooks(), mutation: m}).Exec(ctx) + default: + return nil, fmt.Errorf("ent: unknown ProviderSpokenLanguage mutation op: %q", m.Op()) + } +} + +// ServiceCategoryClient is a client for the ServiceCategory schema. +type ServiceCategoryClient struct { + config +} + +// NewServiceCategoryClient returns a client for the ServiceCategory from the given config. +func NewServiceCategoryClient(c config) *ServiceCategoryClient { + return &ServiceCategoryClient{config: c} +} + +// Use adds a list of mutation hooks to the hooks stack. +// A call to `Use(f, g, h)` equals to `servicecategory.Hooks(f(g(h())))`. +func (c *ServiceCategoryClient) Use(hooks ...Hook) { + c.hooks.ServiceCategory = append(c.hooks.ServiceCategory, hooks...) +} + +// Intercept adds a list of query interceptors to the interceptors stack. +// A call to `Intercept(f, g, h)` equals to `servicecategory.Intercept(f(g(h())))`. +func (c *ServiceCategoryClient) Intercept(interceptors ...Interceptor) { + c.inters.ServiceCategory = append(c.inters.ServiceCategory, interceptors...) +} + +// Create returns a builder for creating a ServiceCategory entity. +func (c *ServiceCategoryClient) Create() *ServiceCategoryCreate { + mutation := newServiceCategoryMutation(c.config, OpCreate) + return &ServiceCategoryCreate{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// CreateBulk returns a builder for creating a bulk of ServiceCategory entities. +func (c *ServiceCategoryClient) CreateBulk(builders ...*ServiceCategoryCreate) *ServiceCategoryCreateBulk { + return &ServiceCategoryCreateBulk{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 *ServiceCategoryClient) MapCreateBulk(slice any, setFunc func(*ServiceCategoryCreate, int)) *ServiceCategoryCreateBulk { + rv := reflect.ValueOf(slice) + if rv.Kind() != reflect.Slice { + return &ServiceCategoryCreateBulk{err: fmt.Errorf("calling to ServiceCategoryClient.MapCreateBulk with wrong type %T, need slice", slice)} + } + builders := make([]*ServiceCategoryCreate, rv.Len()) + for i := 0; i < rv.Len(); i++ { + builders[i] = c.Create() + setFunc(builders[i], i) + } + return &ServiceCategoryCreateBulk{config: c.config, builders: builders} +} + +// Update returns an update builder for ServiceCategory. +func (c *ServiceCategoryClient) Update() *ServiceCategoryUpdate { + mutation := newServiceCategoryMutation(c.config, OpUpdate) + return &ServiceCategoryUpdate{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// UpdateOne returns an update builder for the given entity. +func (c *ServiceCategoryClient) UpdateOne(_m *ServiceCategory) *ServiceCategoryUpdateOne { + mutation := newServiceCategoryMutation(c.config, OpUpdateOne, withServiceCategory(_m)) + return &ServiceCategoryUpdateOne{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// UpdateOneID returns an update builder for the given id. +func (c *ServiceCategoryClient) UpdateOneID(id uuid.UUID) *ServiceCategoryUpdateOne { + mutation := newServiceCategoryMutation(c.config, OpUpdateOne, withServiceCategoryID(id)) + return &ServiceCategoryUpdateOne{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// Delete returns a delete builder for ServiceCategory. +func (c *ServiceCategoryClient) Delete() *ServiceCategoryDelete { + mutation := newServiceCategoryMutation(c.config, OpDelete) + return &ServiceCategoryDelete{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// DeleteOne returns a builder for deleting the given entity. +func (c *ServiceCategoryClient) DeleteOne(_m *ServiceCategory) *ServiceCategoryDeleteOne { + return c.DeleteOneID(_m.ID) +} + +// DeleteOneID returns a builder for deleting the given entity by its id. +func (c *ServiceCategoryClient) DeleteOneID(id uuid.UUID) *ServiceCategoryDeleteOne { + builder := c.Delete().Where(servicecategory.ID(id)) + builder.mutation.id = &id + builder.mutation.op = OpDeleteOne + return &ServiceCategoryDeleteOne{builder} +} + +// Query returns a query builder for ServiceCategory. +func (c *ServiceCategoryClient) Query() *ServiceCategoryQuery { + return &ServiceCategoryQuery{ + config: c.config, + ctx: &QueryContext{Type: TypeServiceCategory}, + inters: c.Interceptors(), + } +} + +// Get returns a ServiceCategory entity by its id. +func (c *ServiceCategoryClient) Get(ctx context.Context, id uuid.UUID) (*ServiceCategory, error) { + return c.Query().Where(servicecategory.ID(id)).Only(ctx) +} + +// GetX is like Get, but panics if an error occurs. +func (c *ServiceCategoryClient) GetX(ctx context.Context, id uuid.UUID) *ServiceCategory { + obj, err := c.Get(ctx, id) + if err != nil { + panic(err) + } + return obj +} + +// QueryChildren queries the children edge of a ServiceCategory. +func (c *ServiceCategoryClient) QueryChildren(_m *ServiceCategory) *ServiceCategoryQuery { + query := (&ServiceCategoryClient{config: c.config}).Query() + query.path = func(context.Context) (fromV *sql.Selector, _ error) { + id := _m.ID + step := sqlgraph.NewStep( + sqlgraph.From(servicecategory.Table, servicecategory.FieldID, id), + sqlgraph.To(servicecategory.Table, servicecategory.FieldID), + sqlgraph.Edge(sqlgraph.O2M, false, servicecategory.ChildrenTable, servicecategory.ChildrenColumn), + ) + fromV = sqlgraph.Neighbors(_m.driver.Dialect(), step) + return fromV, nil + } + return query +} + +// QueryParent queries the parent edge of a ServiceCategory. +func (c *ServiceCategoryClient) QueryParent(_m *ServiceCategory) *ServiceCategoryQuery { + query := (&ServiceCategoryClient{config: c.config}).Query() + query.path = func(context.Context) (fromV *sql.Selector, _ error) { + id := _m.ID + step := sqlgraph.NewStep( + sqlgraph.From(servicecategory.Table, servicecategory.FieldID, id), + sqlgraph.To(servicecategory.Table, servicecategory.FieldID), + sqlgraph.Edge(sqlgraph.M2O, true, servicecategory.ParentTable, servicecategory.ParentColumn), + ) + fromV = sqlgraph.Neighbors(_m.driver.Dialect(), step) + return fromV, nil + } + return query +} + +// QueryLocalizedIn queries the localized_in edge of a ServiceCategory. +func (c *ServiceCategoryClient) QueryLocalizedIn(_m *ServiceCategory) *SupportedLocaleQuery { + query := (&SupportedLocaleClient{config: c.config}).Query() + query.path = func(context.Context) (fromV *sql.Selector, _ error) { + id := _m.ID + step := sqlgraph.NewStep( + sqlgraph.From(servicecategory.Table, servicecategory.FieldID, id), + sqlgraph.To(supportedlocale.Table, supportedlocale.FieldID), + sqlgraph.Edge(sqlgraph.M2M, false, servicecategory.LocalizedInTable, servicecategory.LocalizedInPrimaryKey...), + ) + fromV = sqlgraph.Neighbors(_m.driver.Dialect(), step) + return fromV, nil + } + return query +} + +// QueryTranslations queries the translations edge of a ServiceCategory. +func (c *ServiceCategoryClient) QueryTranslations(_m *ServiceCategory) *ServiceCategoryTranslationQuery { + query := (&ServiceCategoryTranslationClient{config: c.config}).Query() + query.path = func(context.Context) (fromV *sql.Selector, _ error) { + id := _m.ID + step := sqlgraph.NewStep( + sqlgraph.From(servicecategory.Table, servicecategory.FieldID, id), + sqlgraph.To(servicecategorytranslation.Table, servicecategorytranslation.CategoryColumn), + sqlgraph.Edge(sqlgraph.O2M, true, servicecategory.TranslationsTable, servicecategory.TranslationsColumn), + ) + fromV = sqlgraph.Neighbors(_m.driver.Dialect(), step) + return fromV, nil + } + return query +} + +// Hooks returns the client hooks. +func (c *ServiceCategoryClient) Hooks() []Hook { + return c.hooks.ServiceCategory +} + +// Interceptors returns the client interceptors. +func (c *ServiceCategoryClient) Interceptors() []Interceptor { + return c.inters.ServiceCategory +} + +func (c *ServiceCategoryClient) mutate(ctx context.Context, m *ServiceCategoryMutation) (Value, error) { + switch m.Op() { + case OpCreate: + return (&ServiceCategoryCreate{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx) + case OpUpdate: + return (&ServiceCategoryUpdate{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx) + case OpUpdateOne: + return (&ServiceCategoryUpdateOne{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx) + case OpDelete, OpDeleteOne: + return (&ServiceCategoryDelete{config: c.config, hooks: c.Hooks(), mutation: m}).Exec(ctx) + default: + return nil, fmt.Errorf("ent: unknown ServiceCategory mutation op: %q", m.Op()) + } +} + +// ServiceCategoryTranslationClient is a client for the ServiceCategoryTranslation schema. +type ServiceCategoryTranslationClient struct { + config +} + +// NewServiceCategoryTranslationClient returns a client for the ServiceCategoryTranslation from the given config. +func NewServiceCategoryTranslationClient(c config) *ServiceCategoryTranslationClient { + return &ServiceCategoryTranslationClient{config: c} +} + +// Use adds a list of mutation hooks to the hooks stack. +// A call to `Use(f, g, h)` equals to `servicecategorytranslation.Hooks(f(g(h())))`. +func (c *ServiceCategoryTranslationClient) Use(hooks ...Hook) { + c.hooks.ServiceCategoryTranslation = append(c.hooks.ServiceCategoryTranslation, hooks...) +} + +// Intercept adds a list of query interceptors to the interceptors stack. +// A call to `Intercept(f, g, h)` equals to `servicecategorytranslation.Intercept(f(g(h())))`. +func (c *ServiceCategoryTranslationClient) Intercept(interceptors ...Interceptor) { + c.inters.ServiceCategoryTranslation = append(c.inters.ServiceCategoryTranslation, interceptors...) +} + +// Create returns a builder for creating a ServiceCategoryTranslation entity. +func (c *ServiceCategoryTranslationClient) Create() *ServiceCategoryTranslationCreate { + mutation := newServiceCategoryTranslationMutation(c.config, OpCreate) + return &ServiceCategoryTranslationCreate{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// CreateBulk returns a builder for creating a bulk of ServiceCategoryTranslation entities. +func (c *ServiceCategoryTranslationClient) CreateBulk(builders ...*ServiceCategoryTranslationCreate) *ServiceCategoryTranslationCreateBulk { + return &ServiceCategoryTranslationCreateBulk{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 *ServiceCategoryTranslationClient) MapCreateBulk(slice any, setFunc func(*ServiceCategoryTranslationCreate, int)) *ServiceCategoryTranslationCreateBulk { + rv := reflect.ValueOf(slice) + if rv.Kind() != reflect.Slice { + return &ServiceCategoryTranslationCreateBulk{err: fmt.Errorf("calling to ServiceCategoryTranslationClient.MapCreateBulk with wrong type %T, need slice", slice)} + } + builders := make([]*ServiceCategoryTranslationCreate, rv.Len()) + for i := 0; i < rv.Len(); i++ { + builders[i] = c.Create() + setFunc(builders[i], i) + } + return &ServiceCategoryTranslationCreateBulk{config: c.config, builders: builders} +} + +// Update returns an update builder for ServiceCategoryTranslation. +func (c *ServiceCategoryTranslationClient) Update() *ServiceCategoryTranslationUpdate { + mutation := newServiceCategoryTranslationMutation(c.config, OpUpdate) + return &ServiceCategoryTranslationUpdate{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// UpdateOne returns an update builder for the given entity. +func (c *ServiceCategoryTranslationClient) UpdateOne(_m *ServiceCategoryTranslation) *ServiceCategoryTranslationUpdateOne { + mutation := newServiceCategoryTranslationMutation(c.config, OpUpdateOne) + mutation.category = &_m.CategoryID + mutation.locale_record = &_m.Locale + return &ServiceCategoryTranslationUpdateOne{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// Delete returns a delete builder for ServiceCategoryTranslation. +func (c *ServiceCategoryTranslationClient) Delete() *ServiceCategoryTranslationDelete { + mutation := newServiceCategoryTranslationMutation(c.config, OpDelete) + return &ServiceCategoryTranslationDelete{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// Query returns a query builder for ServiceCategoryTranslation. +func (c *ServiceCategoryTranslationClient) Query() *ServiceCategoryTranslationQuery { + return &ServiceCategoryTranslationQuery{ + config: c.config, + ctx: &QueryContext{Type: TypeServiceCategoryTranslation}, + inters: c.Interceptors(), + } +} + +// QueryCategory queries the category edge of a ServiceCategoryTranslation. +func (c *ServiceCategoryTranslationClient) QueryCategory(_m *ServiceCategoryTranslation) *ServiceCategoryQuery { + return c.Query(). + Where(servicecategorytranslation.CategoryID(_m.CategoryID), servicecategorytranslation.Locale(_m.Locale)). + QueryCategory() +} + +// QueryLocaleRecord queries the locale_record edge of a ServiceCategoryTranslation. +func (c *ServiceCategoryTranslationClient) QueryLocaleRecord(_m *ServiceCategoryTranslation) *SupportedLocaleQuery { + return c.Query(). + Where(servicecategorytranslation.CategoryID(_m.CategoryID), servicecategorytranslation.Locale(_m.Locale)). + QueryLocaleRecord() +} + +// Hooks returns the client hooks. +func (c *ServiceCategoryTranslationClient) Hooks() []Hook { + return c.hooks.ServiceCategoryTranslation +} + +// Interceptors returns the client interceptors. +func (c *ServiceCategoryTranslationClient) Interceptors() []Interceptor { + return c.inters.ServiceCategoryTranslation +} + +func (c *ServiceCategoryTranslationClient) mutate(ctx context.Context, m *ServiceCategoryTranslationMutation) (Value, error) { + switch m.Op() { + case OpCreate: + return (&ServiceCategoryTranslationCreate{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx) + case OpUpdate: + return (&ServiceCategoryTranslationUpdate{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx) + case OpUpdateOne: + return (&ServiceCategoryTranslationUpdateOne{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx) + case OpDelete, OpDeleteOne: + return (&ServiceCategoryTranslationDelete{config: c.config, hooks: c.Hooks(), mutation: m}).Exec(ctx) + default: + return nil, fmt.Errorf("ent: unknown ServiceCategoryTranslation mutation op: %q", m.Op()) + } +} + +// SpokenLanguageClient is a client for the SpokenLanguage schema. +type SpokenLanguageClient struct { + config +} + +// NewSpokenLanguageClient returns a client for the SpokenLanguage from the given config. +func NewSpokenLanguageClient(c config) *SpokenLanguageClient { + return &SpokenLanguageClient{config: c} +} + +// Use adds a list of mutation hooks to the hooks stack. +// A call to `Use(f, g, h)` equals to `spokenlanguage.Hooks(f(g(h())))`. +func (c *SpokenLanguageClient) Use(hooks ...Hook) { + c.hooks.SpokenLanguage = append(c.hooks.SpokenLanguage, hooks...) +} + +// Intercept adds a list of query interceptors to the interceptors stack. +// A call to `Intercept(f, g, h)` equals to `spokenlanguage.Intercept(f(g(h())))`. +func (c *SpokenLanguageClient) Intercept(interceptors ...Interceptor) { + c.inters.SpokenLanguage = append(c.inters.SpokenLanguage, interceptors...) +} + +// Create returns a builder for creating a SpokenLanguage entity. +func (c *SpokenLanguageClient) Create() *SpokenLanguageCreate { + mutation := newSpokenLanguageMutation(c.config, OpCreate) + return &SpokenLanguageCreate{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// CreateBulk returns a builder for creating a bulk of SpokenLanguage entities. +func (c *SpokenLanguageClient) CreateBulk(builders ...*SpokenLanguageCreate) *SpokenLanguageCreateBulk { + return &SpokenLanguageCreateBulk{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 *SpokenLanguageClient) MapCreateBulk(slice any, setFunc func(*SpokenLanguageCreate, int)) *SpokenLanguageCreateBulk { + rv := reflect.ValueOf(slice) + if rv.Kind() != reflect.Slice { + return &SpokenLanguageCreateBulk{err: fmt.Errorf("calling to SpokenLanguageClient.MapCreateBulk with wrong type %T, need slice", slice)} + } + builders := make([]*SpokenLanguageCreate, rv.Len()) + for i := 0; i < rv.Len(); i++ { + builders[i] = c.Create() + setFunc(builders[i], i) + } + return &SpokenLanguageCreateBulk{config: c.config, builders: builders} +} + +// Update returns an update builder for SpokenLanguage. +func (c *SpokenLanguageClient) Update() *SpokenLanguageUpdate { + mutation := newSpokenLanguageMutation(c.config, OpUpdate) + return &SpokenLanguageUpdate{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// UpdateOne returns an update builder for the given entity. +func (c *SpokenLanguageClient) UpdateOne(_m *SpokenLanguage) *SpokenLanguageUpdateOne { + mutation := newSpokenLanguageMutation(c.config, OpUpdateOne, withSpokenLanguage(_m)) + return &SpokenLanguageUpdateOne{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// UpdateOneID returns an update builder for the given id. +func (c *SpokenLanguageClient) UpdateOneID(id string) *SpokenLanguageUpdateOne { + mutation := newSpokenLanguageMutation(c.config, OpUpdateOne, withSpokenLanguageID(id)) + return &SpokenLanguageUpdateOne{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// Delete returns a delete builder for SpokenLanguage. +func (c *SpokenLanguageClient) Delete() *SpokenLanguageDelete { + mutation := newSpokenLanguageMutation(c.config, OpDelete) + return &SpokenLanguageDelete{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// DeleteOne returns a builder for deleting the given entity. +func (c *SpokenLanguageClient) DeleteOne(_m *SpokenLanguage) *SpokenLanguageDeleteOne { + return c.DeleteOneID(_m.ID) +} + +// DeleteOneID returns a builder for deleting the given entity by its id. +func (c *SpokenLanguageClient) DeleteOneID(id string) *SpokenLanguageDeleteOne { + builder := c.Delete().Where(spokenlanguage.ID(id)) + builder.mutation.id = &id + builder.mutation.op = OpDeleteOne + return &SpokenLanguageDeleteOne{builder} +} + +// Query returns a query builder for SpokenLanguage. +func (c *SpokenLanguageClient) Query() *SpokenLanguageQuery { + return &SpokenLanguageQuery{ + config: c.config, + ctx: &QueryContext{Type: TypeSpokenLanguage}, + inters: c.Interceptors(), + } +} + +// Get returns a SpokenLanguage entity by its id. +func (c *SpokenLanguageClient) Get(ctx context.Context, id string) (*SpokenLanguage, error) { + return c.Query().Where(spokenlanguage.ID(id)).Only(ctx) +} + +// GetX is like Get, but panics if an error occurs. +func (c *SpokenLanguageClient) GetX(ctx context.Context, id string) *SpokenLanguage { + obj, err := c.Get(ctx, id) + if err != nil { + panic(err) + } + return obj +} + +// QueryLocalizedIn queries the localized_in edge of a SpokenLanguage. +func (c *SpokenLanguageClient) QueryLocalizedIn(_m *SpokenLanguage) *SupportedLocaleQuery { + query := (&SupportedLocaleClient{config: c.config}).Query() + query.path = func(context.Context) (fromV *sql.Selector, _ error) { + id := _m.ID + step := sqlgraph.NewStep( + sqlgraph.From(spokenlanguage.Table, spokenlanguage.FieldID, id), + sqlgraph.To(supportedlocale.Table, supportedlocale.FieldID), + sqlgraph.Edge(sqlgraph.M2M, false, spokenlanguage.LocalizedInTable, spokenlanguage.LocalizedInPrimaryKey...), + ) + fromV = sqlgraph.Neighbors(_m.driver.Dialect(), step) + return fromV, nil + } + return query +} + +// QueryProviderProfiles queries the provider_profiles edge of a SpokenLanguage. +func (c *SpokenLanguageClient) QueryProviderProfiles(_m *SpokenLanguage) *ProviderProfileQuery { + query := (&ProviderProfileClient{config: c.config}).Query() + query.path = func(context.Context) (fromV *sql.Selector, _ error) { + id := _m.ID + step := sqlgraph.NewStep( + sqlgraph.From(spokenlanguage.Table, spokenlanguage.FieldID, id), + sqlgraph.To(providerprofile.Table, providerprofile.FieldID), + sqlgraph.Edge(sqlgraph.M2M, true, spokenlanguage.ProviderProfilesTable, spokenlanguage.ProviderProfilesPrimaryKey...), + ) + fromV = sqlgraph.Neighbors(_m.driver.Dialect(), step) + return fromV, nil + } + return query +} + +// QueryTranslations queries the translations edge of a SpokenLanguage. +func (c *SpokenLanguageClient) QueryTranslations(_m *SpokenLanguage) *SpokenLanguageTranslationQuery { + query := (&SpokenLanguageTranslationClient{config: c.config}).Query() + query.path = func(context.Context) (fromV *sql.Selector, _ error) { + id := _m.ID + step := sqlgraph.NewStep( + sqlgraph.From(spokenlanguage.Table, spokenlanguage.FieldID, id), + sqlgraph.To(spokenlanguagetranslation.Table, spokenlanguagetranslation.LanguageColumn), + sqlgraph.Edge(sqlgraph.O2M, true, spokenlanguage.TranslationsTable, spokenlanguage.TranslationsColumn), + ) + fromV = sqlgraph.Neighbors(_m.driver.Dialect(), step) + return fromV, nil + } + return query +} + +// Hooks returns the client hooks. +func (c *SpokenLanguageClient) Hooks() []Hook { + return c.hooks.SpokenLanguage +} + +// Interceptors returns the client interceptors. +func (c *SpokenLanguageClient) Interceptors() []Interceptor { + return c.inters.SpokenLanguage +} + +func (c *SpokenLanguageClient) mutate(ctx context.Context, m *SpokenLanguageMutation) (Value, error) { + switch m.Op() { + case OpCreate: + return (&SpokenLanguageCreate{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx) + case OpUpdate: + return (&SpokenLanguageUpdate{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx) + case OpUpdateOne: + return (&SpokenLanguageUpdateOne{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx) + case OpDelete, OpDeleteOne: + return (&SpokenLanguageDelete{config: c.config, hooks: c.Hooks(), mutation: m}).Exec(ctx) + default: + return nil, fmt.Errorf("ent: unknown SpokenLanguage mutation op: %q", m.Op()) + } +} + +// SpokenLanguageTranslationClient is a client for the SpokenLanguageTranslation schema. +type SpokenLanguageTranslationClient struct { + config +} + +// NewSpokenLanguageTranslationClient returns a client for the SpokenLanguageTranslation from the given config. +func NewSpokenLanguageTranslationClient(c config) *SpokenLanguageTranslationClient { + return &SpokenLanguageTranslationClient{config: c} +} + +// Use adds a list of mutation hooks to the hooks stack. +// A call to `Use(f, g, h)` equals to `spokenlanguagetranslation.Hooks(f(g(h())))`. +func (c *SpokenLanguageTranslationClient) Use(hooks ...Hook) { + c.hooks.SpokenLanguageTranslation = append(c.hooks.SpokenLanguageTranslation, hooks...) +} + +// Intercept adds a list of query interceptors to the interceptors stack. +// A call to `Intercept(f, g, h)` equals to `spokenlanguagetranslation.Intercept(f(g(h())))`. +func (c *SpokenLanguageTranslationClient) Intercept(interceptors ...Interceptor) { + c.inters.SpokenLanguageTranslation = append(c.inters.SpokenLanguageTranslation, interceptors...) +} + +// Create returns a builder for creating a SpokenLanguageTranslation entity. +func (c *SpokenLanguageTranslationClient) Create() *SpokenLanguageTranslationCreate { + mutation := newSpokenLanguageTranslationMutation(c.config, OpCreate) + return &SpokenLanguageTranslationCreate{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// CreateBulk returns a builder for creating a bulk of SpokenLanguageTranslation entities. +func (c *SpokenLanguageTranslationClient) CreateBulk(builders ...*SpokenLanguageTranslationCreate) *SpokenLanguageTranslationCreateBulk { + return &SpokenLanguageTranslationCreateBulk{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 *SpokenLanguageTranslationClient) MapCreateBulk(slice any, setFunc func(*SpokenLanguageTranslationCreate, int)) *SpokenLanguageTranslationCreateBulk { + rv := reflect.ValueOf(slice) + if rv.Kind() != reflect.Slice { + return &SpokenLanguageTranslationCreateBulk{err: fmt.Errorf("calling to SpokenLanguageTranslationClient.MapCreateBulk with wrong type %T, need slice", slice)} + } + builders := make([]*SpokenLanguageTranslationCreate, rv.Len()) + for i := 0; i < rv.Len(); i++ { + builders[i] = c.Create() + setFunc(builders[i], i) + } + return &SpokenLanguageTranslationCreateBulk{config: c.config, builders: builders} +} + +// Update returns an update builder for SpokenLanguageTranslation. +func (c *SpokenLanguageTranslationClient) Update() *SpokenLanguageTranslationUpdate { + mutation := newSpokenLanguageTranslationMutation(c.config, OpUpdate) + return &SpokenLanguageTranslationUpdate{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// UpdateOne returns an update builder for the given entity. +func (c *SpokenLanguageTranslationClient) UpdateOne(_m *SpokenLanguageTranslation) *SpokenLanguageTranslationUpdateOne { + mutation := newSpokenLanguageTranslationMutation(c.config, OpUpdateOne) + mutation.language = &_m.LanguageCode + mutation.locale_record = &_m.Locale + return &SpokenLanguageTranslationUpdateOne{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// Delete returns a delete builder for SpokenLanguageTranslation. +func (c *SpokenLanguageTranslationClient) Delete() *SpokenLanguageTranslationDelete { + mutation := newSpokenLanguageTranslationMutation(c.config, OpDelete) + return &SpokenLanguageTranslationDelete{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// Query returns a query builder for SpokenLanguageTranslation. +func (c *SpokenLanguageTranslationClient) Query() *SpokenLanguageTranslationQuery { + return &SpokenLanguageTranslationQuery{ + config: c.config, + ctx: &QueryContext{Type: TypeSpokenLanguageTranslation}, + inters: c.Interceptors(), + } +} + +// QueryLanguage queries the language edge of a SpokenLanguageTranslation. +func (c *SpokenLanguageTranslationClient) QueryLanguage(_m *SpokenLanguageTranslation) *SpokenLanguageQuery { + return c.Query(). + Where(spokenlanguagetranslation.LanguageCode(_m.LanguageCode), spokenlanguagetranslation.Locale(_m.Locale)). + QueryLanguage() +} + +// QueryLocaleRecord queries the locale_record edge of a SpokenLanguageTranslation. +func (c *SpokenLanguageTranslationClient) QueryLocaleRecord(_m *SpokenLanguageTranslation) *SupportedLocaleQuery { + return c.Query(). + Where(spokenlanguagetranslation.LanguageCode(_m.LanguageCode), spokenlanguagetranslation.Locale(_m.Locale)). + QueryLocaleRecord() +} + +// Hooks returns the client hooks. +func (c *SpokenLanguageTranslationClient) Hooks() []Hook { + return c.hooks.SpokenLanguageTranslation +} + +// Interceptors returns the client interceptors. +func (c *SpokenLanguageTranslationClient) Interceptors() []Interceptor { + return c.inters.SpokenLanguageTranslation +} + +func (c *SpokenLanguageTranslationClient) mutate(ctx context.Context, m *SpokenLanguageTranslationMutation) (Value, error) { + switch m.Op() { + case OpCreate: + return (&SpokenLanguageTranslationCreate{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx) + case OpUpdate: + return (&SpokenLanguageTranslationUpdate{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx) + case OpUpdateOne: + return (&SpokenLanguageTranslationUpdateOne{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx) + case OpDelete, OpDeleteOne: + return (&SpokenLanguageTranslationDelete{config: c.config, hooks: c.Hooks(), mutation: m}).Exec(ctx) + default: + return nil, fmt.Errorf("ent: unknown SpokenLanguageTranslation mutation op: %q", m.Op()) + } +} + +// SupportedLocaleClient is a client for the SupportedLocale schema. +type SupportedLocaleClient struct { + config +} + +// NewSupportedLocaleClient returns a client for the SupportedLocale from the given config. +func NewSupportedLocaleClient(c config) *SupportedLocaleClient { + return &SupportedLocaleClient{config: c} +} + +// Use adds a list of mutation hooks to the hooks stack. +// A call to `Use(f, g, h)` equals to `supportedlocale.Hooks(f(g(h())))`. +func (c *SupportedLocaleClient) Use(hooks ...Hook) { + c.hooks.SupportedLocale = append(c.hooks.SupportedLocale, hooks...) +} + +// Intercept adds a list of query interceptors to the interceptors stack. +// A call to `Intercept(f, g, h)` equals to `supportedlocale.Intercept(f(g(h())))`. +func (c *SupportedLocaleClient) Intercept(interceptors ...Interceptor) { + c.inters.SupportedLocale = append(c.inters.SupportedLocale, interceptors...) +} + +// Create returns a builder for creating a SupportedLocale entity. +func (c *SupportedLocaleClient) Create() *SupportedLocaleCreate { + mutation := newSupportedLocaleMutation(c.config, OpCreate) + return &SupportedLocaleCreate{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// CreateBulk returns a builder for creating a bulk of SupportedLocale entities. +func (c *SupportedLocaleClient) CreateBulk(builders ...*SupportedLocaleCreate) *SupportedLocaleCreateBulk { + return &SupportedLocaleCreateBulk{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 *SupportedLocaleClient) MapCreateBulk(slice any, setFunc func(*SupportedLocaleCreate, int)) *SupportedLocaleCreateBulk { + rv := reflect.ValueOf(slice) + if rv.Kind() != reflect.Slice { + return &SupportedLocaleCreateBulk{err: fmt.Errorf("calling to SupportedLocaleClient.MapCreateBulk with wrong type %T, need slice", slice)} + } + builders := make([]*SupportedLocaleCreate, rv.Len()) + for i := 0; i < rv.Len(); i++ { + builders[i] = c.Create() + setFunc(builders[i], i) + } + return &SupportedLocaleCreateBulk{config: c.config, builders: builders} +} + +// Update returns an update builder for SupportedLocale. +func (c *SupportedLocaleClient) Update() *SupportedLocaleUpdate { + mutation := newSupportedLocaleMutation(c.config, OpUpdate) + return &SupportedLocaleUpdate{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// UpdateOne returns an update builder for the given entity. +func (c *SupportedLocaleClient) UpdateOne(_m *SupportedLocale) *SupportedLocaleUpdateOne { + mutation := newSupportedLocaleMutation(c.config, OpUpdateOne, withSupportedLocale(_m)) + return &SupportedLocaleUpdateOne{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// UpdateOneID returns an update builder for the given id. +func (c *SupportedLocaleClient) UpdateOneID(id string) *SupportedLocaleUpdateOne { + mutation := newSupportedLocaleMutation(c.config, OpUpdateOne, withSupportedLocaleID(id)) + return &SupportedLocaleUpdateOne{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// Delete returns a delete builder for SupportedLocale. +func (c *SupportedLocaleClient) Delete() *SupportedLocaleDelete { + mutation := newSupportedLocaleMutation(c.config, OpDelete) + return &SupportedLocaleDelete{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// DeleteOne returns a builder for deleting the given entity. +func (c *SupportedLocaleClient) DeleteOne(_m *SupportedLocale) *SupportedLocaleDeleteOne { + return c.DeleteOneID(_m.ID) +} + +// DeleteOneID returns a builder for deleting the given entity by its id. +func (c *SupportedLocaleClient) DeleteOneID(id string) *SupportedLocaleDeleteOne { + builder := c.Delete().Where(supportedlocale.ID(id)) + builder.mutation.id = &id + builder.mutation.op = OpDeleteOne + return &SupportedLocaleDeleteOne{builder} +} + +// Query returns a query builder for SupportedLocale. +func (c *SupportedLocaleClient) Query() *SupportedLocaleQuery { + return &SupportedLocaleQuery{ + config: c.config, + ctx: &QueryContext{Type: TypeSupportedLocale}, + inters: c.Interceptors(), + } +} + +// Get returns a SupportedLocale entity by its id. +func (c *SupportedLocaleClient) Get(ctx context.Context, id string) (*SupportedLocale, error) { + return c.Query().Where(supportedlocale.ID(id)).Only(ctx) +} + +// GetX is like Get, but panics if an error occurs. +func (c *SupportedLocaleClient) GetX(ctx context.Context, id string) *SupportedLocale { + obj, err := c.Get(ctx, id) + if err != nil { + panic(err) + } + return obj +} + +// QueryTranslatedCategories queries the translated_categories edge of a SupportedLocale. +func (c *SupportedLocaleClient) QueryTranslatedCategories(_m *SupportedLocale) *ServiceCategoryQuery { + query := (&ServiceCategoryClient{config: c.config}).Query() + query.path = func(context.Context) (fromV *sql.Selector, _ error) { + id := _m.ID + step := sqlgraph.NewStep( + sqlgraph.From(supportedlocale.Table, supportedlocale.FieldID, id), + sqlgraph.To(servicecategory.Table, servicecategory.FieldID), + sqlgraph.Edge(sqlgraph.M2M, true, supportedlocale.TranslatedCategoriesTable, supportedlocale.TranslatedCategoriesPrimaryKey...), + ) + fromV = sqlgraph.Neighbors(_m.driver.Dialect(), step) + return fromV, nil + } + return query +} + +// QueryTranslatedLanguages queries the translated_languages edge of a SupportedLocale. +func (c *SupportedLocaleClient) QueryTranslatedLanguages(_m *SupportedLocale) *SpokenLanguageQuery { + query := (&SpokenLanguageClient{config: c.config}).Query() + query.path = func(context.Context) (fromV *sql.Selector, _ error) { + id := _m.ID + step := sqlgraph.NewStep( + sqlgraph.From(supportedlocale.Table, supportedlocale.FieldID, id), + sqlgraph.To(spokenlanguage.Table, spokenlanguage.FieldID), + sqlgraph.Edge(sqlgraph.M2M, true, supportedlocale.TranslatedLanguagesTable, supportedlocale.TranslatedLanguagesPrimaryKey...), + ) + fromV = sqlgraph.Neighbors(_m.driver.Dialect(), step) + return fromV, nil + } + return query +} + +// Hooks returns the client hooks. +func (c *SupportedLocaleClient) Hooks() []Hook { + return c.hooks.SupportedLocale +} + +// Interceptors returns the client interceptors. +func (c *SupportedLocaleClient) Interceptors() []Interceptor { + return c.inters.SupportedLocale +} + +func (c *SupportedLocaleClient) mutate(ctx context.Context, m *SupportedLocaleMutation) (Value, error) { + switch m.Op() { + case OpCreate: + return (&SupportedLocaleCreate{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx) + case OpUpdate: + return (&SupportedLocaleUpdate{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx) + case OpUpdateOne: + return (&SupportedLocaleUpdateOne{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx) + case OpDelete, OpDeleteOne: + return (&SupportedLocaleDelete{config: c.config, hooks: c.Hooks(), mutation: m}).Exec(ctx) + default: + return nil, fmt.Errorf("ent: unknown SupportedLocale mutation op: %q", m.Op()) + } +} + +// UserAccountClient is a client for the UserAccount schema. +type UserAccountClient struct { + config +} + +// NewUserAccountClient returns a client for the UserAccount from the given config. +func NewUserAccountClient(c config) *UserAccountClient { + return &UserAccountClient{config: c} +} + +// Use adds a list of mutation hooks to the hooks stack. +// A call to `Use(f, g, h)` equals to `useraccount.Hooks(f(g(h())))`. +func (c *UserAccountClient) Use(hooks ...Hook) { + c.hooks.UserAccount = append(c.hooks.UserAccount, hooks...) +} + +// Intercept adds a list of query interceptors to the interceptors stack. +// A call to `Intercept(f, g, h)` equals to `useraccount.Intercept(f(g(h())))`. +func (c *UserAccountClient) Intercept(interceptors ...Interceptor) { + c.inters.UserAccount = append(c.inters.UserAccount, interceptors...) +} + +// Create returns a builder for creating a UserAccount entity. +func (c *UserAccountClient) Create() *UserAccountCreate { + mutation := newUserAccountMutation(c.config, OpCreate) + return &UserAccountCreate{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// CreateBulk returns a builder for creating a bulk of UserAccount entities. +func (c *UserAccountClient) CreateBulk(builders ...*UserAccountCreate) *UserAccountCreateBulk { + return &UserAccountCreateBulk{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 *UserAccountClient) MapCreateBulk(slice any, setFunc func(*UserAccountCreate, int)) *UserAccountCreateBulk { + rv := reflect.ValueOf(slice) + if rv.Kind() != reflect.Slice { + return &UserAccountCreateBulk{err: fmt.Errorf("calling to UserAccountClient.MapCreateBulk with wrong type %T, need slice", slice)} + } + builders := make([]*UserAccountCreate, rv.Len()) + for i := 0; i < rv.Len(); i++ { + builders[i] = c.Create() + setFunc(builders[i], i) + } + return &UserAccountCreateBulk{config: c.config, builders: builders} +} + +// Update returns an update builder for UserAccount. +func (c *UserAccountClient) Update() *UserAccountUpdate { + mutation := newUserAccountMutation(c.config, OpUpdate) + return &UserAccountUpdate{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// UpdateOne returns an update builder for the given entity. +func (c *UserAccountClient) UpdateOne(_m *UserAccount) *UserAccountUpdateOne { + mutation := newUserAccountMutation(c.config, OpUpdateOne, withUserAccount(_m)) + return &UserAccountUpdateOne{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// UpdateOneID returns an update builder for the given id. +func (c *UserAccountClient) UpdateOneID(id uuid.UUID) *UserAccountUpdateOne { + mutation := newUserAccountMutation(c.config, OpUpdateOne, withUserAccountID(id)) + return &UserAccountUpdateOne{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// Delete returns a delete builder for UserAccount. +func (c *UserAccountClient) Delete() *UserAccountDelete { + mutation := newUserAccountMutation(c.config, OpDelete) + return &UserAccountDelete{config: c.config, hooks: c.Hooks(), mutation: mutation} +} + +// DeleteOne returns a builder for deleting the given entity. +func (c *UserAccountClient) DeleteOne(_m *UserAccount) *UserAccountDeleteOne { + return c.DeleteOneID(_m.ID) +} + +// DeleteOneID returns a builder for deleting the given entity by its id. +func (c *UserAccountClient) DeleteOneID(id uuid.UUID) *UserAccountDeleteOne { + builder := c.Delete().Where(useraccount.ID(id)) + builder.mutation.id = &id + builder.mutation.op = OpDeleteOne + return &UserAccountDeleteOne{builder} +} + +// Query returns a query builder for UserAccount. +func (c *UserAccountClient) Query() *UserAccountQuery { + return &UserAccountQuery{ + config: c.config, + ctx: &QueryContext{Type: TypeUserAccount}, + inters: c.Interceptors(), + } +} + +// Get returns a UserAccount entity by its id. +func (c *UserAccountClient) Get(ctx context.Context, id uuid.UUID) (*UserAccount, error) { + return c.Query().Where(useraccount.ID(id)).Only(ctx) +} + +// GetX is like Get, but panics if an error occurs. +func (c *UserAccountClient) GetX(ctx context.Context, id uuid.UUID) *UserAccount { + obj, err := c.Get(ctx, id) + if err != nil { + panic(err) + } + return obj +} + +// Hooks returns the client hooks. +func (c *UserAccountClient) Hooks() []Hook { + return c.hooks.UserAccount +} + +// Interceptors returns the client interceptors. +func (c *UserAccountClient) Interceptors() []Interceptor { + return c.inters.UserAccount +} + +func (c *UserAccountClient) mutate(ctx context.Context, m *UserAccountMutation) (Value, error) { + switch m.Op() { + case OpCreate: + return (&UserAccountCreate{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx) + case OpUpdate: + return (&UserAccountUpdate{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx) + case OpUpdateOne: + return (&UserAccountUpdateOne{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx) + case OpDelete, OpDeleteOne: + return (&UserAccountDelete{config: c.config, hooks: c.Hooks(), mutation: m}).Exec(ctx) + default: + return nil, fmt.Errorf("ent: unknown UserAccount mutation op: %q", m.Op()) + } +} + +// hooks and interceptors per client, for fast access. +type ( + hooks struct { + AdministrativeArea, ContactRevealDailyLimit, ContactRevealEvent, InternalUser, + Listing, ListingEvent, ListingMedia, Locality, PlatformRole, + ProviderContactChannel, ProviderProfile, ProviderServiceLocality, + ProviderSpokenLanguage, ServiceCategory, ServiceCategoryTranslation, + SpokenLanguage, SpokenLanguageTranslation, SupportedLocale, + UserAccount []ent.Hook + } + inters struct { + AdministrativeArea, ContactRevealDailyLimit, ContactRevealEvent, InternalUser, + Listing, ListingEvent, ListingMedia, Locality, PlatformRole, + ProviderContactChannel, ProviderProfile, ProviderServiceLocality, + ProviderSpokenLanguage, ServiceCategory, ServiceCategoryTranslation, + SpokenLanguage, SpokenLanguageTranslation, SupportedLocale, + UserAccount []ent.Interceptor + } +) diff --git a/backend/ent/contactrevealdailylimit.go b/backend/ent/contactrevealdailylimit.go new file mode 100644 index 0000000..f42e62c --- /dev/null +++ b/backend/ent/contactrevealdailylimit.go @@ -0,0 +1,151 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "fmt" + "strings" + "time" + + "entgo.io/ent" + "entgo.io/ent/dialect/sql" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/contactrevealdailylimit" + "github.com/google/uuid" +) + +// ContactRevealDailyLimit is the model entity for the ContactRevealDailyLimit schema. +type ContactRevealDailyLimit struct { + config `json:"-"` + // ID of the ent. + ID uuid.UUID `json:"id,omitempty"` + // CustomerInternalUserID holds the value of the "customer_internal_user_id" field. + CustomerInternalUserID uuid.UUID `json:"customer_internal_user_id,omitempty"` + // UtcDay holds the value of the "utc_day" field. + UtcDay time.Time `json:"utc_day,omitempty"` + // SuccessfulCount holds the value of the "successful_count" field. + SuccessfulCount int `json:"successful_count,omitempty"` + // CreatedAt holds the value of the "created_at" field. + CreatedAt time.Time `json:"created_at,omitempty"` + // UpdatedAt holds the value of the "updated_at" field. + UpdatedAt time.Time `json:"updated_at,omitempty"` + selectValues sql.SelectValues +} + +// scanValues returns the types for scanning values from sql.Rows. +func (*ContactRevealDailyLimit) scanValues(columns []string) ([]any, error) { + values := make([]any, len(columns)) + for i := range columns { + switch columns[i] { + case contactrevealdailylimit.FieldSuccessfulCount: + values[i] = new(sql.NullInt64) + case contactrevealdailylimit.FieldUtcDay, contactrevealdailylimit.FieldCreatedAt, contactrevealdailylimit.FieldUpdatedAt: + values[i] = new(sql.NullTime) + case contactrevealdailylimit.FieldID, contactrevealdailylimit.FieldCustomerInternalUserID: + 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 ContactRevealDailyLimit fields. +func (_m *ContactRevealDailyLimit) 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 contactrevealdailylimit.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 contactrevealdailylimit.FieldCustomerInternalUserID: + if value, ok := values[i].(*uuid.UUID); !ok { + return fmt.Errorf("unexpected type %T for field customer_internal_user_id", values[i]) + } else if value != nil { + _m.CustomerInternalUserID = *value + } + case contactrevealdailylimit.FieldUtcDay: + if value, ok := values[i].(*sql.NullTime); !ok { + return fmt.Errorf("unexpected type %T for field utc_day", values[i]) + } else if value.Valid { + _m.UtcDay = value.Time + } + case contactrevealdailylimit.FieldSuccessfulCount: + if value, ok := values[i].(*sql.NullInt64); !ok { + return fmt.Errorf("unexpected type %T for field successful_count", values[i]) + } else if value.Valid { + _m.SuccessfulCount = int(value.Int64) + } + case contactrevealdailylimit.FieldCreatedAt: + if value, ok := values[i].(*sql.NullTime); !ok { + return fmt.Errorf("unexpected type %T for field created_at", values[i]) + } else if value.Valid { + _m.CreatedAt = value.Time + } + case contactrevealdailylimit.FieldUpdatedAt: + if value, ok := values[i].(*sql.NullTime); !ok { + return fmt.Errorf("unexpected type %T for field updated_at", values[i]) + } else if value.Valid { + _m.UpdatedAt = value.Time + } + default: + _m.selectValues.Set(columns[i], values[i]) + } + } + return nil +} + +// Value returns the ent.Value that was dynamically selected and assigned to the ContactRevealDailyLimit. +// This includes values selected through modifiers, order, etc. +func (_m *ContactRevealDailyLimit) Value(name string) (ent.Value, error) { + return _m.selectValues.Get(name) +} + +// Update returns a builder for updating this ContactRevealDailyLimit. +// Note that you need to call ContactRevealDailyLimit.Unwrap() before calling this method if this ContactRevealDailyLimit +// was returned from a transaction, and the transaction was committed or rolled back. +func (_m *ContactRevealDailyLimit) Update() *ContactRevealDailyLimitUpdateOne { + return NewContactRevealDailyLimitClient(_m.config).UpdateOne(_m) +} + +// Unwrap unwraps the ContactRevealDailyLimit 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 *ContactRevealDailyLimit) Unwrap() *ContactRevealDailyLimit { + _tx, ok := _m.config.driver.(*txDriver) + if !ok { + panic("ent: ContactRevealDailyLimit is not a transactional entity") + } + _m.config.driver = _tx.drv + return _m +} + +// String implements the fmt.Stringer. +func (_m *ContactRevealDailyLimit) String() string { + var builder strings.Builder + builder.WriteString("ContactRevealDailyLimit(") + builder.WriteString(fmt.Sprintf("id=%v, ", _m.ID)) + builder.WriteString("customer_internal_user_id=") + builder.WriteString(fmt.Sprintf("%v", _m.CustomerInternalUserID)) + builder.WriteString(", ") + builder.WriteString("utc_day=") + builder.WriteString(_m.UtcDay.Format(time.ANSIC)) + builder.WriteString(", ") + builder.WriteString("successful_count=") + builder.WriteString(fmt.Sprintf("%v", _m.SuccessfulCount)) + builder.WriteString(", ") + builder.WriteString("created_at=") + builder.WriteString(_m.CreatedAt.Format(time.ANSIC)) + builder.WriteString(", ") + builder.WriteString("updated_at=") + builder.WriteString(_m.UpdatedAt.Format(time.ANSIC)) + builder.WriteByte(')') + return builder.String() +} + +// ContactRevealDailyLimits is a parsable slice of ContactRevealDailyLimit. +type ContactRevealDailyLimits []*ContactRevealDailyLimit diff --git a/backend/ent/contactrevealdailylimit/contactrevealdailylimit.go b/backend/ent/contactrevealdailylimit/contactrevealdailylimit.go new file mode 100644 index 0000000..e9c0979 --- /dev/null +++ b/backend/ent/contactrevealdailylimit/contactrevealdailylimit.go @@ -0,0 +1,97 @@ +// Code generated by ent, DO NOT EDIT. + +package contactrevealdailylimit + +import ( + "time" + + "entgo.io/ent/dialect/sql" + "github.com/google/uuid" +) + +const ( + // Label holds the string label denoting the contactrevealdailylimit type in the database. + Label = "contact_reveal_daily_limit" + // FieldID holds the string denoting the id field in the database. + FieldID = "id" + // FieldCustomerInternalUserID holds the string denoting the customer_internal_user_id field in the database. + FieldCustomerInternalUserID = "customer_internal_user_id" + // FieldUtcDay holds the string denoting the utc_day field in the database. + FieldUtcDay = "utc_day" + // FieldSuccessfulCount holds the string denoting the successful_count field in the database. + FieldSuccessfulCount = "successful_count" + // FieldCreatedAt holds the string denoting the created_at field in the database. + FieldCreatedAt = "created_at" + // FieldUpdatedAt holds the string denoting the updated_at field in the database. + FieldUpdatedAt = "updated_at" + // Table holds the table name of the contactrevealdailylimit in the database. + Table = "contact_reveal_daily_limits" +) + +// Columns holds all SQL columns for contactrevealdailylimit fields. +var Columns = []string{ + FieldID, + FieldCustomerInternalUserID, + FieldUtcDay, + FieldSuccessfulCount, + FieldCreatedAt, + FieldUpdatedAt, +} + +// 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 ( + // DefaultSuccessfulCount holds the default value on creation for the "successful_count" field. + DefaultSuccessfulCount int + // SuccessfulCountValidator is a validator for the "successful_count" field. It is called by the builders before save. + SuccessfulCountValidator func(int) error + // DefaultCreatedAt holds the default value on creation for the "created_at" field. + DefaultCreatedAt func() time.Time + // DefaultUpdatedAt holds the default value on creation for the "updated_at" field. + DefaultUpdatedAt func() time.Time + // UpdateDefaultUpdatedAt holds the default value on update for the "updated_at" field. + UpdateDefaultUpdatedAt func() time.Time + // DefaultID holds the default value on creation for the "id" field. + DefaultID func() uuid.UUID +) + +// OrderOption defines the ordering options for the ContactRevealDailyLimit 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() +} + +// ByCustomerInternalUserID orders the results by the customer_internal_user_id field. +func ByCustomerInternalUserID(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldCustomerInternalUserID, opts...).ToFunc() +} + +// ByUtcDay orders the results by the utc_day field. +func ByUtcDay(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldUtcDay, opts...).ToFunc() +} + +// BySuccessfulCount orders the results by the successful_count field. +func BySuccessfulCount(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldSuccessfulCount, opts...).ToFunc() +} + +// ByCreatedAt orders the results by the created_at field. +func ByCreatedAt(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldCreatedAt, opts...).ToFunc() +} + +// ByUpdatedAt orders the results by the updated_at field. +func ByUpdatedAt(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldUpdatedAt, opts...).ToFunc() +} diff --git a/backend/ent/contactrevealdailylimit/where.go b/backend/ent/contactrevealdailylimit/where.go new file mode 100644 index 0000000..df22ca5 --- /dev/null +++ b/backend/ent/contactrevealdailylimit/where.go @@ -0,0 +1,296 @@ +// Code generated by ent, DO NOT EDIT. + +package contactrevealdailylimit + +import ( + "time" + + "entgo.io/ent/dialect/sql" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/predicate" + "github.com/google/uuid" +) + +// ID filters vertices based on their ID field. +func ID(id uuid.UUID) predicate.ContactRevealDailyLimit { + return predicate.ContactRevealDailyLimit(sql.FieldEQ(FieldID, id)) +} + +// IDEQ applies the EQ predicate on the ID field. +func IDEQ(id uuid.UUID) predicate.ContactRevealDailyLimit { + return predicate.ContactRevealDailyLimit(sql.FieldEQ(FieldID, id)) +} + +// IDNEQ applies the NEQ predicate on the ID field. +func IDNEQ(id uuid.UUID) predicate.ContactRevealDailyLimit { + return predicate.ContactRevealDailyLimit(sql.FieldNEQ(FieldID, id)) +} + +// IDIn applies the In predicate on the ID field. +func IDIn(ids ...uuid.UUID) predicate.ContactRevealDailyLimit { + return predicate.ContactRevealDailyLimit(sql.FieldIn(FieldID, ids...)) +} + +// IDNotIn applies the NotIn predicate on the ID field. +func IDNotIn(ids ...uuid.UUID) predicate.ContactRevealDailyLimit { + return predicate.ContactRevealDailyLimit(sql.FieldNotIn(FieldID, ids...)) +} + +// IDGT applies the GT predicate on the ID field. +func IDGT(id uuid.UUID) predicate.ContactRevealDailyLimit { + return predicate.ContactRevealDailyLimit(sql.FieldGT(FieldID, id)) +} + +// IDGTE applies the GTE predicate on the ID field. +func IDGTE(id uuid.UUID) predicate.ContactRevealDailyLimit { + return predicate.ContactRevealDailyLimit(sql.FieldGTE(FieldID, id)) +} + +// IDLT applies the LT predicate on the ID field. +func IDLT(id uuid.UUID) predicate.ContactRevealDailyLimit { + return predicate.ContactRevealDailyLimit(sql.FieldLT(FieldID, id)) +} + +// IDLTE applies the LTE predicate on the ID field. +func IDLTE(id uuid.UUID) predicate.ContactRevealDailyLimit { + return predicate.ContactRevealDailyLimit(sql.FieldLTE(FieldID, id)) +} + +// CustomerInternalUserID applies equality check predicate on the "customer_internal_user_id" field. It's identical to CustomerInternalUserIDEQ. +func CustomerInternalUserID(v uuid.UUID) predicate.ContactRevealDailyLimit { + return predicate.ContactRevealDailyLimit(sql.FieldEQ(FieldCustomerInternalUserID, v)) +} + +// UtcDay applies equality check predicate on the "utc_day" field. It's identical to UtcDayEQ. +func UtcDay(v time.Time) predicate.ContactRevealDailyLimit { + return predicate.ContactRevealDailyLimit(sql.FieldEQ(FieldUtcDay, v)) +} + +// SuccessfulCount applies equality check predicate on the "successful_count" field. It's identical to SuccessfulCountEQ. +func SuccessfulCount(v int) predicate.ContactRevealDailyLimit { + return predicate.ContactRevealDailyLimit(sql.FieldEQ(FieldSuccessfulCount, v)) +} + +// CreatedAt applies equality check predicate on the "created_at" field. It's identical to CreatedAtEQ. +func CreatedAt(v time.Time) predicate.ContactRevealDailyLimit { + return predicate.ContactRevealDailyLimit(sql.FieldEQ(FieldCreatedAt, v)) +} + +// UpdatedAt applies equality check predicate on the "updated_at" field. It's identical to UpdatedAtEQ. +func UpdatedAt(v time.Time) predicate.ContactRevealDailyLimit { + return predicate.ContactRevealDailyLimit(sql.FieldEQ(FieldUpdatedAt, v)) +} + +// CustomerInternalUserIDEQ applies the EQ predicate on the "customer_internal_user_id" field. +func CustomerInternalUserIDEQ(v uuid.UUID) predicate.ContactRevealDailyLimit { + return predicate.ContactRevealDailyLimit(sql.FieldEQ(FieldCustomerInternalUserID, v)) +} + +// CustomerInternalUserIDNEQ applies the NEQ predicate on the "customer_internal_user_id" field. +func CustomerInternalUserIDNEQ(v uuid.UUID) predicate.ContactRevealDailyLimit { + return predicate.ContactRevealDailyLimit(sql.FieldNEQ(FieldCustomerInternalUserID, v)) +} + +// CustomerInternalUserIDIn applies the In predicate on the "customer_internal_user_id" field. +func CustomerInternalUserIDIn(vs ...uuid.UUID) predicate.ContactRevealDailyLimit { + return predicate.ContactRevealDailyLimit(sql.FieldIn(FieldCustomerInternalUserID, vs...)) +} + +// CustomerInternalUserIDNotIn applies the NotIn predicate on the "customer_internal_user_id" field. +func CustomerInternalUserIDNotIn(vs ...uuid.UUID) predicate.ContactRevealDailyLimit { + return predicate.ContactRevealDailyLimit(sql.FieldNotIn(FieldCustomerInternalUserID, vs...)) +} + +// CustomerInternalUserIDGT applies the GT predicate on the "customer_internal_user_id" field. +func CustomerInternalUserIDGT(v uuid.UUID) predicate.ContactRevealDailyLimit { + return predicate.ContactRevealDailyLimit(sql.FieldGT(FieldCustomerInternalUserID, v)) +} + +// CustomerInternalUserIDGTE applies the GTE predicate on the "customer_internal_user_id" field. +func CustomerInternalUserIDGTE(v uuid.UUID) predicate.ContactRevealDailyLimit { + return predicate.ContactRevealDailyLimit(sql.FieldGTE(FieldCustomerInternalUserID, v)) +} + +// CustomerInternalUserIDLT applies the LT predicate on the "customer_internal_user_id" field. +func CustomerInternalUserIDLT(v uuid.UUID) predicate.ContactRevealDailyLimit { + return predicate.ContactRevealDailyLimit(sql.FieldLT(FieldCustomerInternalUserID, v)) +} + +// CustomerInternalUserIDLTE applies the LTE predicate on the "customer_internal_user_id" field. +func CustomerInternalUserIDLTE(v uuid.UUID) predicate.ContactRevealDailyLimit { + return predicate.ContactRevealDailyLimit(sql.FieldLTE(FieldCustomerInternalUserID, v)) +} + +// UtcDayEQ applies the EQ predicate on the "utc_day" field. +func UtcDayEQ(v time.Time) predicate.ContactRevealDailyLimit { + return predicate.ContactRevealDailyLimit(sql.FieldEQ(FieldUtcDay, v)) +} + +// UtcDayNEQ applies the NEQ predicate on the "utc_day" field. +func UtcDayNEQ(v time.Time) predicate.ContactRevealDailyLimit { + return predicate.ContactRevealDailyLimit(sql.FieldNEQ(FieldUtcDay, v)) +} + +// UtcDayIn applies the In predicate on the "utc_day" field. +func UtcDayIn(vs ...time.Time) predicate.ContactRevealDailyLimit { + return predicate.ContactRevealDailyLimit(sql.FieldIn(FieldUtcDay, vs...)) +} + +// UtcDayNotIn applies the NotIn predicate on the "utc_day" field. +func UtcDayNotIn(vs ...time.Time) predicate.ContactRevealDailyLimit { + return predicate.ContactRevealDailyLimit(sql.FieldNotIn(FieldUtcDay, vs...)) +} + +// UtcDayGT applies the GT predicate on the "utc_day" field. +func UtcDayGT(v time.Time) predicate.ContactRevealDailyLimit { + return predicate.ContactRevealDailyLimit(sql.FieldGT(FieldUtcDay, v)) +} + +// UtcDayGTE applies the GTE predicate on the "utc_day" field. +func UtcDayGTE(v time.Time) predicate.ContactRevealDailyLimit { + return predicate.ContactRevealDailyLimit(sql.FieldGTE(FieldUtcDay, v)) +} + +// UtcDayLT applies the LT predicate on the "utc_day" field. +func UtcDayLT(v time.Time) predicate.ContactRevealDailyLimit { + return predicate.ContactRevealDailyLimit(sql.FieldLT(FieldUtcDay, v)) +} + +// UtcDayLTE applies the LTE predicate on the "utc_day" field. +func UtcDayLTE(v time.Time) predicate.ContactRevealDailyLimit { + return predicate.ContactRevealDailyLimit(sql.FieldLTE(FieldUtcDay, v)) +} + +// SuccessfulCountEQ applies the EQ predicate on the "successful_count" field. +func SuccessfulCountEQ(v int) predicate.ContactRevealDailyLimit { + return predicate.ContactRevealDailyLimit(sql.FieldEQ(FieldSuccessfulCount, v)) +} + +// SuccessfulCountNEQ applies the NEQ predicate on the "successful_count" field. +func SuccessfulCountNEQ(v int) predicate.ContactRevealDailyLimit { + return predicate.ContactRevealDailyLimit(sql.FieldNEQ(FieldSuccessfulCount, v)) +} + +// SuccessfulCountIn applies the In predicate on the "successful_count" field. +func SuccessfulCountIn(vs ...int) predicate.ContactRevealDailyLimit { + return predicate.ContactRevealDailyLimit(sql.FieldIn(FieldSuccessfulCount, vs...)) +} + +// SuccessfulCountNotIn applies the NotIn predicate on the "successful_count" field. +func SuccessfulCountNotIn(vs ...int) predicate.ContactRevealDailyLimit { + return predicate.ContactRevealDailyLimit(sql.FieldNotIn(FieldSuccessfulCount, vs...)) +} + +// SuccessfulCountGT applies the GT predicate on the "successful_count" field. +func SuccessfulCountGT(v int) predicate.ContactRevealDailyLimit { + return predicate.ContactRevealDailyLimit(sql.FieldGT(FieldSuccessfulCount, v)) +} + +// SuccessfulCountGTE applies the GTE predicate on the "successful_count" field. +func SuccessfulCountGTE(v int) predicate.ContactRevealDailyLimit { + return predicate.ContactRevealDailyLimit(sql.FieldGTE(FieldSuccessfulCount, v)) +} + +// SuccessfulCountLT applies the LT predicate on the "successful_count" field. +func SuccessfulCountLT(v int) predicate.ContactRevealDailyLimit { + return predicate.ContactRevealDailyLimit(sql.FieldLT(FieldSuccessfulCount, v)) +} + +// SuccessfulCountLTE applies the LTE predicate on the "successful_count" field. +func SuccessfulCountLTE(v int) predicate.ContactRevealDailyLimit { + return predicate.ContactRevealDailyLimit(sql.FieldLTE(FieldSuccessfulCount, v)) +} + +// CreatedAtEQ applies the EQ predicate on the "created_at" field. +func CreatedAtEQ(v time.Time) predicate.ContactRevealDailyLimit { + return predicate.ContactRevealDailyLimit(sql.FieldEQ(FieldCreatedAt, v)) +} + +// CreatedAtNEQ applies the NEQ predicate on the "created_at" field. +func CreatedAtNEQ(v time.Time) predicate.ContactRevealDailyLimit { + return predicate.ContactRevealDailyLimit(sql.FieldNEQ(FieldCreatedAt, v)) +} + +// CreatedAtIn applies the In predicate on the "created_at" field. +func CreatedAtIn(vs ...time.Time) predicate.ContactRevealDailyLimit { + return predicate.ContactRevealDailyLimit(sql.FieldIn(FieldCreatedAt, vs...)) +} + +// CreatedAtNotIn applies the NotIn predicate on the "created_at" field. +func CreatedAtNotIn(vs ...time.Time) predicate.ContactRevealDailyLimit { + return predicate.ContactRevealDailyLimit(sql.FieldNotIn(FieldCreatedAt, vs...)) +} + +// CreatedAtGT applies the GT predicate on the "created_at" field. +func CreatedAtGT(v time.Time) predicate.ContactRevealDailyLimit { + return predicate.ContactRevealDailyLimit(sql.FieldGT(FieldCreatedAt, v)) +} + +// CreatedAtGTE applies the GTE predicate on the "created_at" field. +func CreatedAtGTE(v time.Time) predicate.ContactRevealDailyLimit { + return predicate.ContactRevealDailyLimit(sql.FieldGTE(FieldCreatedAt, v)) +} + +// CreatedAtLT applies the LT predicate on the "created_at" field. +func CreatedAtLT(v time.Time) predicate.ContactRevealDailyLimit { + return predicate.ContactRevealDailyLimit(sql.FieldLT(FieldCreatedAt, v)) +} + +// CreatedAtLTE applies the LTE predicate on the "created_at" field. +func CreatedAtLTE(v time.Time) predicate.ContactRevealDailyLimit { + return predicate.ContactRevealDailyLimit(sql.FieldLTE(FieldCreatedAt, v)) +} + +// UpdatedAtEQ applies the EQ predicate on the "updated_at" field. +func UpdatedAtEQ(v time.Time) predicate.ContactRevealDailyLimit { + return predicate.ContactRevealDailyLimit(sql.FieldEQ(FieldUpdatedAt, v)) +} + +// UpdatedAtNEQ applies the NEQ predicate on the "updated_at" field. +func UpdatedAtNEQ(v time.Time) predicate.ContactRevealDailyLimit { + return predicate.ContactRevealDailyLimit(sql.FieldNEQ(FieldUpdatedAt, v)) +} + +// UpdatedAtIn applies the In predicate on the "updated_at" field. +func UpdatedAtIn(vs ...time.Time) predicate.ContactRevealDailyLimit { + return predicate.ContactRevealDailyLimit(sql.FieldIn(FieldUpdatedAt, vs...)) +} + +// UpdatedAtNotIn applies the NotIn predicate on the "updated_at" field. +func UpdatedAtNotIn(vs ...time.Time) predicate.ContactRevealDailyLimit { + return predicate.ContactRevealDailyLimit(sql.FieldNotIn(FieldUpdatedAt, vs...)) +} + +// UpdatedAtGT applies the GT predicate on the "updated_at" field. +func UpdatedAtGT(v time.Time) predicate.ContactRevealDailyLimit { + return predicate.ContactRevealDailyLimit(sql.FieldGT(FieldUpdatedAt, v)) +} + +// UpdatedAtGTE applies the GTE predicate on the "updated_at" field. +func UpdatedAtGTE(v time.Time) predicate.ContactRevealDailyLimit { + return predicate.ContactRevealDailyLimit(sql.FieldGTE(FieldUpdatedAt, v)) +} + +// UpdatedAtLT applies the LT predicate on the "updated_at" field. +func UpdatedAtLT(v time.Time) predicate.ContactRevealDailyLimit { + return predicate.ContactRevealDailyLimit(sql.FieldLT(FieldUpdatedAt, v)) +} + +// UpdatedAtLTE applies the LTE predicate on the "updated_at" field. +func UpdatedAtLTE(v time.Time) predicate.ContactRevealDailyLimit { + return predicate.ContactRevealDailyLimit(sql.FieldLTE(FieldUpdatedAt, v)) +} + +// And groups predicates with the AND operator between them. +func And(predicates ...predicate.ContactRevealDailyLimit) predicate.ContactRevealDailyLimit { + return predicate.ContactRevealDailyLimit(sql.AndPredicates(predicates...)) +} + +// Or groups predicates with the OR operator between them. +func Or(predicates ...predicate.ContactRevealDailyLimit) predicate.ContactRevealDailyLimit { + return predicate.ContactRevealDailyLimit(sql.OrPredicates(predicates...)) +} + +// Not applies the not operator on the given predicate. +func Not(p predicate.ContactRevealDailyLimit) predicate.ContactRevealDailyLimit { + return predicate.ContactRevealDailyLimit(sql.NotPredicates(p)) +} diff --git a/backend/ent/contactrevealdailylimit_create.go b/backend/ent/contactrevealdailylimit_create.go new file mode 100644 index 0000000..23400d7 --- /dev/null +++ b/backend/ent/contactrevealdailylimit_create.go @@ -0,0 +1,307 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "context" + "errors" + "fmt" + "time" + + "entgo.io/ent/dialect/sql/sqlgraph" + "entgo.io/ent/schema/field" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/contactrevealdailylimit" + "github.com/google/uuid" +) + +// ContactRevealDailyLimitCreate is the builder for creating a ContactRevealDailyLimit entity. +type ContactRevealDailyLimitCreate struct { + config + mutation *ContactRevealDailyLimitMutation + hooks []Hook +} + +// SetCustomerInternalUserID sets the "customer_internal_user_id" field. +func (_c *ContactRevealDailyLimitCreate) SetCustomerInternalUserID(v uuid.UUID) *ContactRevealDailyLimitCreate { + _c.mutation.SetCustomerInternalUserID(v) + return _c +} + +// SetUtcDay sets the "utc_day" field. +func (_c *ContactRevealDailyLimitCreate) SetUtcDay(v time.Time) *ContactRevealDailyLimitCreate { + _c.mutation.SetUtcDay(v) + return _c +} + +// SetSuccessfulCount sets the "successful_count" field. +func (_c *ContactRevealDailyLimitCreate) SetSuccessfulCount(v int) *ContactRevealDailyLimitCreate { + _c.mutation.SetSuccessfulCount(v) + return _c +} + +// SetNillableSuccessfulCount sets the "successful_count" field if the given value is not nil. +func (_c *ContactRevealDailyLimitCreate) SetNillableSuccessfulCount(v *int) *ContactRevealDailyLimitCreate { + if v != nil { + _c.SetSuccessfulCount(*v) + } + return _c +} + +// SetCreatedAt sets the "created_at" field. +func (_c *ContactRevealDailyLimitCreate) SetCreatedAt(v time.Time) *ContactRevealDailyLimitCreate { + _c.mutation.SetCreatedAt(v) + return _c +} + +// SetNillableCreatedAt sets the "created_at" field if the given value is not nil. +func (_c *ContactRevealDailyLimitCreate) SetNillableCreatedAt(v *time.Time) *ContactRevealDailyLimitCreate { + if v != nil { + _c.SetCreatedAt(*v) + } + return _c +} + +// SetUpdatedAt sets the "updated_at" field. +func (_c *ContactRevealDailyLimitCreate) SetUpdatedAt(v time.Time) *ContactRevealDailyLimitCreate { + _c.mutation.SetUpdatedAt(v) + return _c +} + +// SetNillableUpdatedAt sets the "updated_at" field if the given value is not nil. +func (_c *ContactRevealDailyLimitCreate) SetNillableUpdatedAt(v *time.Time) *ContactRevealDailyLimitCreate { + if v != nil { + _c.SetUpdatedAt(*v) + } + return _c +} + +// SetID sets the "id" field. +func (_c *ContactRevealDailyLimitCreate) SetID(v uuid.UUID) *ContactRevealDailyLimitCreate { + _c.mutation.SetID(v) + return _c +} + +// SetNillableID sets the "id" field if the given value is not nil. +func (_c *ContactRevealDailyLimitCreate) SetNillableID(v *uuid.UUID) *ContactRevealDailyLimitCreate { + if v != nil { + _c.SetID(*v) + } + return _c +} + +// Mutation returns the ContactRevealDailyLimitMutation object of the builder. +func (_c *ContactRevealDailyLimitCreate) Mutation() *ContactRevealDailyLimitMutation { + return _c.mutation +} + +// Save creates the ContactRevealDailyLimit in the database. +func (_c *ContactRevealDailyLimitCreate) Save(ctx context.Context) (*ContactRevealDailyLimit, error) { + _c.defaults() + return withHooks(ctx, _c.sqlSave, _c.mutation, _c.hooks) +} + +// SaveX calls Save and panics if Save returns an error. +func (_c *ContactRevealDailyLimitCreate) SaveX(ctx context.Context) *ContactRevealDailyLimit { + v, err := _c.Save(ctx) + if err != nil { + panic(err) + } + return v +} + +// Exec executes the query. +func (_c *ContactRevealDailyLimitCreate) Exec(ctx context.Context) error { + _, err := _c.Save(ctx) + return err +} + +// ExecX is like Exec, but panics if an error occurs. +func (_c *ContactRevealDailyLimitCreate) 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 *ContactRevealDailyLimitCreate) defaults() { + if _, ok := _c.mutation.SuccessfulCount(); !ok { + v := contactrevealdailylimit.DefaultSuccessfulCount + _c.mutation.SetSuccessfulCount(v) + } + if _, ok := _c.mutation.CreatedAt(); !ok { + v := contactrevealdailylimit.DefaultCreatedAt() + _c.mutation.SetCreatedAt(v) + } + if _, ok := _c.mutation.UpdatedAt(); !ok { + v := contactrevealdailylimit.DefaultUpdatedAt() + _c.mutation.SetUpdatedAt(v) + } + if _, ok := _c.mutation.ID(); !ok { + v := contactrevealdailylimit.DefaultID() + _c.mutation.SetID(v) + } +} + +// check runs all checks and user-defined validators on the builder. +func (_c *ContactRevealDailyLimitCreate) check() error { + if _, ok := _c.mutation.CustomerInternalUserID(); !ok { + return &ValidationError{Name: "customer_internal_user_id", err: errors.New(`ent: missing required field "ContactRevealDailyLimit.customer_internal_user_id"`)} + } + if _, ok := _c.mutation.UtcDay(); !ok { + return &ValidationError{Name: "utc_day", err: errors.New(`ent: missing required field "ContactRevealDailyLimit.utc_day"`)} + } + if _, ok := _c.mutation.SuccessfulCount(); !ok { + return &ValidationError{Name: "successful_count", err: errors.New(`ent: missing required field "ContactRevealDailyLimit.successful_count"`)} + } + if v, ok := _c.mutation.SuccessfulCount(); ok { + if err := contactrevealdailylimit.SuccessfulCountValidator(v); err != nil { + return &ValidationError{Name: "successful_count", err: fmt.Errorf(`ent: validator failed for field "ContactRevealDailyLimit.successful_count": %w`, err)} + } + } + if _, ok := _c.mutation.CreatedAt(); !ok { + return &ValidationError{Name: "created_at", err: errors.New(`ent: missing required field "ContactRevealDailyLimit.created_at"`)} + } + if _, ok := _c.mutation.UpdatedAt(); !ok { + return &ValidationError{Name: "updated_at", err: errors.New(`ent: missing required field "ContactRevealDailyLimit.updated_at"`)} + } + return nil +} + +func (_c *ContactRevealDailyLimitCreate) sqlSave(ctx context.Context) (*ContactRevealDailyLimit, 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 *ContactRevealDailyLimitCreate) createSpec() (*ContactRevealDailyLimit, *sqlgraph.CreateSpec) { + var ( + _node = &ContactRevealDailyLimit{config: _c.config} + _spec = sqlgraph.NewCreateSpec(contactrevealdailylimit.Table, sqlgraph.NewFieldSpec(contactrevealdailylimit.FieldID, field.TypeUUID)) + ) + if id, ok := _c.mutation.ID(); ok { + _node.ID = id + _spec.ID.Value = &id + } + if value, ok := _c.mutation.CustomerInternalUserID(); ok { + _spec.SetField(contactrevealdailylimit.FieldCustomerInternalUserID, field.TypeUUID, value) + _node.CustomerInternalUserID = value + } + if value, ok := _c.mutation.UtcDay(); ok { + _spec.SetField(contactrevealdailylimit.FieldUtcDay, field.TypeTime, value) + _node.UtcDay = value + } + if value, ok := _c.mutation.SuccessfulCount(); ok { + _spec.SetField(contactrevealdailylimit.FieldSuccessfulCount, field.TypeInt, value) + _node.SuccessfulCount = value + } + if value, ok := _c.mutation.CreatedAt(); ok { + _spec.SetField(contactrevealdailylimit.FieldCreatedAt, field.TypeTime, value) + _node.CreatedAt = value + } + if value, ok := _c.mutation.UpdatedAt(); ok { + _spec.SetField(contactrevealdailylimit.FieldUpdatedAt, field.TypeTime, value) + _node.UpdatedAt = value + } + return _node, _spec +} + +// ContactRevealDailyLimitCreateBulk is the builder for creating many ContactRevealDailyLimit entities in bulk. +type ContactRevealDailyLimitCreateBulk struct { + config + err error + builders []*ContactRevealDailyLimitCreate +} + +// Save creates the ContactRevealDailyLimit entities in the database. +func (_c *ContactRevealDailyLimitCreateBulk) Save(ctx context.Context) ([]*ContactRevealDailyLimit, error) { + if _c.err != nil { + return nil, _c.err + } + specs := make([]*sqlgraph.CreateSpec, len(_c.builders)) + nodes := make([]*ContactRevealDailyLimit, 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.(*ContactRevealDailyLimitMutation) + 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 *ContactRevealDailyLimitCreateBulk) SaveX(ctx context.Context) []*ContactRevealDailyLimit { + v, err := _c.Save(ctx) + if err != nil { + panic(err) + } + return v +} + +// Exec executes the query. +func (_c *ContactRevealDailyLimitCreateBulk) Exec(ctx context.Context) error { + _, err := _c.Save(ctx) + return err +} + +// ExecX is like Exec, but panics if an error occurs. +func (_c *ContactRevealDailyLimitCreateBulk) ExecX(ctx context.Context) { + if err := _c.Exec(ctx); err != nil { + panic(err) + } +} diff --git a/backend/ent/contactrevealdailylimit_delete.go b/backend/ent/contactrevealdailylimit_delete.go new file mode 100644 index 0000000..aaa2a1a --- /dev/null +++ b/backend/ent/contactrevealdailylimit_delete.go @@ -0,0 +1,88 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "context" + + "entgo.io/ent/dialect/sql" + "entgo.io/ent/dialect/sql/sqlgraph" + "entgo.io/ent/schema/field" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/contactrevealdailylimit" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/predicate" +) + +// ContactRevealDailyLimitDelete is the builder for deleting a ContactRevealDailyLimit entity. +type ContactRevealDailyLimitDelete struct { + config + hooks []Hook + mutation *ContactRevealDailyLimitMutation +} + +// Where appends a list predicates to the ContactRevealDailyLimitDelete builder. +func (_d *ContactRevealDailyLimitDelete) Where(ps ...predicate.ContactRevealDailyLimit) *ContactRevealDailyLimitDelete { + _d.mutation.Where(ps...) + return _d +} + +// Exec executes the deletion query and returns how many vertices were deleted. +func (_d *ContactRevealDailyLimitDelete) 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 *ContactRevealDailyLimitDelete) ExecX(ctx context.Context) int { + n, err := _d.Exec(ctx) + if err != nil { + panic(err) + } + return n +} + +func (_d *ContactRevealDailyLimitDelete) sqlExec(ctx context.Context) (int, error) { + _spec := sqlgraph.NewDeleteSpec(contactrevealdailylimit.Table, sqlgraph.NewFieldSpec(contactrevealdailylimit.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 +} + +// ContactRevealDailyLimitDeleteOne is the builder for deleting a single ContactRevealDailyLimit entity. +type ContactRevealDailyLimitDeleteOne struct { + _d *ContactRevealDailyLimitDelete +} + +// Where appends a list predicates to the ContactRevealDailyLimitDelete builder. +func (_d *ContactRevealDailyLimitDeleteOne) Where(ps ...predicate.ContactRevealDailyLimit) *ContactRevealDailyLimitDeleteOne { + _d._d.mutation.Where(ps...) + return _d +} + +// Exec executes the deletion query. +func (_d *ContactRevealDailyLimitDeleteOne) Exec(ctx context.Context) error { + n, err := _d._d.Exec(ctx) + switch { + case err != nil: + return err + case n == 0: + return &NotFoundError{contactrevealdailylimit.Label} + default: + return nil + } +} + +// ExecX is like Exec, but panics if an error occurs. +func (_d *ContactRevealDailyLimitDeleteOne) ExecX(ctx context.Context) { + if err := _d.Exec(ctx); err != nil { + panic(err) + } +} diff --git a/backend/ent/contactrevealdailylimit_query.go b/backend/ent/contactrevealdailylimit_query.go new file mode 100644 index 0000000..978a1f7 --- /dev/null +++ b/backend/ent/contactrevealdailylimit_query.go @@ -0,0 +1,528 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "context" + "fmt" + "math" + + "entgo.io/ent" + "entgo.io/ent/dialect/sql" + "entgo.io/ent/dialect/sql/sqlgraph" + "entgo.io/ent/schema/field" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/contactrevealdailylimit" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/predicate" + "github.com/google/uuid" +) + +// ContactRevealDailyLimitQuery is the builder for querying ContactRevealDailyLimit entities. +type ContactRevealDailyLimitQuery struct { + config + ctx *QueryContext + order []contactrevealdailylimit.OrderOption + inters []Interceptor + predicates []predicate.ContactRevealDailyLimit + // intermediate query (i.e. traversal path). + sql *sql.Selector + path func(context.Context) (*sql.Selector, error) +} + +// Where adds a new predicate for the ContactRevealDailyLimitQuery builder. +func (_q *ContactRevealDailyLimitQuery) Where(ps ...predicate.ContactRevealDailyLimit) *ContactRevealDailyLimitQuery { + _q.predicates = append(_q.predicates, ps...) + return _q +} + +// Limit the number of records to be returned by this query. +func (_q *ContactRevealDailyLimitQuery) Limit(limit int) *ContactRevealDailyLimitQuery { + _q.ctx.Limit = &limit + return _q +} + +// Offset to start from. +func (_q *ContactRevealDailyLimitQuery) Offset(offset int) *ContactRevealDailyLimitQuery { + _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 *ContactRevealDailyLimitQuery) Unique(unique bool) *ContactRevealDailyLimitQuery { + _q.ctx.Unique = &unique + return _q +} + +// Order specifies how the records should be ordered. +func (_q *ContactRevealDailyLimitQuery) Order(o ...contactrevealdailylimit.OrderOption) *ContactRevealDailyLimitQuery { + _q.order = append(_q.order, o...) + return _q +} + +// First returns the first ContactRevealDailyLimit entity from the query. +// Returns a *NotFoundError when no ContactRevealDailyLimit was found. +func (_q *ContactRevealDailyLimitQuery) First(ctx context.Context) (*ContactRevealDailyLimit, 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{contactrevealdailylimit.Label} + } + return nodes[0], nil +} + +// FirstX is like First, but panics if an error occurs. +func (_q *ContactRevealDailyLimitQuery) FirstX(ctx context.Context) *ContactRevealDailyLimit { + node, err := _q.First(ctx) + if err != nil && !IsNotFound(err) { + panic(err) + } + return node +} + +// FirstID returns the first ContactRevealDailyLimit ID from the query. +// Returns a *NotFoundError when no ContactRevealDailyLimit ID was found. +func (_q *ContactRevealDailyLimitQuery) 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{contactrevealdailylimit.Label} + return + } + return ids[0], nil +} + +// FirstIDX is like FirstID, but panics if an error occurs. +func (_q *ContactRevealDailyLimitQuery) FirstIDX(ctx context.Context) uuid.UUID { + id, err := _q.FirstID(ctx) + if err != nil && !IsNotFound(err) { + panic(err) + } + return id +} + +// Only returns a single ContactRevealDailyLimit entity found by the query, ensuring it only returns one. +// Returns a *NotSingularError when more than one ContactRevealDailyLimit entity is found. +// Returns a *NotFoundError when no ContactRevealDailyLimit entities are found. +func (_q *ContactRevealDailyLimitQuery) Only(ctx context.Context) (*ContactRevealDailyLimit, 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{contactrevealdailylimit.Label} + default: + return nil, &NotSingularError{contactrevealdailylimit.Label} + } +} + +// OnlyX is like Only, but panics if an error occurs. +func (_q *ContactRevealDailyLimitQuery) OnlyX(ctx context.Context) *ContactRevealDailyLimit { + node, err := _q.Only(ctx) + if err != nil { + panic(err) + } + return node +} + +// OnlyID is like Only, but returns the only ContactRevealDailyLimit ID in the query. +// Returns a *NotSingularError when more than one ContactRevealDailyLimit ID is found. +// Returns a *NotFoundError when no entities are found. +func (_q *ContactRevealDailyLimitQuery) 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{contactrevealdailylimit.Label} + default: + err = &NotSingularError{contactrevealdailylimit.Label} + } + return +} + +// OnlyIDX is like OnlyID, but panics if an error occurs. +func (_q *ContactRevealDailyLimitQuery) 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 ContactRevealDailyLimits. +func (_q *ContactRevealDailyLimitQuery) All(ctx context.Context) ([]*ContactRevealDailyLimit, error) { + ctx = setContextOp(ctx, _q.ctx, ent.OpQueryAll) + if err := _q.prepareQuery(ctx); err != nil { + return nil, err + } + qr := querierAll[[]*ContactRevealDailyLimit, *ContactRevealDailyLimitQuery]() + return withInterceptors[[]*ContactRevealDailyLimit](ctx, _q, qr, _q.inters) +} + +// AllX is like All, but panics if an error occurs. +func (_q *ContactRevealDailyLimitQuery) AllX(ctx context.Context) []*ContactRevealDailyLimit { + nodes, err := _q.All(ctx) + if err != nil { + panic(err) + } + return nodes +} + +// IDs executes the query and returns a list of ContactRevealDailyLimit IDs. +func (_q *ContactRevealDailyLimitQuery) 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(contactrevealdailylimit.FieldID).Scan(ctx, &ids); err != nil { + return nil, err + } + return ids, nil +} + +// IDsX is like IDs, but panics if an error occurs. +func (_q *ContactRevealDailyLimitQuery) 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 *ContactRevealDailyLimitQuery) 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[*ContactRevealDailyLimitQuery](), _q.inters) +} + +// CountX is like Count, but panics if an error occurs. +func (_q *ContactRevealDailyLimitQuery) 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 *ContactRevealDailyLimitQuery) 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 *ContactRevealDailyLimitQuery) ExistX(ctx context.Context) bool { + exist, err := _q.Exist(ctx) + if err != nil { + panic(err) + } + return exist +} + +// Clone returns a duplicate of the ContactRevealDailyLimitQuery 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 *ContactRevealDailyLimitQuery) Clone() *ContactRevealDailyLimitQuery { + if _q == nil { + return nil + } + return &ContactRevealDailyLimitQuery{ + config: _q.config, + ctx: _q.ctx.Clone(), + order: append([]contactrevealdailylimit.OrderOption{}, _q.order...), + inters: append([]Interceptor{}, _q.inters...), + predicates: append([]predicate.ContactRevealDailyLimit{}, _q.predicates...), + // clone intermediate query. + sql: _q.sql.Clone(), + path: _q.path, + } +} + +// GroupBy is used to group vertices by one or more fields/columns. +// It is often used with aggregate functions, like: count, max, mean, min, sum. +// +// Example: +// +// var v []struct { +// CustomerInternalUserID uuid.UUID `json:"customer_internal_user_id,omitempty"` +// Count int `json:"count,omitempty"` +// } +// +// client.ContactRevealDailyLimit.Query(). +// GroupBy(contactrevealdailylimit.FieldCustomerInternalUserID). +// Aggregate(ent.Count()). +// Scan(ctx, &v) +func (_q *ContactRevealDailyLimitQuery) GroupBy(field string, fields ...string) *ContactRevealDailyLimitGroupBy { + _q.ctx.Fields = append([]string{field}, fields...) + grbuild := &ContactRevealDailyLimitGroupBy{build: _q} + grbuild.flds = &_q.ctx.Fields + grbuild.label = contactrevealdailylimit.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 { +// CustomerInternalUserID uuid.UUID `json:"customer_internal_user_id,omitempty"` +// } +// +// client.ContactRevealDailyLimit.Query(). +// Select(contactrevealdailylimit.FieldCustomerInternalUserID). +// Scan(ctx, &v) +func (_q *ContactRevealDailyLimitQuery) Select(fields ...string) *ContactRevealDailyLimitSelect { + _q.ctx.Fields = append(_q.ctx.Fields, fields...) + sbuild := &ContactRevealDailyLimitSelect{ContactRevealDailyLimitQuery: _q} + sbuild.label = contactrevealdailylimit.Label + sbuild.flds, sbuild.scan = &_q.ctx.Fields, sbuild.Scan + return sbuild +} + +// Aggregate returns a ContactRevealDailyLimitSelect configured with the given aggregations. +func (_q *ContactRevealDailyLimitQuery) Aggregate(fns ...AggregateFunc) *ContactRevealDailyLimitSelect { + return _q.Select().Aggregate(fns...) +} + +func (_q *ContactRevealDailyLimitQuery) 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 !contactrevealdailylimit.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 *ContactRevealDailyLimitQuery) sqlAll(ctx context.Context, hooks ...queryHook) ([]*ContactRevealDailyLimit, error) { + var ( + nodes = []*ContactRevealDailyLimit{} + _spec = _q.querySpec() + ) + _spec.ScanValues = func(columns []string) ([]any, error) { + return (*ContactRevealDailyLimit).scanValues(nil, columns) + } + _spec.Assign = func(columns []string, values []any) error { + node := &ContactRevealDailyLimit{config: _q.config} + nodes = append(nodes, node) + return node.assignValues(columns, values) + } + for i := range hooks { + hooks[i](ctx, _spec) + } + if err := sqlgraph.QueryNodes(ctx, _q.driver, _spec); err != nil { + return nil, err + } + if len(nodes) == 0 { + return nodes, nil + } + return nodes, nil +} + +func (_q *ContactRevealDailyLimitQuery) sqlCount(ctx context.Context) (int, error) { + _spec := _q.querySpec() + _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 *ContactRevealDailyLimitQuery) querySpec() *sqlgraph.QuerySpec { + _spec := sqlgraph.NewQuerySpec(contactrevealdailylimit.Table, contactrevealdailylimit.Columns, sqlgraph.NewFieldSpec(contactrevealdailylimit.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, contactrevealdailylimit.FieldID) + for i := range fields { + if fields[i] != contactrevealdailylimit.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 *ContactRevealDailyLimitQuery) sqlQuery(ctx context.Context) *sql.Selector { + builder := sql.Dialect(_q.driver.Dialect()) + t1 := builder.Table(contactrevealdailylimit.Table) + columns := _q.ctx.Fields + if len(columns) == 0 { + columns = contactrevealdailylimit.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 _, 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 +} + +// ContactRevealDailyLimitGroupBy is the group-by builder for ContactRevealDailyLimit entities. +type ContactRevealDailyLimitGroupBy struct { + selector + build *ContactRevealDailyLimitQuery +} + +// Aggregate adds the given aggregation functions to the group-by query. +func (_g *ContactRevealDailyLimitGroupBy) Aggregate(fns ...AggregateFunc) *ContactRevealDailyLimitGroupBy { + _g.fns = append(_g.fns, fns...) + return _g +} + +// Scan applies the selector query and scans the result into the given value. +func (_g *ContactRevealDailyLimitGroupBy) 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[*ContactRevealDailyLimitQuery, *ContactRevealDailyLimitGroupBy](ctx, _g.build, _g, _g.build.inters, v) +} + +func (_g *ContactRevealDailyLimitGroupBy) sqlScan(ctx context.Context, root *ContactRevealDailyLimitQuery, 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) +} + +// ContactRevealDailyLimitSelect is the builder for selecting fields of ContactRevealDailyLimit entities. +type ContactRevealDailyLimitSelect struct { + *ContactRevealDailyLimitQuery + selector +} + +// Aggregate adds the given aggregation functions to the selector query. +func (_s *ContactRevealDailyLimitSelect) Aggregate(fns ...AggregateFunc) *ContactRevealDailyLimitSelect { + _s.fns = append(_s.fns, fns...) + return _s +} + +// Scan applies the selector query and scans the result into the given value. +func (_s *ContactRevealDailyLimitSelect) 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[*ContactRevealDailyLimitQuery, *ContactRevealDailyLimitSelect](ctx, _s.ContactRevealDailyLimitQuery, _s, _s.inters, v) +} + +func (_s *ContactRevealDailyLimitSelect) sqlScan(ctx context.Context, root *ContactRevealDailyLimitQuery, 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/backend/ent/contactrevealdailylimit_update.go b/backend/ent/contactrevealdailylimit_update.go new file mode 100644 index 0000000..6626023 --- /dev/null +++ b/backend/ent/contactrevealdailylimit_update.go @@ -0,0 +1,292 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "context" + "errors" + "fmt" + "time" + + "entgo.io/ent/dialect/sql" + "entgo.io/ent/dialect/sql/sqlgraph" + "entgo.io/ent/schema/field" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/contactrevealdailylimit" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/predicate" +) + +// ContactRevealDailyLimitUpdate is the builder for updating ContactRevealDailyLimit entities. +type ContactRevealDailyLimitUpdate struct { + config + hooks []Hook + mutation *ContactRevealDailyLimitMutation +} + +// Where appends a list predicates to the ContactRevealDailyLimitUpdate builder. +func (_u *ContactRevealDailyLimitUpdate) Where(ps ...predicate.ContactRevealDailyLimit) *ContactRevealDailyLimitUpdate { + _u.mutation.Where(ps...) + return _u +} + +// SetSuccessfulCount sets the "successful_count" field. +func (_u *ContactRevealDailyLimitUpdate) SetSuccessfulCount(v int) *ContactRevealDailyLimitUpdate { + _u.mutation.ResetSuccessfulCount() + _u.mutation.SetSuccessfulCount(v) + return _u +} + +// SetNillableSuccessfulCount sets the "successful_count" field if the given value is not nil. +func (_u *ContactRevealDailyLimitUpdate) SetNillableSuccessfulCount(v *int) *ContactRevealDailyLimitUpdate { + if v != nil { + _u.SetSuccessfulCount(*v) + } + return _u +} + +// AddSuccessfulCount adds value to the "successful_count" field. +func (_u *ContactRevealDailyLimitUpdate) AddSuccessfulCount(v int) *ContactRevealDailyLimitUpdate { + _u.mutation.AddSuccessfulCount(v) + return _u +} + +// SetUpdatedAt sets the "updated_at" field. +func (_u *ContactRevealDailyLimitUpdate) SetUpdatedAt(v time.Time) *ContactRevealDailyLimitUpdate { + _u.mutation.SetUpdatedAt(v) + return _u +} + +// Mutation returns the ContactRevealDailyLimitMutation object of the builder. +func (_u *ContactRevealDailyLimitUpdate) Mutation() *ContactRevealDailyLimitMutation { + return _u.mutation +} + +// Save executes the query and returns the number of nodes affected by the update operation. +func (_u *ContactRevealDailyLimitUpdate) Save(ctx context.Context) (int, error) { + _u.defaults() + return withHooks(ctx, _u.sqlSave, _u.mutation, _u.hooks) +} + +// SaveX is like Save, but panics if an error occurs. +func (_u *ContactRevealDailyLimitUpdate) SaveX(ctx context.Context) int { + affected, err := _u.Save(ctx) + if err != nil { + panic(err) + } + return affected +} + +// Exec executes the query. +func (_u *ContactRevealDailyLimitUpdate) Exec(ctx context.Context) error { + _, err := _u.Save(ctx) + return err +} + +// ExecX is like Exec, but panics if an error occurs. +func (_u *ContactRevealDailyLimitUpdate) ExecX(ctx context.Context) { + if err := _u.Exec(ctx); err != nil { + panic(err) + } +} + +// defaults sets the default values of the builder before save. +func (_u *ContactRevealDailyLimitUpdate) defaults() { + if _, ok := _u.mutation.UpdatedAt(); !ok { + v := contactrevealdailylimit.UpdateDefaultUpdatedAt() + _u.mutation.SetUpdatedAt(v) + } +} + +// check runs all checks and user-defined validators on the builder. +func (_u *ContactRevealDailyLimitUpdate) check() error { + if v, ok := _u.mutation.SuccessfulCount(); ok { + if err := contactrevealdailylimit.SuccessfulCountValidator(v); err != nil { + return &ValidationError{Name: "successful_count", err: fmt.Errorf(`ent: validator failed for field "ContactRevealDailyLimit.successful_count": %w`, err)} + } + } + return nil +} + +func (_u *ContactRevealDailyLimitUpdate) sqlSave(ctx context.Context) (_node int, err error) { + if err := _u.check(); err != nil { + return _node, err + } + _spec := sqlgraph.NewUpdateSpec(contactrevealdailylimit.Table, contactrevealdailylimit.Columns, sqlgraph.NewFieldSpec(contactrevealdailylimit.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.SuccessfulCount(); ok { + _spec.SetField(contactrevealdailylimit.FieldSuccessfulCount, field.TypeInt, value) + } + if value, ok := _u.mutation.AddedSuccessfulCount(); ok { + _spec.AddField(contactrevealdailylimit.FieldSuccessfulCount, field.TypeInt, value) + } + if value, ok := _u.mutation.UpdatedAt(); ok { + _spec.SetField(contactrevealdailylimit.FieldUpdatedAt, field.TypeTime, value) + } + if _node, err = sqlgraph.UpdateNodes(ctx, _u.driver, _spec); err != nil { + if _, ok := err.(*sqlgraph.NotFoundError); ok { + err = &NotFoundError{contactrevealdailylimit.Label} + } else if sqlgraph.IsConstraintError(err) { + err = &ConstraintError{msg: err.Error(), wrap: err} + } + return 0, err + } + _u.mutation.done = true + return _node, nil +} + +// ContactRevealDailyLimitUpdateOne is the builder for updating a single ContactRevealDailyLimit entity. +type ContactRevealDailyLimitUpdateOne struct { + config + fields []string + hooks []Hook + mutation *ContactRevealDailyLimitMutation +} + +// SetSuccessfulCount sets the "successful_count" field. +func (_u *ContactRevealDailyLimitUpdateOne) SetSuccessfulCount(v int) *ContactRevealDailyLimitUpdateOne { + _u.mutation.ResetSuccessfulCount() + _u.mutation.SetSuccessfulCount(v) + return _u +} + +// SetNillableSuccessfulCount sets the "successful_count" field if the given value is not nil. +func (_u *ContactRevealDailyLimitUpdateOne) SetNillableSuccessfulCount(v *int) *ContactRevealDailyLimitUpdateOne { + if v != nil { + _u.SetSuccessfulCount(*v) + } + return _u +} + +// AddSuccessfulCount adds value to the "successful_count" field. +func (_u *ContactRevealDailyLimitUpdateOne) AddSuccessfulCount(v int) *ContactRevealDailyLimitUpdateOne { + _u.mutation.AddSuccessfulCount(v) + return _u +} + +// SetUpdatedAt sets the "updated_at" field. +func (_u *ContactRevealDailyLimitUpdateOne) SetUpdatedAt(v time.Time) *ContactRevealDailyLimitUpdateOne { + _u.mutation.SetUpdatedAt(v) + return _u +} + +// Mutation returns the ContactRevealDailyLimitMutation object of the builder. +func (_u *ContactRevealDailyLimitUpdateOne) Mutation() *ContactRevealDailyLimitMutation { + return _u.mutation +} + +// Where appends a list predicates to the ContactRevealDailyLimitUpdate builder. +func (_u *ContactRevealDailyLimitUpdateOne) Where(ps ...predicate.ContactRevealDailyLimit) *ContactRevealDailyLimitUpdateOne { + _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 *ContactRevealDailyLimitUpdateOne) Select(field string, fields ...string) *ContactRevealDailyLimitUpdateOne { + _u.fields = append([]string{field}, fields...) + return _u +} + +// Save executes the query and returns the updated ContactRevealDailyLimit entity. +func (_u *ContactRevealDailyLimitUpdateOne) Save(ctx context.Context) (*ContactRevealDailyLimit, error) { + _u.defaults() + return withHooks(ctx, _u.sqlSave, _u.mutation, _u.hooks) +} + +// SaveX is like Save, but panics if an error occurs. +func (_u *ContactRevealDailyLimitUpdateOne) SaveX(ctx context.Context) *ContactRevealDailyLimit { + node, err := _u.Save(ctx) + if err != nil { + panic(err) + } + return node +} + +// Exec executes the query on the entity. +func (_u *ContactRevealDailyLimitUpdateOne) Exec(ctx context.Context) error { + _, err := _u.Save(ctx) + return err +} + +// ExecX is like Exec, but panics if an error occurs. +func (_u *ContactRevealDailyLimitUpdateOne) ExecX(ctx context.Context) { + if err := _u.Exec(ctx); err != nil { + panic(err) + } +} + +// defaults sets the default values of the builder before save. +func (_u *ContactRevealDailyLimitUpdateOne) defaults() { + if _, ok := _u.mutation.UpdatedAt(); !ok { + v := contactrevealdailylimit.UpdateDefaultUpdatedAt() + _u.mutation.SetUpdatedAt(v) + } +} + +// check runs all checks and user-defined validators on the builder. +func (_u *ContactRevealDailyLimitUpdateOne) check() error { + if v, ok := _u.mutation.SuccessfulCount(); ok { + if err := contactrevealdailylimit.SuccessfulCountValidator(v); err != nil { + return &ValidationError{Name: "successful_count", err: fmt.Errorf(`ent: validator failed for field "ContactRevealDailyLimit.successful_count": %w`, err)} + } + } + return nil +} + +func (_u *ContactRevealDailyLimitUpdateOne) sqlSave(ctx context.Context) (_node *ContactRevealDailyLimit, err error) { + if err := _u.check(); err != nil { + return _node, err + } + _spec := sqlgraph.NewUpdateSpec(contactrevealdailylimit.Table, contactrevealdailylimit.Columns, sqlgraph.NewFieldSpec(contactrevealdailylimit.FieldID, field.TypeUUID)) + id, ok := _u.mutation.ID() + if !ok { + return nil, &ValidationError{Name: "id", err: errors.New(`ent: missing "ContactRevealDailyLimit.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, contactrevealdailylimit.FieldID) + for _, f := range fields { + if !contactrevealdailylimit.ValidColumn(f) { + return nil, &ValidationError{Name: f, err: fmt.Errorf("ent: invalid field %q for query", f)} + } + if f != contactrevealdailylimit.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.SuccessfulCount(); ok { + _spec.SetField(contactrevealdailylimit.FieldSuccessfulCount, field.TypeInt, value) + } + if value, ok := _u.mutation.AddedSuccessfulCount(); ok { + _spec.AddField(contactrevealdailylimit.FieldSuccessfulCount, field.TypeInt, value) + } + if value, ok := _u.mutation.UpdatedAt(); ok { + _spec.SetField(contactrevealdailylimit.FieldUpdatedAt, field.TypeTime, value) + } + _node = &ContactRevealDailyLimit{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{contactrevealdailylimit.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/backend/ent/contactrevealevent.go b/backend/ent/contactrevealevent.go new file mode 100644 index 0000000..758c61e --- /dev/null +++ b/backend/ent/contactrevealevent.go @@ -0,0 +1,162 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "fmt" + "strings" + "time" + + "entgo.io/ent" + "entgo.io/ent/dialect/sql" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/contactrevealevent" + "github.com/google/uuid" +) + +// ContactRevealEvent is the model entity for the ContactRevealEvent schema. +type ContactRevealEvent struct { + config `json:"-"` + // ID of the ent. + ID uuid.UUID `json:"id,omitempty"` + // CustomerInternalUserID holds the value of the "customer_internal_user_id" field. + CustomerInternalUserID uuid.UUID `json:"customer_internal_user_id,omitempty"` + // ProviderInternalUserID holds the value of the "provider_internal_user_id" field. + ProviderInternalUserID uuid.UUID `json:"provider_internal_user_id,omitempty"` + // ListingID holds the value of the "listing_id" field. + ListingID uuid.UUID `json:"listing_id,omitempty"` + // Channel holds the value of the "channel" field. + Channel contactrevealevent.Channel `json:"channel,omitempty"` + // UtcDay holds the value of the "utc_day" field. + UtcDay time.Time `json:"utc_day,omitempty"` + // RevealedAt holds the value of the "revealed_at" field. + RevealedAt time.Time `json:"revealed_at,omitempty"` + selectValues sql.SelectValues +} + +// scanValues returns the types for scanning values from sql.Rows. +func (*ContactRevealEvent) scanValues(columns []string) ([]any, error) { + values := make([]any, len(columns)) + for i := range columns { + switch columns[i] { + case contactrevealevent.FieldChannel: + values[i] = new(sql.NullString) + case contactrevealevent.FieldUtcDay, contactrevealevent.FieldRevealedAt: + values[i] = new(sql.NullTime) + case contactrevealevent.FieldID, contactrevealevent.FieldCustomerInternalUserID, contactrevealevent.FieldProviderInternalUserID, contactrevealevent.FieldListingID: + 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 ContactRevealEvent fields. +func (_m *ContactRevealEvent) 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 contactrevealevent.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 contactrevealevent.FieldCustomerInternalUserID: + if value, ok := values[i].(*uuid.UUID); !ok { + return fmt.Errorf("unexpected type %T for field customer_internal_user_id", values[i]) + } else if value != nil { + _m.CustomerInternalUserID = *value + } + case contactrevealevent.FieldProviderInternalUserID: + if value, ok := values[i].(*uuid.UUID); !ok { + return fmt.Errorf("unexpected type %T for field provider_internal_user_id", values[i]) + } else if value != nil { + _m.ProviderInternalUserID = *value + } + case contactrevealevent.FieldListingID: + if value, ok := values[i].(*uuid.UUID); !ok { + return fmt.Errorf("unexpected type %T for field listing_id", values[i]) + } else if value != nil { + _m.ListingID = *value + } + case contactrevealevent.FieldChannel: + if value, ok := values[i].(*sql.NullString); !ok { + return fmt.Errorf("unexpected type %T for field channel", values[i]) + } else if value.Valid { + _m.Channel = contactrevealevent.Channel(value.String) + } + case contactrevealevent.FieldUtcDay: + if value, ok := values[i].(*sql.NullTime); !ok { + return fmt.Errorf("unexpected type %T for field utc_day", values[i]) + } else if value.Valid { + _m.UtcDay = value.Time + } + case contactrevealevent.FieldRevealedAt: + if value, ok := values[i].(*sql.NullTime); !ok { + return fmt.Errorf("unexpected type %T for field revealed_at", values[i]) + } else if value.Valid { + _m.RevealedAt = value.Time + } + default: + _m.selectValues.Set(columns[i], values[i]) + } + } + return nil +} + +// Value returns the ent.Value that was dynamically selected and assigned to the ContactRevealEvent. +// This includes values selected through modifiers, order, etc. +func (_m *ContactRevealEvent) Value(name string) (ent.Value, error) { + return _m.selectValues.Get(name) +} + +// Update returns a builder for updating this ContactRevealEvent. +// Note that you need to call ContactRevealEvent.Unwrap() before calling this method if this ContactRevealEvent +// was returned from a transaction, and the transaction was committed or rolled back. +func (_m *ContactRevealEvent) Update() *ContactRevealEventUpdateOne { + return NewContactRevealEventClient(_m.config).UpdateOne(_m) +} + +// Unwrap unwraps the ContactRevealEvent 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 *ContactRevealEvent) Unwrap() *ContactRevealEvent { + _tx, ok := _m.config.driver.(*txDriver) + if !ok { + panic("ent: ContactRevealEvent is not a transactional entity") + } + _m.config.driver = _tx.drv + return _m +} + +// String implements the fmt.Stringer. +func (_m *ContactRevealEvent) String() string { + var builder strings.Builder + builder.WriteString("ContactRevealEvent(") + builder.WriteString(fmt.Sprintf("id=%v, ", _m.ID)) + builder.WriteString("customer_internal_user_id=") + builder.WriteString(fmt.Sprintf("%v", _m.CustomerInternalUserID)) + builder.WriteString(", ") + builder.WriteString("provider_internal_user_id=") + builder.WriteString(fmt.Sprintf("%v", _m.ProviderInternalUserID)) + builder.WriteString(", ") + builder.WriteString("listing_id=") + builder.WriteString(fmt.Sprintf("%v", _m.ListingID)) + builder.WriteString(", ") + builder.WriteString("channel=") + builder.WriteString(fmt.Sprintf("%v", _m.Channel)) + builder.WriteString(", ") + builder.WriteString("utc_day=") + builder.WriteString(_m.UtcDay.Format(time.ANSIC)) + builder.WriteString(", ") + builder.WriteString("revealed_at=") + builder.WriteString(_m.RevealedAt.Format(time.ANSIC)) + builder.WriteByte(')') + return builder.String() +} + +// ContactRevealEvents is a parsable slice of ContactRevealEvent. +type ContactRevealEvents []*ContactRevealEvent diff --git a/backend/ent/contactrevealevent/contactrevealevent.go b/backend/ent/contactrevealevent/contactrevealevent.go new file mode 100644 index 0000000..1568274 --- /dev/null +++ b/backend/ent/contactrevealevent/contactrevealevent.go @@ -0,0 +1,121 @@ +// Code generated by ent, DO NOT EDIT. + +package contactrevealevent + +import ( + "fmt" + "time" + + "entgo.io/ent/dialect/sql" + "github.com/google/uuid" +) + +const ( + // Label holds the string label denoting the contactrevealevent type in the database. + Label = "contact_reveal_event" + // FieldID holds the string denoting the id field in the database. + FieldID = "id" + // FieldCustomerInternalUserID holds the string denoting the customer_internal_user_id field in the database. + FieldCustomerInternalUserID = "customer_internal_user_id" + // FieldProviderInternalUserID holds the string denoting the provider_internal_user_id field in the database. + FieldProviderInternalUserID = "provider_internal_user_id" + // FieldListingID holds the string denoting the listing_id field in the database. + FieldListingID = "listing_id" + // FieldChannel holds the string denoting the channel field in the database. + FieldChannel = "channel" + // FieldUtcDay holds the string denoting the utc_day field in the database. + FieldUtcDay = "utc_day" + // FieldRevealedAt holds the string denoting the revealed_at field in the database. + FieldRevealedAt = "revealed_at" + // Table holds the table name of the contactrevealevent in the database. + Table = "contact_reveal_events" +) + +// Columns holds all SQL columns for contactrevealevent fields. +var Columns = []string{ + FieldID, + FieldCustomerInternalUserID, + FieldProviderInternalUserID, + FieldListingID, + FieldChannel, + FieldUtcDay, + FieldRevealedAt, +} + +// 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 ( + // DefaultRevealedAt holds the default value on creation for the "revealed_at" field. + DefaultRevealedAt func() time.Time + // DefaultID holds the default value on creation for the "id" field. + DefaultID func() uuid.UUID +) + +// Channel defines the type for the "channel" enum field. +type Channel string + +// Channel values. +const ( + ChannelPhone Channel = "phone" + ChannelWhatsapp Channel = "whatsapp" +) + +func (c Channel) String() string { + return string(c) +} + +// ChannelValidator is a validator for the "channel" field enum values. It is called by the builders before save. +func ChannelValidator(c Channel) error { + switch c { + case ChannelPhone, ChannelWhatsapp: + return nil + default: + return fmt.Errorf("contactrevealevent: invalid enum value for channel field: %q", c) + } +} + +// OrderOption defines the ordering options for the ContactRevealEvent 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() +} + +// ByCustomerInternalUserID orders the results by the customer_internal_user_id field. +func ByCustomerInternalUserID(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldCustomerInternalUserID, opts...).ToFunc() +} + +// ByProviderInternalUserID orders the results by the provider_internal_user_id field. +func ByProviderInternalUserID(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldProviderInternalUserID, opts...).ToFunc() +} + +// ByListingID orders the results by the listing_id field. +func ByListingID(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldListingID, opts...).ToFunc() +} + +// ByChannel orders the results by the channel field. +func ByChannel(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldChannel, opts...).ToFunc() +} + +// ByUtcDay orders the results by the utc_day field. +func ByUtcDay(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldUtcDay, opts...).ToFunc() +} + +// ByRevealedAt orders the results by the revealed_at field. +func ByRevealedAt(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldRevealedAt, opts...).ToFunc() +} diff --git a/backend/ent/contactrevealevent/where.go b/backend/ent/contactrevealevent/where.go new file mode 100644 index 0000000..acd9075 --- /dev/null +++ b/backend/ent/contactrevealevent/where.go @@ -0,0 +1,316 @@ +// Code generated by ent, DO NOT EDIT. + +package contactrevealevent + +import ( + "time" + + "entgo.io/ent/dialect/sql" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/predicate" + "github.com/google/uuid" +) + +// ID filters vertices based on their ID field. +func ID(id uuid.UUID) predicate.ContactRevealEvent { + return predicate.ContactRevealEvent(sql.FieldEQ(FieldID, id)) +} + +// IDEQ applies the EQ predicate on the ID field. +func IDEQ(id uuid.UUID) predicate.ContactRevealEvent { + return predicate.ContactRevealEvent(sql.FieldEQ(FieldID, id)) +} + +// IDNEQ applies the NEQ predicate on the ID field. +func IDNEQ(id uuid.UUID) predicate.ContactRevealEvent { + return predicate.ContactRevealEvent(sql.FieldNEQ(FieldID, id)) +} + +// IDIn applies the In predicate on the ID field. +func IDIn(ids ...uuid.UUID) predicate.ContactRevealEvent { + return predicate.ContactRevealEvent(sql.FieldIn(FieldID, ids...)) +} + +// IDNotIn applies the NotIn predicate on the ID field. +func IDNotIn(ids ...uuid.UUID) predicate.ContactRevealEvent { + return predicate.ContactRevealEvent(sql.FieldNotIn(FieldID, ids...)) +} + +// IDGT applies the GT predicate on the ID field. +func IDGT(id uuid.UUID) predicate.ContactRevealEvent { + return predicate.ContactRevealEvent(sql.FieldGT(FieldID, id)) +} + +// IDGTE applies the GTE predicate on the ID field. +func IDGTE(id uuid.UUID) predicate.ContactRevealEvent { + return predicate.ContactRevealEvent(sql.FieldGTE(FieldID, id)) +} + +// IDLT applies the LT predicate on the ID field. +func IDLT(id uuid.UUID) predicate.ContactRevealEvent { + return predicate.ContactRevealEvent(sql.FieldLT(FieldID, id)) +} + +// IDLTE applies the LTE predicate on the ID field. +func IDLTE(id uuid.UUID) predicate.ContactRevealEvent { + return predicate.ContactRevealEvent(sql.FieldLTE(FieldID, id)) +} + +// CustomerInternalUserID applies equality check predicate on the "customer_internal_user_id" field. It's identical to CustomerInternalUserIDEQ. +func CustomerInternalUserID(v uuid.UUID) predicate.ContactRevealEvent { + return predicate.ContactRevealEvent(sql.FieldEQ(FieldCustomerInternalUserID, v)) +} + +// ProviderInternalUserID applies equality check predicate on the "provider_internal_user_id" field. It's identical to ProviderInternalUserIDEQ. +func ProviderInternalUserID(v uuid.UUID) predicate.ContactRevealEvent { + return predicate.ContactRevealEvent(sql.FieldEQ(FieldProviderInternalUserID, v)) +} + +// ListingID applies equality check predicate on the "listing_id" field. It's identical to ListingIDEQ. +func ListingID(v uuid.UUID) predicate.ContactRevealEvent { + return predicate.ContactRevealEvent(sql.FieldEQ(FieldListingID, v)) +} + +// UtcDay applies equality check predicate on the "utc_day" field. It's identical to UtcDayEQ. +func UtcDay(v time.Time) predicate.ContactRevealEvent { + return predicate.ContactRevealEvent(sql.FieldEQ(FieldUtcDay, v)) +} + +// RevealedAt applies equality check predicate on the "revealed_at" field. It's identical to RevealedAtEQ. +func RevealedAt(v time.Time) predicate.ContactRevealEvent { + return predicate.ContactRevealEvent(sql.FieldEQ(FieldRevealedAt, v)) +} + +// CustomerInternalUserIDEQ applies the EQ predicate on the "customer_internal_user_id" field. +func CustomerInternalUserIDEQ(v uuid.UUID) predicate.ContactRevealEvent { + return predicate.ContactRevealEvent(sql.FieldEQ(FieldCustomerInternalUserID, v)) +} + +// CustomerInternalUserIDNEQ applies the NEQ predicate on the "customer_internal_user_id" field. +func CustomerInternalUserIDNEQ(v uuid.UUID) predicate.ContactRevealEvent { + return predicate.ContactRevealEvent(sql.FieldNEQ(FieldCustomerInternalUserID, v)) +} + +// CustomerInternalUserIDIn applies the In predicate on the "customer_internal_user_id" field. +func CustomerInternalUserIDIn(vs ...uuid.UUID) predicate.ContactRevealEvent { + return predicate.ContactRevealEvent(sql.FieldIn(FieldCustomerInternalUserID, vs...)) +} + +// CustomerInternalUserIDNotIn applies the NotIn predicate on the "customer_internal_user_id" field. +func CustomerInternalUserIDNotIn(vs ...uuid.UUID) predicate.ContactRevealEvent { + return predicate.ContactRevealEvent(sql.FieldNotIn(FieldCustomerInternalUserID, vs...)) +} + +// CustomerInternalUserIDGT applies the GT predicate on the "customer_internal_user_id" field. +func CustomerInternalUserIDGT(v uuid.UUID) predicate.ContactRevealEvent { + return predicate.ContactRevealEvent(sql.FieldGT(FieldCustomerInternalUserID, v)) +} + +// CustomerInternalUserIDGTE applies the GTE predicate on the "customer_internal_user_id" field. +func CustomerInternalUserIDGTE(v uuid.UUID) predicate.ContactRevealEvent { + return predicate.ContactRevealEvent(sql.FieldGTE(FieldCustomerInternalUserID, v)) +} + +// CustomerInternalUserIDLT applies the LT predicate on the "customer_internal_user_id" field. +func CustomerInternalUserIDLT(v uuid.UUID) predicate.ContactRevealEvent { + return predicate.ContactRevealEvent(sql.FieldLT(FieldCustomerInternalUserID, v)) +} + +// CustomerInternalUserIDLTE applies the LTE predicate on the "customer_internal_user_id" field. +func CustomerInternalUserIDLTE(v uuid.UUID) predicate.ContactRevealEvent { + return predicate.ContactRevealEvent(sql.FieldLTE(FieldCustomerInternalUserID, v)) +} + +// ProviderInternalUserIDEQ applies the EQ predicate on the "provider_internal_user_id" field. +func ProviderInternalUserIDEQ(v uuid.UUID) predicate.ContactRevealEvent { + return predicate.ContactRevealEvent(sql.FieldEQ(FieldProviderInternalUserID, v)) +} + +// ProviderInternalUserIDNEQ applies the NEQ predicate on the "provider_internal_user_id" field. +func ProviderInternalUserIDNEQ(v uuid.UUID) predicate.ContactRevealEvent { + return predicate.ContactRevealEvent(sql.FieldNEQ(FieldProviderInternalUserID, v)) +} + +// ProviderInternalUserIDIn applies the In predicate on the "provider_internal_user_id" field. +func ProviderInternalUserIDIn(vs ...uuid.UUID) predicate.ContactRevealEvent { + return predicate.ContactRevealEvent(sql.FieldIn(FieldProviderInternalUserID, vs...)) +} + +// ProviderInternalUserIDNotIn applies the NotIn predicate on the "provider_internal_user_id" field. +func ProviderInternalUserIDNotIn(vs ...uuid.UUID) predicate.ContactRevealEvent { + return predicate.ContactRevealEvent(sql.FieldNotIn(FieldProviderInternalUserID, vs...)) +} + +// ProviderInternalUserIDGT applies the GT predicate on the "provider_internal_user_id" field. +func ProviderInternalUserIDGT(v uuid.UUID) predicate.ContactRevealEvent { + return predicate.ContactRevealEvent(sql.FieldGT(FieldProviderInternalUserID, v)) +} + +// ProviderInternalUserIDGTE applies the GTE predicate on the "provider_internal_user_id" field. +func ProviderInternalUserIDGTE(v uuid.UUID) predicate.ContactRevealEvent { + return predicate.ContactRevealEvent(sql.FieldGTE(FieldProviderInternalUserID, v)) +} + +// ProviderInternalUserIDLT applies the LT predicate on the "provider_internal_user_id" field. +func ProviderInternalUserIDLT(v uuid.UUID) predicate.ContactRevealEvent { + return predicate.ContactRevealEvent(sql.FieldLT(FieldProviderInternalUserID, v)) +} + +// ProviderInternalUserIDLTE applies the LTE predicate on the "provider_internal_user_id" field. +func ProviderInternalUserIDLTE(v uuid.UUID) predicate.ContactRevealEvent { + return predicate.ContactRevealEvent(sql.FieldLTE(FieldProviderInternalUserID, v)) +} + +// ListingIDEQ applies the EQ predicate on the "listing_id" field. +func ListingIDEQ(v uuid.UUID) predicate.ContactRevealEvent { + return predicate.ContactRevealEvent(sql.FieldEQ(FieldListingID, v)) +} + +// ListingIDNEQ applies the NEQ predicate on the "listing_id" field. +func ListingIDNEQ(v uuid.UUID) predicate.ContactRevealEvent { + return predicate.ContactRevealEvent(sql.FieldNEQ(FieldListingID, v)) +} + +// ListingIDIn applies the In predicate on the "listing_id" field. +func ListingIDIn(vs ...uuid.UUID) predicate.ContactRevealEvent { + return predicate.ContactRevealEvent(sql.FieldIn(FieldListingID, vs...)) +} + +// ListingIDNotIn applies the NotIn predicate on the "listing_id" field. +func ListingIDNotIn(vs ...uuid.UUID) predicate.ContactRevealEvent { + return predicate.ContactRevealEvent(sql.FieldNotIn(FieldListingID, vs...)) +} + +// ListingIDGT applies the GT predicate on the "listing_id" field. +func ListingIDGT(v uuid.UUID) predicate.ContactRevealEvent { + return predicate.ContactRevealEvent(sql.FieldGT(FieldListingID, v)) +} + +// ListingIDGTE applies the GTE predicate on the "listing_id" field. +func ListingIDGTE(v uuid.UUID) predicate.ContactRevealEvent { + return predicate.ContactRevealEvent(sql.FieldGTE(FieldListingID, v)) +} + +// ListingIDLT applies the LT predicate on the "listing_id" field. +func ListingIDLT(v uuid.UUID) predicate.ContactRevealEvent { + return predicate.ContactRevealEvent(sql.FieldLT(FieldListingID, v)) +} + +// ListingIDLTE applies the LTE predicate on the "listing_id" field. +func ListingIDLTE(v uuid.UUID) predicate.ContactRevealEvent { + return predicate.ContactRevealEvent(sql.FieldLTE(FieldListingID, v)) +} + +// ChannelEQ applies the EQ predicate on the "channel" field. +func ChannelEQ(v Channel) predicate.ContactRevealEvent { + return predicate.ContactRevealEvent(sql.FieldEQ(FieldChannel, v)) +} + +// ChannelNEQ applies the NEQ predicate on the "channel" field. +func ChannelNEQ(v Channel) predicate.ContactRevealEvent { + return predicate.ContactRevealEvent(sql.FieldNEQ(FieldChannel, v)) +} + +// ChannelIn applies the In predicate on the "channel" field. +func ChannelIn(vs ...Channel) predicate.ContactRevealEvent { + return predicate.ContactRevealEvent(sql.FieldIn(FieldChannel, vs...)) +} + +// ChannelNotIn applies the NotIn predicate on the "channel" field. +func ChannelNotIn(vs ...Channel) predicate.ContactRevealEvent { + return predicate.ContactRevealEvent(sql.FieldNotIn(FieldChannel, vs...)) +} + +// UtcDayEQ applies the EQ predicate on the "utc_day" field. +func UtcDayEQ(v time.Time) predicate.ContactRevealEvent { + return predicate.ContactRevealEvent(sql.FieldEQ(FieldUtcDay, v)) +} + +// UtcDayNEQ applies the NEQ predicate on the "utc_day" field. +func UtcDayNEQ(v time.Time) predicate.ContactRevealEvent { + return predicate.ContactRevealEvent(sql.FieldNEQ(FieldUtcDay, v)) +} + +// UtcDayIn applies the In predicate on the "utc_day" field. +func UtcDayIn(vs ...time.Time) predicate.ContactRevealEvent { + return predicate.ContactRevealEvent(sql.FieldIn(FieldUtcDay, vs...)) +} + +// UtcDayNotIn applies the NotIn predicate on the "utc_day" field. +func UtcDayNotIn(vs ...time.Time) predicate.ContactRevealEvent { + return predicate.ContactRevealEvent(sql.FieldNotIn(FieldUtcDay, vs...)) +} + +// UtcDayGT applies the GT predicate on the "utc_day" field. +func UtcDayGT(v time.Time) predicate.ContactRevealEvent { + return predicate.ContactRevealEvent(sql.FieldGT(FieldUtcDay, v)) +} + +// UtcDayGTE applies the GTE predicate on the "utc_day" field. +func UtcDayGTE(v time.Time) predicate.ContactRevealEvent { + return predicate.ContactRevealEvent(sql.FieldGTE(FieldUtcDay, v)) +} + +// UtcDayLT applies the LT predicate on the "utc_day" field. +func UtcDayLT(v time.Time) predicate.ContactRevealEvent { + return predicate.ContactRevealEvent(sql.FieldLT(FieldUtcDay, v)) +} + +// UtcDayLTE applies the LTE predicate on the "utc_day" field. +func UtcDayLTE(v time.Time) predicate.ContactRevealEvent { + return predicate.ContactRevealEvent(sql.FieldLTE(FieldUtcDay, v)) +} + +// RevealedAtEQ applies the EQ predicate on the "revealed_at" field. +func RevealedAtEQ(v time.Time) predicate.ContactRevealEvent { + return predicate.ContactRevealEvent(sql.FieldEQ(FieldRevealedAt, v)) +} + +// RevealedAtNEQ applies the NEQ predicate on the "revealed_at" field. +func RevealedAtNEQ(v time.Time) predicate.ContactRevealEvent { + return predicate.ContactRevealEvent(sql.FieldNEQ(FieldRevealedAt, v)) +} + +// RevealedAtIn applies the In predicate on the "revealed_at" field. +func RevealedAtIn(vs ...time.Time) predicate.ContactRevealEvent { + return predicate.ContactRevealEvent(sql.FieldIn(FieldRevealedAt, vs...)) +} + +// RevealedAtNotIn applies the NotIn predicate on the "revealed_at" field. +func RevealedAtNotIn(vs ...time.Time) predicate.ContactRevealEvent { + return predicate.ContactRevealEvent(sql.FieldNotIn(FieldRevealedAt, vs...)) +} + +// RevealedAtGT applies the GT predicate on the "revealed_at" field. +func RevealedAtGT(v time.Time) predicate.ContactRevealEvent { + return predicate.ContactRevealEvent(sql.FieldGT(FieldRevealedAt, v)) +} + +// RevealedAtGTE applies the GTE predicate on the "revealed_at" field. +func RevealedAtGTE(v time.Time) predicate.ContactRevealEvent { + return predicate.ContactRevealEvent(sql.FieldGTE(FieldRevealedAt, v)) +} + +// RevealedAtLT applies the LT predicate on the "revealed_at" field. +func RevealedAtLT(v time.Time) predicate.ContactRevealEvent { + return predicate.ContactRevealEvent(sql.FieldLT(FieldRevealedAt, v)) +} + +// RevealedAtLTE applies the LTE predicate on the "revealed_at" field. +func RevealedAtLTE(v time.Time) predicate.ContactRevealEvent { + return predicate.ContactRevealEvent(sql.FieldLTE(FieldRevealedAt, v)) +} + +// And groups predicates with the AND operator between them. +func And(predicates ...predicate.ContactRevealEvent) predicate.ContactRevealEvent { + return predicate.ContactRevealEvent(sql.AndPredicates(predicates...)) +} + +// Or groups predicates with the OR operator between them. +func Or(predicates ...predicate.ContactRevealEvent) predicate.ContactRevealEvent { + return predicate.ContactRevealEvent(sql.OrPredicates(predicates...)) +} + +// Not applies the not operator on the given predicate. +func Not(p predicate.ContactRevealEvent) predicate.ContactRevealEvent { + return predicate.ContactRevealEvent(sql.NotPredicates(p)) +} diff --git a/backend/ent/contactrevealevent_create.go b/backend/ent/contactrevealevent_create.go new file mode 100644 index 0000000..b1f6c5e --- /dev/null +++ b/backend/ent/contactrevealevent_create.go @@ -0,0 +1,296 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "context" + "errors" + "fmt" + "time" + + "entgo.io/ent/dialect/sql/sqlgraph" + "entgo.io/ent/schema/field" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/contactrevealevent" + "github.com/google/uuid" +) + +// ContactRevealEventCreate is the builder for creating a ContactRevealEvent entity. +type ContactRevealEventCreate struct { + config + mutation *ContactRevealEventMutation + hooks []Hook +} + +// SetCustomerInternalUserID sets the "customer_internal_user_id" field. +func (_c *ContactRevealEventCreate) SetCustomerInternalUserID(v uuid.UUID) *ContactRevealEventCreate { + _c.mutation.SetCustomerInternalUserID(v) + return _c +} + +// SetProviderInternalUserID sets the "provider_internal_user_id" field. +func (_c *ContactRevealEventCreate) SetProviderInternalUserID(v uuid.UUID) *ContactRevealEventCreate { + _c.mutation.SetProviderInternalUserID(v) + return _c +} + +// SetListingID sets the "listing_id" field. +func (_c *ContactRevealEventCreate) SetListingID(v uuid.UUID) *ContactRevealEventCreate { + _c.mutation.SetListingID(v) + return _c +} + +// SetChannel sets the "channel" field. +func (_c *ContactRevealEventCreate) SetChannel(v contactrevealevent.Channel) *ContactRevealEventCreate { + _c.mutation.SetChannel(v) + return _c +} + +// SetUtcDay sets the "utc_day" field. +func (_c *ContactRevealEventCreate) SetUtcDay(v time.Time) *ContactRevealEventCreate { + _c.mutation.SetUtcDay(v) + return _c +} + +// SetRevealedAt sets the "revealed_at" field. +func (_c *ContactRevealEventCreate) SetRevealedAt(v time.Time) *ContactRevealEventCreate { + _c.mutation.SetRevealedAt(v) + return _c +} + +// SetNillableRevealedAt sets the "revealed_at" field if the given value is not nil. +func (_c *ContactRevealEventCreate) SetNillableRevealedAt(v *time.Time) *ContactRevealEventCreate { + if v != nil { + _c.SetRevealedAt(*v) + } + return _c +} + +// SetID sets the "id" field. +func (_c *ContactRevealEventCreate) SetID(v uuid.UUID) *ContactRevealEventCreate { + _c.mutation.SetID(v) + return _c +} + +// SetNillableID sets the "id" field if the given value is not nil. +func (_c *ContactRevealEventCreate) SetNillableID(v *uuid.UUID) *ContactRevealEventCreate { + if v != nil { + _c.SetID(*v) + } + return _c +} + +// Mutation returns the ContactRevealEventMutation object of the builder. +func (_c *ContactRevealEventCreate) Mutation() *ContactRevealEventMutation { + return _c.mutation +} + +// Save creates the ContactRevealEvent in the database. +func (_c *ContactRevealEventCreate) Save(ctx context.Context) (*ContactRevealEvent, error) { + _c.defaults() + return withHooks(ctx, _c.sqlSave, _c.mutation, _c.hooks) +} + +// SaveX calls Save and panics if Save returns an error. +func (_c *ContactRevealEventCreate) SaveX(ctx context.Context) *ContactRevealEvent { + v, err := _c.Save(ctx) + if err != nil { + panic(err) + } + return v +} + +// Exec executes the query. +func (_c *ContactRevealEventCreate) Exec(ctx context.Context) error { + _, err := _c.Save(ctx) + return err +} + +// ExecX is like Exec, but panics if an error occurs. +func (_c *ContactRevealEventCreate) 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 *ContactRevealEventCreate) defaults() { + if _, ok := _c.mutation.RevealedAt(); !ok { + v := contactrevealevent.DefaultRevealedAt() + _c.mutation.SetRevealedAt(v) + } + if _, ok := _c.mutation.ID(); !ok { + v := contactrevealevent.DefaultID() + _c.mutation.SetID(v) + } +} + +// check runs all checks and user-defined validators on the builder. +func (_c *ContactRevealEventCreate) check() error { + if _, ok := _c.mutation.CustomerInternalUserID(); !ok { + return &ValidationError{Name: "customer_internal_user_id", err: errors.New(`ent: missing required field "ContactRevealEvent.customer_internal_user_id"`)} + } + if _, ok := _c.mutation.ProviderInternalUserID(); !ok { + return &ValidationError{Name: "provider_internal_user_id", err: errors.New(`ent: missing required field "ContactRevealEvent.provider_internal_user_id"`)} + } + if _, ok := _c.mutation.ListingID(); !ok { + return &ValidationError{Name: "listing_id", err: errors.New(`ent: missing required field "ContactRevealEvent.listing_id"`)} + } + if _, ok := _c.mutation.Channel(); !ok { + return &ValidationError{Name: "channel", err: errors.New(`ent: missing required field "ContactRevealEvent.channel"`)} + } + if v, ok := _c.mutation.Channel(); ok { + if err := contactrevealevent.ChannelValidator(v); err != nil { + return &ValidationError{Name: "channel", err: fmt.Errorf(`ent: validator failed for field "ContactRevealEvent.channel": %w`, err)} + } + } + if _, ok := _c.mutation.UtcDay(); !ok { + return &ValidationError{Name: "utc_day", err: errors.New(`ent: missing required field "ContactRevealEvent.utc_day"`)} + } + if _, ok := _c.mutation.RevealedAt(); !ok { + return &ValidationError{Name: "revealed_at", err: errors.New(`ent: missing required field "ContactRevealEvent.revealed_at"`)} + } + return nil +} + +func (_c *ContactRevealEventCreate) sqlSave(ctx context.Context) (*ContactRevealEvent, 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 *ContactRevealEventCreate) createSpec() (*ContactRevealEvent, *sqlgraph.CreateSpec) { + var ( + _node = &ContactRevealEvent{config: _c.config} + _spec = sqlgraph.NewCreateSpec(contactrevealevent.Table, sqlgraph.NewFieldSpec(contactrevealevent.FieldID, field.TypeUUID)) + ) + if id, ok := _c.mutation.ID(); ok { + _node.ID = id + _spec.ID.Value = &id + } + if value, ok := _c.mutation.CustomerInternalUserID(); ok { + _spec.SetField(contactrevealevent.FieldCustomerInternalUserID, field.TypeUUID, value) + _node.CustomerInternalUserID = value + } + if value, ok := _c.mutation.ProviderInternalUserID(); ok { + _spec.SetField(contactrevealevent.FieldProviderInternalUserID, field.TypeUUID, value) + _node.ProviderInternalUserID = value + } + if value, ok := _c.mutation.ListingID(); ok { + _spec.SetField(contactrevealevent.FieldListingID, field.TypeUUID, value) + _node.ListingID = value + } + if value, ok := _c.mutation.Channel(); ok { + _spec.SetField(contactrevealevent.FieldChannel, field.TypeEnum, value) + _node.Channel = value + } + if value, ok := _c.mutation.UtcDay(); ok { + _spec.SetField(contactrevealevent.FieldUtcDay, field.TypeTime, value) + _node.UtcDay = value + } + if value, ok := _c.mutation.RevealedAt(); ok { + _spec.SetField(contactrevealevent.FieldRevealedAt, field.TypeTime, value) + _node.RevealedAt = value + } + return _node, _spec +} + +// ContactRevealEventCreateBulk is the builder for creating many ContactRevealEvent entities in bulk. +type ContactRevealEventCreateBulk struct { + config + err error + builders []*ContactRevealEventCreate +} + +// Save creates the ContactRevealEvent entities in the database. +func (_c *ContactRevealEventCreateBulk) Save(ctx context.Context) ([]*ContactRevealEvent, error) { + if _c.err != nil { + return nil, _c.err + } + specs := make([]*sqlgraph.CreateSpec, len(_c.builders)) + nodes := make([]*ContactRevealEvent, 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.(*ContactRevealEventMutation) + 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 *ContactRevealEventCreateBulk) SaveX(ctx context.Context) []*ContactRevealEvent { + v, err := _c.Save(ctx) + if err != nil { + panic(err) + } + return v +} + +// Exec executes the query. +func (_c *ContactRevealEventCreateBulk) Exec(ctx context.Context) error { + _, err := _c.Save(ctx) + return err +} + +// ExecX is like Exec, but panics if an error occurs. +func (_c *ContactRevealEventCreateBulk) ExecX(ctx context.Context) { + if err := _c.Exec(ctx); err != nil { + panic(err) + } +} diff --git a/backend/ent/contactrevealevent_delete.go b/backend/ent/contactrevealevent_delete.go new file mode 100644 index 0000000..b367ff3 --- /dev/null +++ b/backend/ent/contactrevealevent_delete.go @@ -0,0 +1,88 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "context" + + "entgo.io/ent/dialect/sql" + "entgo.io/ent/dialect/sql/sqlgraph" + "entgo.io/ent/schema/field" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/contactrevealevent" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/predicate" +) + +// ContactRevealEventDelete is the builder for deleting a ContactRevealEvent entity. +type ContactRevealEventDelete struct { + config + hooks []Hook + mutation *ContactRevealEventMutation +} + +// Where appends a list predicates to the ContactRevealEventDelete builder. +func (_d *ContactRevealEventDelete) Where(ps ...predicate.ContactRevealEvent) *ContactRevealEventDelete { + _d.mutation.Where(ps...) + return _d +} + +// Exec executes the deletion query and returns how many vertices were deleted. +func (_d *ContactRevealEventDelete) 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 *ContactRevealEventDelete) ExecX(ctx context.Context) int { + n, err := _d.Exec(ctx) + if err != nil { + panic(err) + } + return n +} + +func (_d *ContactRevealEventDelete) sqlExec(ctx context.Context) (int, error) { + _spec := sqlgraph.NewDeleteSpec(contactrevealevent.Table, sqlgraph.NewFieldSpec(contactrevealevent.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 +} + +// ContactRevealEventDeleteOne is the builder for deleting a single ContactRevealEvent entity. +type ContactRevealEventDeleteOne struct { + _d *ContactRevealEventDelete +} + +// Where appends a list predicates to the ContactRevealEventDelete builder. +func (_d *ContactRevealEventDeleteOne) Where(ps ...predicate.ContactRevealEvent) *ContactRevealEventDeleteOne { + _d._d.mutation.Where(ps...) + return _d +} + +// Exec executes the deletion query. +func (_d *ContactRevealEventDeleteOne) Exec(ctx context.Context) error { + n, err := _d._d.Exec(ctx) + switch { + case err != nil: + return err + case n == 0: + return &NotFoundError{contactrevealevent.Label} + default: + return nil + } +} + +// ExecX is like Exec, but panics if an error occurs. +func (_d *ContactRevealEventDeleteOne) ExecX(ctx context.Context) { + if err := _d.Exec(ctx); err != nil { + panic(err) + } +} diff --git a/backend/ent/contactrevealevent_query.go b/backend/ent/contactrevealevent_query.go new file mode 100644 index 0000000..e79ce56 --- /dev/null +++ b/backend/ent/contactrevealevent_query.go @@ -0,0 +1,528 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "context" + "fmt" + "math" + + "entgo.io/ent" + "entgo.io/ent/dialect/sql" + "entgo.io/ent/dialect/sql/sqlgraph" + "entgo.io/ent/schema/field" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/contactrevealevent" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/predicate" + "github.com/google/uuid" +) + +// ContactRevealEventQuery is the builder for querying ContactRevealEvent entities. +type ContactRevealEventQuery struct { + config + ctx *QueryContext + order []contactrevealevent.OrderOption + inters []Interceptor + predicates []predicate.ContactRevealEvent + // intermediate query (i.e. traversal path). + sql *sql.Selector + path func(context.Context) (*sql.Selector, error) +} + +// Where adds a new predicate for the ContactRevealEventQuery builder. +func (_q *ContactRevealEventQuery) Where(ps ...predicate.ContactRevealEvent) *ContactRevealEventQuery { + _q.predicates = append(_q.predicates, ps...) + return _q +} + +// Limit the number of records to be returned by this query. +func (_q *ContactRevealEventQuery) Limit(limit int) *ContactRevealEventQuery { + _q.ctx.Limit = &limit + return _q +} + +// Offset to start from. +func (_q *ContactRevealEventQuery) Offset(offset int) *ContactRevealEventQuery { + _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 *ContactRevealEventQuery) Unique(unique bool) *ContactRevealEventQuery { + _q.ctx.Unique = &unique + return _q +} + +// Order specifies how the records should be ordered. +func (_q *ContactRevealEventQuery) Order(o ...contactrevealevent.OrderOption) *ContactRevealEventQuery { + _q.order = append(_q.order, o...) + return _q +} + +// First returns the first ContactRevealEvent entity from the query. +// Returns a *NotFoundError when no ContactRevealEvent was found. +func (_q *ContactRevealEventQuery) First(ctx context.Context) (*ContactRevealEvent, 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{contactrevealevent.Label} + } + return nodes[0], nil +} + +// FirstX is like First, but panics if an error occurs. +func (_q *ContactRevealEventQuery) FirstX(ctx context.Context) *ContactRevealEvent { + node, err := _q.First(ctx) + if err != nil && !IsNotFound(err) { + panic(err) + } + return node +} + +// FirstID returns the first ContactRevealEvent ID from the query. +// Returns a *NotFoundError when no ContactRevealEvent ID was found. +func (_q *ContactRevealEventQuery) 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{contactrevealevent.Label} + return + } + return ids[0], nil +} + +// FirstIDX is like FirstID, but panics if an error occurs. +func (_q *ContactRevealEventQuery) FirstIDX(ctx context.Context) uuid.UUID { + id, err := _q.FirstID(ctx) + if err != nil && !IsNotFound(err) { + panic(err) + } + return id +} + +// Only returns a single ContactRevealEvent entity found by the query, ensuring it only returns one. +// Returns a *NotSingularError when more than one ContactRevealEvent entity is found. +// Returns a *NotFoundError when no ContactRevealEvent entities are found. +func (_q *ContactRevealEventQuery) Only(ctx context.Context) (*ContactRevealEvent, 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{contactrevealevent.Label} + default: + return nil, &NotSingularError{contactrevealevent.Label} + } +} + +// OnlyX is like Only, but panics if an error occurs. +func (_q *ContactRevealEventQuery) OnlyX(ctx context.Context) *ContactRevealEvent { + node, err := _q.Only(ctx) + if err != nil { + panic(err) + } + return node +} + +// OnlyID is like Only, but returns the only ContactRevealEvent ID in the query. +// Returns a *NotSingularError when more than one ContactRevealEvent ID is found. +// Returns a *NotFoundError when no entities are found. +func (_q *ContactRevealEventQuery) 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{contactrevealevent.Label} + default: + err = &NotSingularError{contactrevealevent.Label} + } + return +} + +// OnlyIDX is like OnlyID, but panics if an error occurs. +func (_q *ContactRevealEventQuery) 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 ContactRevealEvents. +func (_q *ContactRevealEventQuery) All(ctx context.Context) ([]*ContactRevealEvent, error) { + ctx = setContextOp(ctx, _q.ctx, ent.OpQueryAll) + if err := _q.prepareQuery(ctx); err != nil { + return nil, err + } + qr := querierAll[[]*ContactRevealEvent, *ContactRevealEventQuery]() + return withInterceptors[[]*ContactRevealEvent](ctx, _q, qr, _q.inters) +} + +// AllX is like All, but panics if an error occurs. +func (_q *ContactRevealEventQuery) AllX(ctx context.Context) []*ContactRevealEvent { + nodes, err := _q.All(ctx) + if err != nil { + panic(err) + } + return nodes +} + +// IDs executes the query and returns a list of ContactRevealEvent IDs. +func (_q *ContactRevealEventQuery) 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(contactrevealevent.FieldID).Scan(ctx, &ids); err != nil { + return nil, err + } + return ids, nil +} + +// IDsX is like IDs, but panics if an error occurs. +func (_q *ContactRevealEventQuery) 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 *ContactRevealEventQuery) 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[*ContactRevealEventQuery](), _q.inters) +} + +// CountX is like Count, but panics if an error occurs. +func (_q *ContactRevealEventQuery) 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 *ContactRevealEventQuery) 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 *ContactRevealEventQuery) ExistX(ctx context.Context) bool { + exist, err := _q.Exist(ctx) + if err != nil { + panic(err) + } + return exist +} + +// Clone returns a duplicate of the ContactRevealEventQuery 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 *ContactRevealEventQuery) Clone() *ContactRevealEventQuery { + if _q == nil { + return nil + } + return &ContactRevealEventQuery{ + config: _q.config, + ctx: _q.ctx.Clone(), + order: append([]contactrevealevent.OrderOption{}, _q.order...), + inters: append([]Interceptor{}, _q.inters...), + predicates: append([]predicate.ContactRevealEvent{}, _q.predicates...), + // clone intermediate query. + sql: _q.sql.Clone(), + path: _q.path, + } +} + +// GroupBy is used to group vertices by one or more fields/columns. +// It is often used with aggregate functions, like: count, max, mean, min, sum. +// +// Example: +// +// var v []struct { +// CustomerInternalUserID uuid.UUID `json:"customer_internal_user_id,omitempty"` +// Count int `json:"count,omitempty"` +// } +// +// client.ContactRevealEvent.Query(). +// GroupBy(contactrevealevent.FieldCustomerInternalUserID). +// Aggregate(ent.Count()). +// Scan(ctx, &v) +func (_q *ContactRevealEventQuery) GroupBy(field string, fields ...string) *ContactRevealEventGroupBy { + _q.ctx.Fields = append([]string{field}, fields...) + grbuild := &ContactRevealEventGroupBy{build: _q} + grbuild.flds = &_q.ctx.Fields + grbuild.label = contactrevealevent.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 { +// CustomerInternalUserID uuid.UUID `json:"customer_internal_user_id,omitempty"` +// } +// +// client.ContactRevealEvent.Query(). +// Select(contactrevealevent.FieldCustomerInternalUserID). +// Scan(ctx, &v) +func (_q *ContactRevealEventQuery) Select(fields ...string) *ContactRevealEventSelect { + _q.ctx.Fields = append(_q.ctx.Fields, fields...) + sbuild := &ContactRevealEventSelect{ContactRevealEventQuery: _q} + sbuild.label = contactrevealevent.Label + sbuild.flds, sbuild.scan = &_q.ctx.Fields, sbuild.Scan + return sbuild +} + +// Aggregate returns a ContactRevealEventSelect configured with the given aggregations. +func (_q *ContactRevealEventQuery) Aggregate(fns ...AggregateFunc) *ContactRevealEventSelect { + return _q.Select().Aggregate(fns...) +} + +func (_q *ContactRevealEventQuery) 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 !contactrevealevent.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 *ContactRevealEventQuery) sqlAll(ctx context.Context, hooks ...queryHook) ([]*ContactRevealEvent, error) { + var ( + nodes = []*ContactRevealEvent{} + _spec = _q.querySpec() + ) + _spec.ScanValues = func(columns []string) ([]any, error) { + return (*ContactRevealEvent).scanValues(nil, columns) + } + _spec.Assign = func(columns []string, values []any) error { + node := &ContactRevealEvent{config: _q.config} + nodes = append(nodes, node) + return node.assignValues(columns, values) + } + for i := range hooks { + hooks[i](ctx, _spec) + } + if err := sqlgraph.QueryNodes(ctx, _q.driver, _spec); err != nil { + return nil, err + } + if len(nodes) == 0 { + return nodes, nil + } + return nodes, nil +} + +func (_q *ContactRevealEventQuery) sqlCount(ctx context.Context) (int, error) { + _spec := _q.querySpec() + _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 *ContactRevealEventQuery) querySpec() *sqlgraph.QuerySpec { + _spec := sqlgraph.NewQuerySpec(contactrevealevent.Table, contactrevealevent.Columns, sqlgraph.NewFieldSpec(contactrevealevent.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, contactrevealevent.FieldID) + for i := range fields { + if fields[i] != contactrevealevent.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 *ContactRevealEventQuery) sqlQuery(ctx context.Context) *sql.Selector { + builder := sql.Dialect(_q.driver.Dialect()) + t1 := builder.Table(contactrevealevent.Table) + columns := _q.ctx.Fields + if len(columns) == 0 { + columns = contactrevealevent.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 _, 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 +} + +// ContactRevealEventGroupBy is the group-by builder for ContactRevealEvent entities. +type ContactRevealEventGroupBy struct { + selector + build *ContactRevealEventQuery +} + +// Aggregate adds the given aggregation functions to the group-by query. +func (_g *ContactRevealEventGroupBy) Aggregate(fns ...AggregateFunc) *ContactRevealEventGroupBy { + _g.fns = append(_g.fns, fns...) + return _g +} + +// Scan applies the selector query and scans the result into the given value. +func (_g *ContactRevealEventGroupBy) 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[*ContactRevealEventQuery, *ContactRevealEventGroupBy](ctx, _g.build, _g, _g.build.inters, v) +} + +func (_g *ContactRevealEventGroupBy) sqlScan(ctx context.Context, root *ContactRevealEventQuery, 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) +} + +// ContactRevealEventSelect is the builder for selecting fields of ContactRevealEvent entities. +type ContactRevealEventSelect struct { + *ContactRevealEventQuery + selector +} + +// Aggregate adds the given aggregation functions to the selector query. +func (_s *ContactRevealEventSelect) Aggregate(fns ...AggregateFunc) *ContactRevealEventSelect { + _s.fns = append(_s.fns, fns...) + return _s +} + +// Scan applies the selector query and scans the result into the given value. +func (_s *ContactRevealEventSelect) 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[*ContactRevealEventQuery, *ContactRevealEventSelect](ctx, _s.ContactRevealEventQuery, _s, _s.inters, v) +} + +func (_s *ContactRevealEventSelect) sqlScan(ctx context.Context, root *ContactRevealEventQuery, 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/backend/ent/contactrevealevent_update.go b/backend/ent/contactrevealevent_update.go new file mode 100644 index 0000000..9921b44 --- /dev/null +++ b/backend/ent/contactrevealevent_update.go @@ -0,0 +1,175 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "context" + "errors" + "fmt" + + "entgo.io/ent/dialect/sql" + "entgo.io/ent/dialect/sql/sqlgraph" + "entgo.io/ent/schema/field" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/contactrevealevent" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/predicate" +) + +// ContactRevealEventUpdate is the builder for updating ContactRevealEvent entities. +type ContactRevealEventUpdate struct { + config + hooks []Hook + mutation *ContactRevealEventMutation +} + +// Where appends a list predicates to the ContactRevealEventUpdate builder. +func (_u *ContactRevealEventUpdate) Where(ps ...predicate.ContactRevealEvent) *ContactRevealEventUpdate { + _u.mutation.Where(ps...) + return _u +} + +// Mutation returns the ContactRevealEventMutation object of the builder. +func (_u *ContactRevealEventUpdate) Mutation() *ContactRevealEventMutation { + return _u.mutation +} + +// Save executes the query and returns the number of nodes affected by the update operation. +func (_u *ContactRevealEventUpdate) 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 *ContactRevealEventUpdate) SaveX(ctx context.Context) int { + affected, err := _u.Save(ctx) + if err != nil { + panic(err) + } + return affected +} + +// Exec executes the query. +func (_u *ContactRevealEventUpdate) Exec(ctx context.Context) error { + _, err := _u.Save(ctx) + return err +} + +// ExecX is like Exec, but panics if an error occurs. +func (_u *ContactRevealEventUpdate) ExecX(ctx context.Context) { + if err := _u.Exec(ctx); err != nil { + panic(err) + } +} + +func (_u *ContactRevealEventUpdate) sqlSave(ctx context.Context) (_node int, err error) { + _spec := sqlgraph.NewUpdateSpec(contactrevealevent.Table, contactrevealevent.Columns, sqlgraph.NewFieldSpec(contactrevealevent.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 _node, err = sqlgraph.UpdateNodes(ctx, _u.driver, _spec); err != nil { + if _, ok := err.(*sqlgraph.NotFoundError); ok { + err = &NotFoundError{contactrevealevent.Label} + } else if sqlgraph.IsConstraintError(err) { + err = &ConstraintError{msg: err.Error(), wrap: err} + } + return 0, err + } + _u.mutation.done = true + return _node, nil +} + +// ContactRevealEventUpdateOne is the builder for updating a single ContactRevealEvent entity. +type ContactRevealEventUpdateOne struct { + config + fields []string + hooks []Hook + mutation *ContactRevealEventMutation +} + +// Mutation returns the ContactRevealEventMutation object of the builder. +func (_u *ContactRevealEventUpdateOne) Mutation() *ContactRevealEventMutation { + return _u.mutation +} + +// Where appends a list predicates to the ContactRevealEventUpdate builder. +func (_u *ContactRevealEventUpdateOne) Where(ps ...predicate.ContactRevealEvent) *ContactRevealEventUpdateOne { + _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 *ContactRevealEventUpdateOne) Select(field string, fields ...string) *ContactRevealEventUpdateOne { + _u.fields = append([]string{field}, fields...) + return _u +} + +// Save executes the query and returns the updated ContactRevealEvent entity. +func (_u *ContactRevealEventUpdateOne) Save(ctx context.Context) (*ContactRevealEvent, error) { + return withHooks(ctx, _u.sqlSave, _u.mutation, _u.hooks) +} + +// SaveX is like Save, but panics if an error occurs. +func (_u *ContactRevealEventUpdateOne) SaveX(ctx context.Context) *ContactRevealEvent { + node, err := _u.Save(ctx) + if err != nil { + panic(err) + } + return node +} + +// Exec executes the query on the entity. +func (_u *ContactRevealEventUpdateOne) Exec(ctx context.Context) error { + _, err := _u.Save(ctx) + return err +} + +// ExecX is like Exec, but panics if an error occurs. +func (_u *ContactRevealEventUpdateOne) ExecX(ctx context.Context) { + if err := _u.Exec(ctx); err != nil { + panic(err) + } +} + +func (_u *ContactRevealEventUpdateOne) sqlSave(ctx context.Context) (_node *ContactRevealEvent, err error) { + _spec := sqlgraph.NewUpdateSpec(contactrevealevent.Table, contactrevealevent.Columns, sqlgraph.NewFieldSpec(contactrevealevent.FieldID, field.TypeUUID)) + id, ok := _u.mutation.ID() + if !ok { + return nil, &ValidationError{Name: "id", err: errors.New(`ent: missing "ContactRevealEvent.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, contactrevealevent.FieldID) + for _, f := range fields { + if !contactrevealevent.ValidColumn(f) { + return nil, &ValidationError{Name: f, err: fmt.Errorf("ent: invalid field %q for query", f)} + } + if f != contactrevealevent.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) + } + } + } + _node = &ContactRevealEvent{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{contactrevealevent.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/backend/ent/ent.go b/backend/ent/ent.go new file mode 100644 index 0000000..f899e10 --- /dev/null +++ b/backend/ent/ent.go @@ -0,0 +1,644 @@ +// 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" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/administrativearea" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/contactrevealdailylimit" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/contactrevealevent" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/internaluser" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/listing" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/listingevent" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/listingmedia" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/locality" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/platformrole" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/providercontactchannel" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/providerprofile" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/providerservicelocality" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/providerspokenlanguage" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/servicecategory" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/servicecategorytranslation" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/spokenlanguage" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/spokenlanguagetranslation" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/supportedlocale" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/useraccount" +) + +// 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{ + administrativearea.Table: administrativearea.ValidColumn, + contactrevealdailylimit.Table: contactrevealdailylimit.ValidColumn, + contactrevealevent.Table: contactrevealevent.ValidColumn, + internaluser.Table: internaluser.ValidColumn, + listing.Table: listing.ValidColumn, + listingevent.Table: listingevent.ValidColumn, + listingmedia.Table: listingmedia.ValidColumn, + locality.Table: locality.ValidColumn, + platformrole.Table: platformrole.ValidColumn, + providercontactchannel.Table: providercontactchannel.ValidColumn, + providerprofile.Table: providerprofile.ValidColumn, + providerservicelocality.Table: providerservicelocality.ValidColumn, + providerspokenlanguage.Table: providerspokenlanguage.ValidColumn, + servicecategory.Table: servicecategory.ValidColumn, + servicecategorytranslation.Table: servicecategorytranslation.ValidColumn, + spokenlanguage.Table: spokenlanguage.ValidColumn, + spokenlanguagetranslation.Table: spokenlanguagetranslation.ValidColumn, + supportedlocale.Table: supportedlocale.ValidColumn, + useraccount.Table: useraccount.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/backend/ent/entc.go b/backend/ent/entc.go new file mode 100644 index 0000000..8d3fdfd --- /dev/null +++ b/backend/ent/entc.go @@ -0,0 +1,3 @@ +package ent + +//go:generate go run -mod=mod entgo.io/ent/cmd/ent generate ./schema diff --git a/backend/ent/enttest/enttest.go b/backend/ent/enttest/enttest.go new file mode 100644 index 0000000..cdd2cb5 --- /dev/null +++ b/backend/ent/enttest/enttest.go @@ -0,0 +1,84 @@ +// Code generated by ent, DO NOT EDIT. + +package enttest + +import ( + "context" + + "github.com/SourceSenseiTheRealOne/juntly/backend/ent" + // required by schema hooks. + _ "github.com/SourceSenseiTheRealOne/juntly/backend/ent/runtime" + + "entgo.io/ent/dialect/sql/schema" + "github.com/SourceSenseiTheRealOne/juntly/backend/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/backend/ent/hook/hook.go b/backend/ent/hook/hook.go new file mode 100644 index 0000000..a0a8e52 --- /dev/null +++ b/backend/ent/hook/hook.go @@ -0,0 +1,415 @@ +// Code generated by ent, DO NOT EDIT. + +package hook + +import ( + "context" + "fmt" + + "github.com/SourceSenseiTheRealOne/juntly/backend/ent" +) + +// The AdministrativeAreaFunc type is an adapter to allow the use of ordinary +// function as AdministrativeArea mutator. +type AdministrativeAreaFunc func(context.Context, *ent.AdministrativeAreaMutation) (ent.Value, error) + +// Mutate calls f(ctx, m). +func (f AdministrativeAreaFunc) Mutate(ctx context.Context, m ent.Mutation) (ent.Value, error) { + if mv, ok := m.(*ent.AdministrativeAreaMutation); ok { + return f(ctx, mv) + } + return nil, fmt.Errorf("unexpected mutation type %T. expect *ent.AdministrativeAreaMutation", m) +} + +// The ContactRevealDailyLimitFunc type is an adapter to allow the use of ordinary +// function as ContactRevealDailyLimit mutator. +type ContactRevealDailyLimitFunc func(context.Context, *ent.ContactRevealDailyLimitMutation) (ent.Value, error) + +// Mutate calls f(ctx, m). +func (f ContactRevealDailyLimitFunc) Mutate(ctx context.Context, m ent.Mutation) (ent.Value, error) { + if mv, ok := m.(*ent.ContactRevealDailyLimitMutation); ok { + return f(ctx, mv) + } + return nil, fmt.Errorf("unexpected mutation type %T. expect *ent.ContactRevealDailyLimitMutation", m) +} + +// The ContactRevealEventFunc type is an adapter to allow the use of ordinary +// function as ContactRevealEvent mutator. +type ContactRevealEventFunc func(context.Context, *ent.ContactRevealEventMutation) (ent.Value, error) + +// Mutate calls f(ctx, m). +func (f ContactRevealEventFunc) Mutate(ctx context.Context, m ent.Mutation) (ent.Value, error) { + if mv, ok := m.(*ent.ContactRevealEventMutation); ok { + return f(ctx, mv) + } + return nil, fmt.Errorf("unexpected mutation type %T. expect *ent.ContactRevealEventMutation", m) +} + +// The InternalUserFunc type is an adapter to allow the use of ordinary +// function as InternalUser mutator. +type InternalUserFunc func(context.Context, *ent.InternalUserMutation) (ent.Value, error) + +// Mutate calls f(ctx, m). +func (f InternalUserFunc) Mutate(ctx context.Context, m ent.Mutation) (ent.Value, error) { + if mv, ok := m.(*ent.InternalUserMutation); ok { + return f(ctx, mv) + } + return nil, fmt.Errorf("unexpected mutation type %T. expect *ent.InternalUserMutation", m) +} + +// The ListingFunc type is an adapter to allow the use of ordinary +// function as Listing mutator. +type ListingFunc func(context.Context, *ent.ListingMutation) (ent.Value, error) + +// Mutate calls f(ctx, m). +func (f ListingFunc) Mutate(ctx context.Context, m ent.Mutation) (ent.Value, error) { + if mv, ok := m.(*ent.ListingMutation); ok { + return f(ctx, mv) + } + return nil, fmt.Errorf("unexpected mutation type %T. expect *ent.ListingMutation", m) +} + +// The ListingEventFunc type is an adapter to allow the use of ordinary +// function as ListingEvent mutator. +type ListingEventFunc func(context.Context, *ent.ListingEventMutation) (ent.Value, error) + +// Mutate calls f(ctx, m). +func (f ListingEventFunc) Mutate(ctx context.Context, m ent.Mutation) (ent.Value, error) { + if mv, ok := m.(*ent.ListingEventMutation); ok { + return f(ctx, mv) + } + return nil, fmt.Errorf("unexpected mutation type %T. expect *ent.ListingEventMutation", m) +} + +// The ListingMediaFunc type is an adapter to allow the use of ordinary +// function as ListingMedia mutator. +type ListingMediaFunc func(context.Context, *ent.ListingMediaMutation) (ent.Value, error) + +// Mutate calls f(ctx, m). +func (f ListingMediaFunc) Mutate(ctx context.Context, m ent.Mutation) (ent.Value, error) { + if mv, ok := m.(*ent.ListingMediaMutation); ok { + return f(ctx, mv) + } + return nil, fmt.Errorf("unexpected mutation type %T. expect *ent.ListingMediaMutation", m) +} + +// The LocalityFunc type is an adapter to allow the use of ordinary +// function as Locality mutator. +type LocalityFunc func(context.Context, *ent.LocalityMutation) (ent.Value, error) + +// Mutate calls f(ctx, m). +func (f LocalityFunc) Mutate(ctx context.Context, m ent.Mutation) (ent.Value, error) { + if mv, ok := m.(*ent.LocalityMutation); ok { + return f(ctx, mv) + } + return nil, fmt.Errorf("unexpected mutation type %T. expect *ent.LocalityMutation", m) +} + +// The PlatformRoleFunc type is an adapter to allow the use of ordinary +// function as PlatformRole mutator. +type PlatformRoleFunc func(context.Context, *ent.PlatformRoleMutation) (ent.Value, error) + +// Mutate calls f(ctx, m). +func (f PlatformRoleFunc) Mutate(ctx context.Context, m ent.Mutation) (ent.Value, error) { + if mv, ok := m.(*ent.PlatformRoleMutation); ok { + return f(ctx, mv) + } + return nil, fmt.Errorf("unexpected mutation type %T. expect *ent.PlatformRoleMutation", m) +} + +// The ProviderContactChannelFunc type is an adapter to allow the use of ordinary +// function as ProviderContactChannel mutator. +type ProviderContactChannelFunc func(context.Context, *ent.ProviderContactChannelMutation) (ent.Value, error) + +// Mutate calls f(ctx, m). +func (f ProviderContactChannelFunc) Mutate(ctx context.Context, m ent.Mutation) (ent.Value, error) { + if mv, ok := m.(*ent.ProviderContactChannelMutation); ok { + return f(ctx, mv) + } + return nil, fmt.Errorf("unexpected mutation type %T. expect *ent.ProviderContactChannelMutation", m) +} + +// The ProviderProfileFunc type is an adapter to allow the use of ordinary +// function as ProviderProfile mutator. +type ProviderProfileFunc func(context.Context, *ent.ProviderProfileMutation) (ent.Value, error) + +// Mutate calls f(ctx, m). +func (f ProviderProfileFunc) Mutate(ctx context.Context, m ent.Mutation) (ent.Value, error) { + if mv, ok := m.(*ent.ProviderProfileMutation); ok { + return f(ctx, mv) + } + return nil, fmt.Errorf("unexpected mutation type %T. expect *ent.ProviderProfileMutation", m) +} + +// The ProviderServiceLocalityFunc type is an adapter to allow the use of ordinary +// function as ProviderServiceLocality mutator. +type ProviderServiceLocalityFunc func(context.Context, *ent.ProviderServiceLocalityMutation) (ent.Value, error) + +// Mutate calls f(ctx, m). +func (f ProviderServiceLocalityFunc) Mutate(ctx context.Context, m ent.Mutation) (ent.Value, error) { + if mv, ok := m.(*ent.ProviderServiceLocalityMutation); ok { + return f(ctx, mv) + } + return nil, fmt.Errorf("unexpected mutation type %T. expect *ent.ProviderServiceLocalityMutation", m) +} + +// The ProviderSpokenLanguageFunc type is an adapter to allow the use of ordinary +// function as ProviderSpokenLanguage mutator. +type ProviderSpokenLanguageFunc func(context.Context, *ent.ProviderSpokenLanguageMutation) (ent.Value, error) + +// Mutate calls f(ctx, m). +func (f ProviderSpokenLanguageFunc) Mutate(ctx context.Context, m ent.Mutation) (ent.Value, error) { + if mv, ok := m.(*ent.ProviderSpokenLanguageMutation); ok { + return f(ctx, mv) + } + return nil, fmt.Errorf("unexpected mutation type %T. expect *ent.ProviderSpokenLanguageMutation", m) +} + +// The ServiceCategoryFunc type is an adapter to allow the use of ordinary +// function as ServiceCategory mutator. +type ServiceCategoryFunc func(context.Context, *ent.ServiceCategoryMutation) (ent.Value, error) + +// Mutate calls f(ctx, m). +func (f ServiceCategoryFunc) Mutate(ctx context.Context, m ent.Mutation) (ent.Value, error) { + if mv, ok := m.(*ent.ServiceCategoryMutation); ok { + return f(ctx, mv) + } + return nil, fmt.Errorf("unexpected mutation type %T. expect *ent.ServiceCategoryMutation", m) +} + +// The ServiceCategoryTranslationFunc type is an adapter to allow the use of ordinary +// function as ServiceCategoryTranslation mutator. +type ServiceCategoryTranslationFunc func(context.Context, *ent.ServiceCategoryTranslationMutation) (ent.Value, error) + +// Mutate calls f(ctx, m). +func (f ServiceCategoryTranslationFunc) Mutate(ctx context.Context, m ent.Mutation) (ent.Value, error) { + if mv, ok := m.(*ent.ServiceCategoryTranslationMutation); ok { + return f(ctx, mv) + } + return nil, fmt.Errorf("unexpected mutation type %T. expect *ent.ServiceCategoryTranslationMutation", m) +} + +// The SpokenLanguageFunc type is an adapter to allow the use of ordinary +// function as SpokenLanguage mutator. +type SpokenLanguageFunc func(context.Context, *ent.SpokenLanguageMutation) (ent.Value, error) + +// Mutate calls f(ctx, m). +func (f SpokenLanguageFunc) Mutate(ctx context.Context, m ent.Mutation) (ent.Value, error) { + if mv, ok := m.(*ent.SpokenLanguageMutation); ok { + return f(ctx, mv) + } + return nil, fmt.Errorf("unexpected mutation type %T. expect *ent.SpokenLanguageMutation", m) +} + +// The SpokenLanguageTranslationFunc type is an adapter to allow the use of ordinary +// function as SpokenLanguageTranslation mutator. +type SpokenLanguageTranslationFunc func(context.Context, *ent.SpokenLanguageTranslationMutation) (ent.Value, error) + +// Mutate calls f(ctx, m). +func (f SpokenLanguageTranslationFunc) Mutate(ctx context.Context, m ent.Mutation) (ent.Value, error) { + if mv, ok := m.(*ent.SpokenLanguageTranslationMutation); ok { + return f(ctx, mv) + } + return nil, fmt.Errorf("unexpected mutation type %T. expect *ent.SpokenLanguageTranslationMutation", m) +} + +// The SupportedLocaleFunc type is an adapter to allow the use of ordinary +// function as SupportedLocale mutator. +type SupportedLocaleFunc func(context.Context, *ent.SupportedLocaleMutation) (ent.Value, error) + +// Mutate calls f(ctx, m). +func (f SupportedLocaleFunc) Mutate(ctx context.Context, m ent.Mutation) (ent.Value, error) { + if mv, ok := m.(*ent.SupportedLocaleMutation); ok { + return f(ctx, mv) + } + return nil, fmt.Errorf("unexpected mutation type %T. expect *ent.SupportedLocaleMutation", m) +} + +// The UserAccountFunc type is an adapter to allow the use of ordinary +// function as UserAccount mutator. +type UserAccountFunc func(context.Context, *ent.UserAccountMutation) (ent.Value, error) + +// Mutate calls f(ctx, m). +func (f UserAccountFunc) Mutate(ctx context.Context, m ent.Mutation) (ent.Value, error) { + if mv, ok := m.(*ent.UserAccountMutation); ok { + return f(ctx, mv) + } + return nil, fmt.Errorf("unexpected mutation type %T. expect *ent.UserAccountMutation", 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/backend/ent/internaluser.go b/backend/ent/internaluser.go new file mode 100644 index 0000000..6514de3 --- /dev/null +++ b/backend/ent/internaluser.go @@ -0,0 +1,129 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "fmt" + "strings" + "time" + + "entgo.io/ent" + "entgo.io/ent/dialect/sql" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/internaluser" + "github.com/google/uuid" +) + +// InternalUser is the model entity for the InternalUser schema. +type InternalUser struct { + config `json:"-"` + // ID of the ent. + ID uuid.UUID `json:"id,omitempty"` + // ClerkSubject holds the value of the "clerk_subject" field. + ClerkSubject string `json:"clerk_subject,omitempty"` + // CreatedAt holds the value of the "created_at" field. + CreatedAt time.Time `json:"created_at,omitempty"` + // UpdatedAt holds the value of the "updated_at" field. + UpdatedAt time.Time `json:"updated_at,omitempty"` + selectValues sql.SelectValues +} + +// scanValues returns the types for scanning values from sql.Rows. +func (*InternalUser) scanValues(columns []string) ([]any, error) { + values := make([]any, len(columns)) + for i := range columns { + switch columns[i] { + case internaluser.FieldClerkSubject: + values[i] = new(sql.NullString) + case internaluser.FieldCreatedAt, internaluser.FieldUpdatedAt: + values[i] = new(sql.NullTime) + case internaluser.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 InternalUser fields. +func (_m *InternalUser) 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 internaluser.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 internaluser.FieldClerkSubject: + if value, ok := values[i].(*sql.NullString); !ok { + return fmt.Errorf("unexpected type %T for field clerk_subject", values[i]) + } else if value.Valid { + _m.ClerkSubject = value.String + } + case internaluser.FieldCreatedAt: + if value, ok := values[i].(*sql.NullTime); !ok { + return fmt.Errorf("unexpected type %T for field created_at", values[i]) + } else if value.Valid { + _m.CreatedAt = value.Time + } + case internaluser.FieldUpdatedAt: + if value, ok := values[i].(*sql.NullTime); !ok { + return fmt.Errorf("unexpected type %T for field updated_at", values[i]) + } else if value.Valid { + _m.UpdatedAt = value.Time + } + default: + _m.selectValues.Set(columns[i], values[i]) + } + } + return nil +} + +// Value returns the ent.Value that was dynamically selected and assigned to the InternalUser. +// This includes values selected through modifiers, order, etc. +func (_m *InternalUser) Value(name string) (ent.Value, error) { + return _m.selectValues.Get(name) +} + +// Update returns a builder for updating this InternalUser. +// Note that you need to call InternalUser.Unwrap() before calling this method if this InternalUser +// was returned from a transaction, and the transaction was committed or rolled back. +func (_m *InternalUser) Update() *InternalUserUpdateOne { + return NewInternalUserClient(_m.config).UpdateOne(_m) +} + +// Unwrap unwraps the InternalUser 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 *InternalUser) Unwrap() *InternalUser { + _tx, ok := _m.config.driver.(*txDriver) + if !ok { + panic("ent: InternalUser is not a transactional entity") + } + _m.config.driver = _tx.drv + return _m +} + +// String implements the fmt.Stringer. +func (_m *InternalUser) String() string { + var builder strings.Builder + builder.WriteString("InternalUser(") + builder.WriteString(fmt.Sprintf("id=%v, ", _m.ID)) + builder.WriteString("clerk_subject=") + builder.WriteString(_m.ClerkSubject) + builder.WriteString(", ") + builder.WriteString("created_at=") + builder.WriteString(_m.CreatedAt.Format(time.ANSIC)) + builder.WriteString(", ") + builder.WriteString("updated_at=") + builder.WriteString(_m.UpdatedAt.Format(time.ANSIC)) + builder.WriteByte(')') + return builder.String() +} + +// InternalUsers is a parsable slice of InternalUser. +type InternalUsers []*InternalUser diff --git a/backend/ent/internaluser/internaluser.go b/backend/ent/internaluser/internaluser.go new file mode 100644 index 0000000..57e0048 --- /dev/null +++ b/backend/ent/internaluser/internaluser.go @@ -0,0 +1,79 @@ +// Code generated by ent, DO NOT EDIT. + +package internaluser + +import ( + "time" + + "entgo.io/ent/dialect/sql" + "github.com/google/uuid" +) + +const ( + // Label holds the string label denoting the internaluser type in the database. + Label = "internal_user" + // FieldID holds the string denoting the id field in the database. + FieldID = "id" + // FieldClerkSubject holds the string denoting the clerk_subject field in the database. + FieldClerkSubject = "clerk_subject" + // FieldCreatedAt holds the string denoting the created_at field in the database. + FieldCreatedAt = "created_at" + // FieldUpdatedAt holds the string denoting the updated_at field in the database. + FieldUpdatedAt = "updated_at" + // Table holds the table name of the internaluser in the database. + Table = "internal_users" +) + +// Columns holds all SQL columns for internaluser fields. +var Columns = []string{ + FieldID, + FieldClerkSubject, + FieldCreatedAt, + FieldUpdatedAt, +} + +// 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 ( + // ClerkSubjectValidator is a validator for the "clerk_subject" field. It is called by the builders before save. + ClerkSubjectValidator func(string) error + // DefaultCreatedAt holds the default value on creation for the "created_at" field. + DefaultCreatedAt func() time.Time + // DefaultUpdatedAt holds the default value on creation for the "updated_at" field. + DefaultUpdatedAt func() time.Time + // UpdateDefaultUpdatedAt holds the default value on update for the "updated_at" field. + UpdateDefaultUpdatedAt func() time.Time + // DefaultID holds the default value on creation for the "id" field. + DefaultID func() uuid.UUID +) + +// OrderOption defines the ordering options for the InternalUser 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() +} + +// ByClerkSubject orders the results by the clerk_subject field. +func ByClerkSubject(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldClerkSubject, opts...).ToFunc() +} + +// ByCreatedAt orders the results by the created_at field. +func ByCreatedAt(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldCreatedAt, opts...).ToFunc() +} + +// ByUpdatedAt orders the results by the updated_at field. +func ByUpdatedAt(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldUpdatedAt, opts...).ToFunc() +} diff --git a/backend/ent/internaluser/where.go b/backend/ent/internaluser/where.go new file mode 100644 index 0000000..83b9f08 --- /dev/null +++ b/backend/ent/internaluser/where.go @@ -0,0 +1,231 @@ +// Code generated by ent, DO NOT EDIT. + +package internaluser + +import ( + "time" + + "entgo.io/ent/dialect/sql" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/predicate" + "github.com/google/uuid" +) + +// ID filters vertices based on their ID field. +func ID(id uuid.UUID) predicate.InternalUser { + return predicate.InternalUser(sql.FieldEQ(FieldID, id)) +} + +// IDEQ applies the EQ predicate on the ID field. +func IDEQ(id uuid.UUID) predicate.InternalUser { + return predicate.InternalUser(sql.FieldEQ(FieldID, id)) +} + +// IDNEQ applies the NEQ predicate on the ID field. +func IDNEQ(id uuid.UUID) predicate.InternalUser { + return predicate.InternalUser(sql.FieldNEQ(FieldID, id)) +} + +// IDIn applies the In predicate on the ID field. +func IDIn(ids ...uuid.UUID) predicate.InternalUser { + return predicate.InternalUser(sql.FieldIn(FieldID, ids...)) +} + +// IDNotIn applies the NotIn predicate on the ID field. +func IDNotIn(ids ...uuid.UUID) predicate.InternalUser { + return predicate.InternalUser(sql.FieldNotIn(FieldID, ids...)) +} + +// IDGT applies the GT predicate on the ID field. +func IDGT(id uuid.UUID) predicate.InternalUser { + return predicate.InternalUser(sql.FieldGT(FieldID, id)) +} + +// IDGTE applies the GTE predicate on the ID field. +func IDGTE(id uuid.UUID) predicate.InternalUser { + return predicate.InternalUser(sql.FieldGTE(FieldID, id)) +} + +// IDLT applies the LT predicate on the ID field. +func IDLT(id uuid.UUID) predicate.InternalUser { + return predicate.InternalUser(sql.FieldLT(FieldID, id)) +} + +// IDLTE applies the LTE predicate on the ID field. +func IDLTE(id uuid.UUID) predicate.InternalUser { + return predicate.InternalUser(sql.FieldLTE(FieldID, id)) +} + +// ClerkSubject applies equality check predicate on the "clerk_subject" field. It's identical to ClerkSubjectEQ. +func ClerkSubject(v string) predicate.InternalUser { + return predicate.InternalUser(sql.FieldEQ(FieldClerkSubject, v)) +} + +// CreatedAt applies equality check predicate on the "created_at" field. It's identical to CreatedAtEQ. +func CreatedAt(v time.Time) predicate.InternalUser { + return predicate.InternalUser(sql.FieldEQ(FieldCreatedAt, v)) +} + +// UpdatedAt applies equality check predicate on the "updated_at" field. It's identical to UpdatedAtEQ. +func UpdatedAt(v time.Time) predicate.InternalUser { + return predicate.InternalUser(sql.FieldEQ(FieldUpdatedAt, v)) +} + +// ClerkSubjectEQ applies the EQ predicate on the "clerk_subject" field. +func ClerkSubjectEQ(v string) predicate.InternalUser { + return predicate.InternalUser(sql.FieldEQ(FieldClerkSubject, v)) +} + +// ClerkSubjectNEQ applies the NEQ predicate on the "clerk_subject" field. +func ClerkSubjectNEQ(v string) predicate.InternalUser { + return predicate.InternalUser(sql.FieldNEQ(FieldClerkSubject, v)) +} + +// ClerkSubjectIn applies the In predicate on the "clerk_subject" field. +func ClerkSubjectIn(vs ...string) predicate.InternalUser { + return predicate.InternalUser(sql.FieldIn(FieldClerkSubject, vs...)) +} + +// ClerkSubjectNotIn applies the NotIn predicate on the "clerk_subject" field. +func ClerkSubjectNotIn(vs ...string) predicate.InternalUser { + return predicate.InternalUser(sql.FieldNotIn(FieldClerkSubject, vs...)) +} + +// ClerkSubjectGT applies the GT predicate on the "clerk_subject" field. +func ClerkSubjectGT(v string) predicate.InternalUser { + return predicate.InternalUser(sql.FieldGT(FieldClerkSubject, v)) +} + +// ClerkSubjectGTE applies the GTE predicate on the "clerk_subject" field. +func ClerkSubjectGTE(v string) predicate.InternalUser { + return predicate.InternalUser(sql.FieldGTE(FieldClerkSubject, v)) +} + +// ClerkSubjectLT applies the LT predicate on the "clerk_subject" field. +func ClerkSubjectLT(v string) predicate.InternalUser { + return predicate.InternalUser(sql.FieldLT(FieldClerkSubject, v)) +} + +// ClerkSubjectLTE applies the LTE predicate on the "clerk_subject" field. +func ClerkSubjectLTE(v string) predicate.InternalUser { + return predicate.InternalUser(sql.FieldLTE(FieldClerkSubject, v)) +} + +// ClerkSubjectContains applies the Contains predicate on the "clerk_subject" field. +func ClerkSubjectContains(v string) predicate.InternalUser { + return predicate.InternalUser(sql.FieldContains(FieldClerkSubject, v)) +} + +// ClerkSubjectHasPrefix applies the HasPrefix predicate on the "clerk_subject" field. +func ClerkSubjectHasPrefix(v string) predicate.InternalUser { + return predicate.InternalUser(sql.FieldHasPrefix(FieldClerkSubject, v)) +} + +// ClerkSubjectHasSuffix applies the HasSuffix predicate on the "clerk_subject" field. +func ClerkSubjectHasSuffix(v string) predicate.InternalUser { + return predicate.InternalUser(sql.FieldHasSuffix(FieldClerkSubject, v)) +} + +// ClerkSubjectEqualFold applies the EqualFold predicate on the "clerk_subject" field. +func ClerkSubjectEqualFold(v string) predicate.InternalUser { + return predicate.InternalUser(sql.FieldEqualFold(FieldClerkSubject, v)) +} + +// ClerkSubjectContainsFold applies the ContainsFold predicate on the "clerk_subject" field. +func ClerkSubjectContainsFold(v string) predicate.InternalUser { + return predicate.InternalUser(sql.FieldContainsFold(FieldClerkSubject, v)) +} + +// CreatedAtEQ applies the EQ predicate on the "created_at" field. +func CreatedAtEQ(v time.Time) predicate.InternalUser { + return predicate.InternalUser(sql.FieldEQ(FieldCreatedAt, v)) +} + +// CreatedAtNEQ applies the NEQ predicate on the "created_at" field. +func CreatedAtNEQ(v time.Time) predicate.InternalUser { + return predicate.InternalUser(sql.FieldNEQ(FieldCreatedAt, v)) +} + +// CreatedAtIn applies the In predicate on the "created_at" field. +func CreatedAtIn(vs ...time.Time) predicate.InternalUser { + return predicate.InternalUser(sql.FieldIn(FieldCreatedAt, vs...)) +} + +// CreatedAtNotIn applies the NotIn predicate on the "created_at" field. +func CreatedAtNotIn(vs ...time.Time) predicate.InternalUser { + return predicate.InternalUser(sql.FieldNotIn(FieldCreatedAt, vs...)) +} + +// CreatedAtGT applies the GT predicate on the "created_at" field. +func CreatedAtGT(v time.Time) predicate.InternalUser { + return predicate.InternalUser(sql.FieldGT(FieldCreatedAt, v)) +} + +// CreatedAtGTE applies the GTE predicate on the "created_at" field. +func CreatedAtGTE(v time.Time) predicate.InternalUser { + return predicate.InternalUser(sql.FieldGTE(FieldCreatedAt, v)) +} + +// CreatedAtLT applies the LT predicate on the "created_at" field. +func CreatedAtLT(v time.Time) predicate.InternalUser { + return predicate.InternalUser(sql.FieldLT(FieldCreatedAt, v)) +} + +// CreatedAtLTE applies the LTE predicate on the "created_at" field. +func CreatedAtLTE(v time.Time) predicate.InternalUser { + return predicate.InternalUser(sql.FieldLTE(FieldCreatedAt, v)) +} + +// UpdatedAtEQ applies the EQ predicate on the "updated_at" field. +func UpdatedAtEQ(v time.Time) predicate.InternalUser { + return predicate.InternalUser(sql.FieldEQ(FieldUpdatedAt, v)) +} + +// UpdatedAtNEQ applies the NEQ predicate on the "updated_at" field. +func UpdatedAtNEQ(v time.Time) predicate.InternalUser { + return predicate.InternalUser(sql.FieldNEQ(FieldUpdatedAt, v)) +} + +// UpdatedAtIn applies the In predicate on the "updated_at" field. +func UpdatedAtIn(vs ...time.Time) predicate.InternalUser { + return predicate.InternalUser(sql.FieldIn(FieldUpdatedAt, vs...)) +} + +// UpdatedAtNotIn applies the NotIn predicate on the "updated_at" field. +func UpdatedAtNotIn(vs ...time.Time) predicate.InternalUser { + return predicate.InternalUser(sql.FieldNotIn(FieldUpdatedAt, vs...)) +} + +// UpdatedAtGT applies the GT predicate on the "updated_at" field. +func UpdatedAtGT(v time.Time) predicate.InternalUser { + return predicate.InternalUser(sql.FieldGT(FieldUpdatedAt, v)) +} + +// UpdatedAtGTE applies the GTE predicate on the "updated_at" field. +func UpdatedAtGTE(v time.Time) predicate.InternalUser { + return predicate.InternalUser(sql.FieldGTE(FieldUpdatedAt, v)) +} + +// UpdatedAtLT applies the LT predicate on the "updated_at" field. +func UpdatedAtLT(v time.Time) predicate.InternalUser { + return predicate.InternalUser(sql.FieldLT(FieldUpdatedAt, v)) +} + +// UpdatedAtLTE applies the LTE predicate on the "updated_at" field. +func UpdatedAtLTE(v time.Time) predicate.InternalUser { + return predicate.InternalUser(sql.FieldLTE(FieldUpdatedAt, v)) +} + +// And groups predicates with the AND operator between them. +func And(predicates ...predicate.InternalUser) predicate.InternalUser { + return predicate.InternalUser(sql.AndPredicates(predicates...)) +} + +// Or groups predicates with the OR operator between them. +func Or(predicates ...predicate.InternalUser) predicate.InternalUser { + return predicate.InternalUser(sql.OrPredicates(predicates...)) +} + +// Not applies the not operator on the given predicate. +func Not(p predicate.InternalUser) predicate.InternalUser { + return predicate.InternalUser(sql.NotPredicates(p)) +} diff --git a/backend/ent/internaluser_create.go b/backend/ent/internaluser_create.go new file mode 100644 index 0000000..9a6038b --- /dev/null +++ b/backend/ent/internaluser_create.go @@ -0,0 +1,269 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "context" + "errors" + "fmt" + "time" + + "entgo.io/ent/dialect/sql/sqlgraph" + "entgo.io/ent/schema/field" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/internaluser" + "github.com/google/uuid" +) + +// InternalUserCreate is the builder for creating a InternalUser entity. +type InternalUserCreate struct { + config + mutation *InternalUserMutation + hooks []Hook +} + +// SetClerkSubject sets the "clerk_subject" field. +func (_c *InternalUserCreate) SetClerkSubject(v string) *InternalUserCreate { + _c.mutation.SetClerkSubject(v) + return _c +} + +// SetCreatedAt sets the "created_at" field. +func (_c *InternalUserCreate) SetCreatedAt(v time.Time) *InternalUserCreate { + _c.mutation.SetCreatedAt(v) + return _c +} + +// SetNillableCreatedAt sets the "created_at" field if the given value is not nil. +func (_c *InternalUserCreate) SetNillableCreatedAt(v *time.Time) *InternalUserCreate { + if v != nil { + _c.SetCreatedAt(*v) + } + return _c +} + +// SetUpdatedAt sets the "updated_at" field. +func (_c *InternalUserCreate) SetUpdatedAt(v time.Time) *InternalUserCreate { + _c.mutation.SetUpdatedAt(v) + return _c +} + +// SetNillableUpdatedAt sets the "updated_at" field if the given value is not nil. +func (_c *InternalUserCreate) SetNillableUpdatedAt(v *time.Time) *InternalUserCreate { + if v != nil { + _c.SetUpdatedAt(*v) + } + return _c +} + +// SetID sets the "id" field. +func (_c *InternalUserCreate) SetID(v uuid.UUID) *InternalUserCreate { + _c.mutation.SetID(v) + return _c +} + +// SetNillableID sets the "id" field if the given value is not nil. +func (_c *InternalUserCreate) SetNillableID(v *uuid.UUID) *InternalUserCreate { + if v != nil { + _c.SetID(*v) + } + return _c +} + +// Mutation returns the InternalUserMutation object of the builder. +func (_c *InternalUserCreate) Mutation() *InternalUserMutation { + return _c.mutation +} + +// Save creates the InternalUser in the database. +func (_c *InternalUserCreate) Save(ctx context.Context) (*InternalUser, error) { + _c.defaults() + return withHooks(ctx, _c.sqlSave, _c.mutation, _c.hooks) +} + +// SaveX calls Save and panics if Save returns an error. +func (_c *InternalUserCreate) SaveX(ctx context.Context) *InternalUser { + v, err := _c.Save(ctx) + if err != nil { + panic(err) + } + return v +} + +// Exec executes the query. +func (_c *InternalUserCreate) Exec(ctx context.Context) error { + _, err := _c.Save(ctx) + return err +} + +// ExecX is like Exec, but panics if an error occurs. +func (_c *InternalUserCreate) 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 *InternalUserCreate) defaults() { + if _, ok := _c.mutation.CreatedAt(); !ok { + v := internaluser.DefaultCreatedAt() + _c.mutation.SetCreatedAt(v) + } + if _, ok := _c.mutation.UpdatedAt(); !ok { + v := internaluser.DefaultUpdatedAt() + _c.mutation.SetUpdatedAt(v) + } + if _, ok := _c.mutation.ID(); !ok { + v := internaluser.DefaultID() + _c.mutation.SetID(v) + } +} + +// check runs all checks and user-defined validators on the builder. +func (_c *InternalUserCreate) check() error { + if _, ok := _c.mutation.ClerkSubject(); !ok { + return &ValidationError{Name: "clerk_subject", err: errors.New(`ent: missing required field "InternalUser.clerk_subject"`)} + } + if v, ok := _c.mutation.ClerkSubject(); ok { + if err := internaluser.ClerkSubjectValidator(v); err != nil { + return &ValidationError{Name: "clerk_subject", err: fmt.Errorf(`ent: validator failed for field "InternalUser.clerk_subject": %w`, err)} + } + } + if _, ok := _c.mutation.CreatedAt(); !ok { + return &ValidationError{Name: "created_at", err: errors.New(`ent: missing required field "InternalUser.created_at"`)} + } + if _, ok := _c.mutation.UpdatedAt(); !ok { + return &ValidationError{Name: "updated_at", err: errors.New(`ent: missing required field "InternalUser.updated_at"`)} + } + return nil +} + +func (_c *InternalUserCreate) sqlSave(ctx context.Context) (*InternalUser, 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 *InternalUserCreate) createSpec() (*InternalUser, *sqlgraph.CreateSpec) { + var ( + _node = &InternalUser{config: _c.config} + _spec = sqlgraph.NewCreateSpec(internaluser.Table, sqlgraph.NewFieldSpec(internaluser.FieldID, field.TypeUUID)) + ) + if id, ok := _c.mutation.ID(); ok { + _node.ID = id + _spec.ID.Value = &id + } + if value, ok := _c.mutation.ClerkSubject(); ok { + _spec.SetField(internaluser.FieldClerkSubject, field.TypeString, value) + _node.ClerkSubject = value + } + if value, ok := _c.mutation.CreatedAt(); ok { + _spec.SetField(internaluser.FieldCreatedAt, field.TypeTime, value) + _node.CreatedAt = value + } + if value, ok := _c.mutation.UpdatedAt(); ok { + _spec.SetField(internaluser.FieldUpdatedAt, field.TypeTime, value) + _node.UpdatedAt = value + } + return _node, _spec +} + +// InternalUserCreateBulk is the builder for creating many InternalUser entities in bulk. +type InternalUserCreateBulk struct { + config + err error + builders []*InternalUserCreate +} + +// Save creates the InternalUser entities in the database. +func (_c *InternalUserCreateBulk) Save(ctx context.Context) ([]*InternalUser, error) { + if _c.err != nil { + return nil, _c.err + } + specs := make([]*sqlgraph.CreateSpec, len(_c.builders)) + nodes := make([]*InternalUser, 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.(*InternalUserMutation) + 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 *InternalUserCreateBulk) SaveX(ctx context.Context) []*InternalUser { + v, err := _c.Save(ctx) + if err != nil { + panic(err) + } + return v +} + +// Exec executes the query. +func (_c *InternalUserCreateBulk) Exec(ctx context.Context) error { + _, err := _c.Save(ctx) + return err +} + +// ExecX is like Exec, but panics if an error occurs. +func (_c *InternalUserCreateBulk) ExecX(ctx context.Context) { + if err := _c.Exec(ctx); err != nil { + panic(err) + } +} diff --git a/backend/ent/internaluser_delete.go b/backend/ent/internaluser_delete.go new file mode 100644 index 0000000..c36f2a1 --- /dev/null +++ b/backend/ent/internaluser_delete.go @@ -0,0 +1,88 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "context" + + "entgo.io/ent/dialect/sql" + "entgo.io/ent/dialect/sql/sqlgraph" + "entgo.io/ent/schema/field" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/internaluser" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/predicate" +) + +// InternalUserDelete is the builder for deleting a InternalUser entity. +type InternalUserDelete struct { + config + hooks []Hook + mutation *InternalUserMutation +} + +// Where appends a list predicates to the InternalUserDelete builder. +func (_d *InternalUserDelete) Where(ps ...predicate.InternalUser) *InternalUserDelete { + _d.mutation.Where(ps...) + return _d +} + +// Exec executes the deletion query and returns how many vertices were deleted. +func (_d *InternalUserDelete) 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 *InternalUserDelete) ExecX(ctx context.Context) int { + n, err := _d.Exec(ctx) + if err != nil { + panic(err) + } + return n +} + +func (_d *InternalUserDelete) sqlExec(ctx context.Context) (int, error) { + _spec := sqlgraph.NewDeleteSpec(internaluser.Table, sqlgraph.NewFieldSpec(internaluser.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 +} + +// InternalUserDeleteOne is the builder for deleting a single InternalUser entity. +type InternalUserDeleteOne struct { + _d *InternalUserDelete +} + +// Where appends a list predicates to the InternalUserDelete builder. +func (_d *InternalUserDeleteOne) Where(ps ...predicate.InternalUser) *InternalUserDeleteOne { + _d._d.mutation.Where(ps...) + return _d +} + +// Exec executes the deletion query. +func (_d *InternalUserDeleteOne) Exec(ctx context.Context) error { + n, err := _d._d.Exec(ctx) + switch { + case err != nil: + return err + case n == 0: + return &NotFoundError{internaluser.Label} + default: + return nil + } +} + +// ExecX is like Exec, but panics if an error occurs. +func (_d *InternalUserDeleteOne) ExecX(ctx context.Context) { + if err := _d.Exec(ctx); err != nil { + panic(err) + } +} diff --git a/backend/ent/internaluser_query.go b/backend/ent/internaluser_query.go new file mode 100644 index 0000000..cd7a908 --- /dev/null +++ b/backend/ent/internaluser_query.go @@ -0,0 +1,528 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "context" + "fmt" + "math" + + "entgo.io/ent" + "entgo.io/ent/dialect/sql" + "entgo.io/ent/dialect/sql/sqlgraph" + "entgo.io/ent/schema/field" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/internaluser" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/predicate" + "github.com/google/uuid" +) + +// InternalUserQuery is the builder for querying InternalUser entities. +type InternalUserQuery struct { + config + ctx *QueryContext + order []internaluser.OrderOption + inters []Interceptor + predicates []predicate.InternalUser + // intermediate query (i.e. traversal path). + sql *sql.Selector + path func(context.Context) (*sql.Selector, error) +} + +// Where adds a new predicate for the InternalUserQuery builder. +func (_q *InternalUserQuery) Where(ps ...predicate.InternalUser) *InternalUserQuery { + _q.predicates = append(_q.predicates, ps...) + return _q +} + +// Limit the number of records to be returned by this query. +func (_q *InternalUserQuery) Limit(limit int) *InternalUserQuery { + _q.ctx.Limit = &limit + return _q +} + +// Offset to start from. +func (_q *InternalUserQuery) Offset(offset int) *InternalUserQuery { + _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 *InternalUserQuery) Unique(unique bool) *InternalUserQuery { + _q.ctx.Unique = &unique + return _q +} + +// Order specifies how the records should be ordered. +func (_q *InternalUserQuery) Order(o ...internaluser.OrderOption) *InternalUserQuery { + _q.order = append(_q.order, o...) + return _q +} + +// First returns the first InternalUser entity from the query. +// Returns a *NotFoundError when no InternalUser was found. +func (_q *InternalUserQuery) First(ctx context.Context) (*InternalUser, 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{internaluser.Label} + } + return nodes[0], nil +} + +// FirstX is like First, but panics if an error occurs. +func (_q *InternalUserQuery) FirstX(ctx context.Context) *InternalUser { + node, err := _q.First(ctx) + if err != nil && !IsNotFound(err) { + panic(err) + } + return node +} + +// FirstID returns the first InternalUser ID from the query. +// Returns a *NotFoundError when no InternalUser ID was found. +func (_q *InternalUserQuery) 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{internaluser.Label} + return + } + return ids[0], nil +} + +// FirstIDX is like FirstID, but panics if an error occurs. +func (_q *InternalUserQuery) FirstIDX(ctx context.Context) uuid.UUID { + id, err := _q.FirstID(ctx) + if err != nil && !IsNotFound(err) { + panic(err) + } + return id +} + +// Only returns a single InternalUser entity found by the query, ensuring it only returns one. +// Returns a *NotSingularError when more than one InternalUser entity is found. +// Returns a *NotFoundError when no InternalUser entities are found. +func (_q *InternalUserQuery) Only(ctx context.Context) (*InternalUser, 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{internaluser.Label} + default: + return nil, &NotSingularError{internaluser.Label} + } +} + +// OnlyX is like Only, but panics if an error occurs. +func (_q *InternalUserQuery) OnlyX(ctx context.Context) *InternalUser { + node, err := _q.Only(ctx) + if err != nil { + panic(err) + } + return node +} + +// OnlyID is like Only, but returns the only InternalUser ID in the query. +// Returns a *NotSingularError when more than one InternalUser ID is found. +// Returns a *NotFoundError when no entities are found. +func (_q *InternalUserQuery) 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{internaluser.Label} + default: + err = &NotSingularError{internaluser.Label} + } + return +} + +// OnlyIDX is like OnlyID, but panics if an error occurs. +func (_q *InternalUserQuery) 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 InternalUsers. +func (_q *InternalUserQuery) All(ctx context.Context) ([]*InternalUser, error) { + ctx = setContextOp(ctx, _q.ctx, ent.OpQueryAll) + if err := _q.prepareQuery(ctx); err != nil { + return nil, err + } + qr := querierAll[[]*InternalUser, *InternalUserQuery]() + return withInterceptors[[]*InternalUser](ctx, _q, qr, _q.inters) +} + +// AllX is like All, but panics if an error occurs. +func (_q *InternalUserQuery) AllX(ctx context.Context) []*InternalUser { + nodes, err := _q.All(ctx) + if err != nil { + panic(err) + } + return nodes +} + +// IDs executes the query and returns a list of InternalUser IDs. +func (_q *InternalUserQuery) 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(internaluser.FieldID).Scan(ctx, &ids); err != nil { + return nil, err + } + return ids, nil +} + +// IDsX is like IDs, but panics if an error occurs. +func (_q *InternalUserQuery) 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 *InternalUserQuery) 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[*InternalUserQuery](), _q.inters) +} + +// CountX is like Count, but panics if an error occurs. +func (_q *InternalUserQuery) 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 *InternalUserQuery) 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 *InternalUserQuery) ExistX(ctx context.Context) bool { + exist, err := _q.Exist(ctx) + if err != nil { + panic(err) + } + return exist +} + +// Clone returns a duplicate of the InternalUserQuery 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 *InternalUserQuery) Clone() *InternalUserQuery { + if _q == nil { + return nil + } + return &InternalUserQuery{ + config: _q.config, + ctx: _q.ctx.Clone(), + order: append([]internaluser.OrderOption{}, _q.order...), + inters: append([]Interceptor{}, _q.inters...), + predicates: append([]predicate.InternalUser{}, _q.predicates...), + // clone intermediate query. + sql: _q.sql.Clone(), + path: _q.path, + } +} + +// GroupBy is used to group vertices by one or more fields/columns. +// It is often used with aggregate functions, like: count, max, mean, min, sum. +// +// Example: +// +// var v []struct { +// ClerkSubject string `json:"clerk_subject,omitempty"` +// Count int `json:"count,omitempty"` +// } +// +// client.InternalUser.Query(). +// GroupBy(internaluser.FieldClerkSubject). +// Aggregate(ent.Count()). +// Scan(ctx, &v) +func (_q *InternalUserQuery) GroupBy(field string, fields ...string) *InternalUserGroupBy { + _q.ctx.Fields = append([]string{field}, fields...) + grbuild := &InternalUserGroupBy{build: _q} + grbuild.flds = &_q.ctx.Fields + grbuild.label = internaluser.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 { +// ClerkSubject string `json:"clerk_subject,omitempty"` +// } +// +// client.InternalUser.Query(). +// Select(internaluser.FieldClerkSubject). +// Scan(ctx, &v) +func (_q *InternalUserQuery) Select(fields ...string) *InternalUserSelect { + _q.ctx.Fields = append(_q.ctx.Fields, fields...) + sbuild := &InternalUserSelect{InternalUserQuery: _q} + sbuild.label = internaluser.Label + sbuild.flds, sbuild.scan = &_q.ctx.Fields, sbuild.Scan + return sbuild +} + +// Aggregate returns a InternalUserSelect configured with the given aggregations. +func (_q *InternalUserQuery) Aggregate(fns ...AggregateFunc) *InternalUserSelect { + return _q.Select().Aggregate(fns...) +} + +func (_q *InternalUserQuery) 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 !internaluser.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 *InternalUserQuery) sqlAll(ctx context.Context, hooks ...queryHook) ([]*InternalUser, error) { + var ( + nodes = []*InternalUser{} + _spec = _q.querySpec() + ) + _spec.ScanValues = func(columns []string) ([]any, error) { + return (*InternalUser).scanValues(nil, columns) + } + _spec.Assign = func(columns []string, values []any) error { + node := &InternalUser{config: _q.config} + nodes = append(nodes, node) + return node.assignValues(columns, values) + } + for i := range hooks { + hooks[i](ctx, _spec) + } + if err := sqlgraph.QueryNodes(ctx, _q.driver, _spec); err != nil { + return nil, err + } + if len(nodes) == 0 { + return nodes, nil + } + return nodes, nil +} + +func (_q *InternalUserQuery) sqlCount(ctx context.Context) (int, error) { + _spec := _q.querySpec() + _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 *InternalUserQuery) querySpec() *sqlgraph.QuerySpec { + _spec := sqlgraph.NewQuerySpec(internaluser.Table, internaluser.Columns, sqlgraph.NewFieldSpec(internaluser.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, internaluser.FieldID) + for i := range fields { + if fields[i] != internaluser.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 *InternalUserQuery) sqlQuery(ctx context.Context) *sql.Selector { + builder := sql.Dialect(_q.driver.Dialect()) + t1 := builder.Table(internaluser.Table) + columns := _q.ctx.Fields + if len(columns) == 0 { + columns = internaluser.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 _, 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 +} + +// InternalUserGroupBy is the group-by builder for InternalUser entities. +type InternalUserGroupBy struct { + selector + build *InternalUserQuery +} + +// Aggregate adds the given aggregation functions to the group-by query. +func (_g *InternalUserGroupBy) Aggregate(fns ...AggregateFunc) *InternalUserGroupBy { + _g.fns = append(_g.fns, fns...) + return _g +} + +// Scan applies the selector query and scans the result into the given value. +func (_g *InternalUserGroupBy) 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[*InternalUserQuery, *InternalUserGroupBy](ctx, _g.build, _g, _g.build.inters, v) +} + +func (_g *InternalUserGroupBy) sqlScan(ctx context.Context, root *InternalUserQuery, 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) +} + +// InternalUserSelect is the builder for selecting fields of InternalUser entities. +type InternalUserSelect struct { + *InternalUserQuery + selector +} + +// Aggregate adds the given aggregation functions to the selector query. +func (_s *InternalUserSelect) Aggregate(fns ...AggregateFunc) *InternalUserSelect { + _s.fns = append(_s.fns, fns...) + return _s +} + +// Scan applies the selector query and scans the result into the given value. +func (_s *InternalUserSelect) 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[*InternalUserQuery, *InternalUserSelect](ctx, _s.InternalUserQuery, _s, _s.inters, v) +} + +func (_s *InternalUserSelect) sqlScan(ctx context.Context, root *InternalUserQuery, 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/backend/ent/internaluser_update.go b/backend/ent/internaluser_update.go new file mode 100644 index 0000000..aaa2464 --- /dev/null +++ b/backend/ent/internaluser_update.go @@ -0,0 +1,212 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "context" + "errors" + "fmt" + "time" + + "entgo.io/ent/dialect/sql" + "entgo.io/ent/dialect/sql/sqlgraph" + "entgo.io/ent/schema/field" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/internaluser" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/predicate" +) + +// InternalUserUpdate is the builder for updating InternalUser entities. +type InternalUserUpdate struct { + config + hooks []Hook + mutation *InternalUserMutation +} + +// Where appends a list predicates to the InternalUserUpdate builder. +func (_u *InternalUserUpdate) Where(ps ...predicate.InternalUser) *InternalUserUpdate { + _u.mutation.Where(ps...) + return _u +} + +// SetUpdatedAt sets the "updated_at" field. +func (_u *InternalUserUpdate) SetUpdatedAt(v time.Time) *InternalUserUpdate { + _u.mutation.SetUpdatedAt(v) + return _u +} + +// Mutation returns the InternalUserMutation object of the builder. +func (_u *InternalUserUpdate) Mutation() *InternalUserMutation { + return _u.mutation +} + +// Save executes the query and returns the number of nodes affected by the update operation. +func (_u *InternalUserUpdate) Save(ctx context.Context) (int, error) { + _u.defaults() + return withHooks(ctx, _u.sqlSave, _u.mutation, _u.hooks) +} + +// SaveX is like Save, but panics if an error occurs. +func (_u *InternalUserUpdate) SaveX(ctx context.Context) int { + affected, err := _u.Save(ctx) + if err != nil { + panic(err) + } + return affected +} + +// Exec executes the query. +func (_u *InternalUserUpdate) Exec(ctx context.Context) error { + _, err := _u.Save(ctx) + return err +} + +// ExecX is like Exec, but panics if an error occurs. +func (_u *InternalUserUpdate) ExecX(ctx context.Context) { + if err := _u.Exec(ctx); err != nil { + panic(err) + } +} + +// defaults sets the default values of the builder before save. +func (_u *InternalUserUpdate) defaults() { + if _, ok := _u.mutation.UpdatedAt(); !ok { + v := internaluser.UpdateDefaultUpdatedAt() + _u.mutation.SetUpdatedAt(v) + } +} + +func (_u *InternalUserUpdate) sqlSave(ctx context.Context) (_node int, err error) { + _spec := sqlgraph.NewUpdateSpec(internaluser.Table, internaluser.Columns, sqlgraph.NewFieldSpec(internaluser.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.UpdatedAt(); ok { + _spec.SetField(internaluser.FieldUpdatedAt, field.TypeTime, value) + } + if _node, err = sqlgraph.UpdateNodes(ctx, _u.driver, _spec); err != nil { + if _, ok := err.(*sqlgraph.NotFoundError); ok { + err = &NotFoundError{internaluser.Label} + } else if sqlgraph.IsConstraintError(err) { + err = &ConstraintError{msg: err.Error(), wrap: err} + } + return 0, err + } + _u.mutation.done = true + return _node, nil +} + +// InternalUserUpdateOne is the builder for updating a single InternalUser entity. +type InternalUserUpdateOne struct { + config + fields []string + hooks []Hook + mutation *InternalUserMutation +} + +// SetUpdatedAt sets the "updated_at" field. +func (_u *InternalUserUpdateOne) SetUpdatedAt(v time.Time) *InternalUserUpdateOne { + _u.mutation.SetUpdatedAt(v) + return _u +} + +// Mutation returns the InternalUserMutation object of the builder. +func (_u *InternalUserUpdateOne) Mutation() *InternalUserMutation { + return _u.mutation +} + +// Where appends a list predicates to the InternalUserUpdate builder. +func (_u *InternalUserUpdateOne) Where(ps ...predicate.InternalUser) *InternalUserUpdateOne { + _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 *InternalUserUpdateOne) Select(field string, fields ...string) *InternalUserUpdateOne { + _u.fields = append([]string{field}, fields...) + return _u +} + +// Save executes the query and returns the updated InternalUser entity. +func (_u *InternalUserUpdateOne) Save(ctx context.Context) (*InternalUser, error) { + _u.defaults() + return withHooks(ctx, _u.sqlSave, _u.mutation, _u.hooks) +} + +// SaveX is like Save, but panics if an error occurs. +func (_u *InternalUserUpdateOne) SaveX(ctx context.Context) *InternalUser { + node, err := _u.Save(ctx) + if err != nil { + panic(err) + } + return node +} + +// Exec executes the query on the entity. +func (_u *InternalUserUpdateOne) Exec(ctx context.Context) error { + _, err := _u.Save(ctx) + return err +} + +// ExecX is like Exec, but panics if an error occurs. +func (_u *InternalUserUpdateOne) ExecX(ctx context.Context) { + if err := _u.Exec(ctx); err != nil { + panic(err) + } +} + +// defaults sets the default values of the builder before save. +func (_u *InternalUserUpdateOne) defaults() { + if _, ok := _u.mutation.UpdatedAt(); !ok { + v := internaluser.UpdateDefaultUpdatedAt() + _u.mutation.SetUpdatedAt(v) + } +} + +func (_u *InternalUserUpdateOne) sqlSave(ctx context.Context) (_node *InternalUser, err error) { + _spec := sqlgraph.NewUpdateSpec(internaluser.Table, internaluser.Columns, sqlgraph.NewFieldSpec(internaluser.FieldID, field.TypeUUID)) + id, ok := _u.mutation.ID() + if !ok { + return nil, &ValidationError{Name: "id", err: errors.New(`ent: missing "InternalUser.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, internaluser.FieldID) + for _, f := range fields { + if !internaluser.ValidColumn(f) { + return nil, &ValidationError{Name: f, err: fmt.Errorf("ent: invalid field %q for query", f)} + } + if f != internaluser.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.UpdatedAt(); ok { + _spec.SetField(internaluser.FieldUpdatedAt, field.TypeTime, value) + } + _node = &InternalUser{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{internaluser.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/backend/ent/listing.go b/backend/ent/listing.go new file mode 100644 index 0000000..354fe47 --- /dev/null +++ b/backend/ent/listing.go @@ -0,0 +1,268 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "fmt" + "strings" + "time" + + "entgo.io/ent" + "entgo.io/ent/dialect/sql" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/listing" + "github.com/google/uuid" +) + +// Listing is the model entity for the Listing schema. +type Listing struct { + config `json:"-"` + // ID of the ent. + ID uuid.UUID `json:"id,omitempty"` + // InternalUserID holds the value of the "internal_user_id" field. + InternalUserID uuid.UUID `json:"internal_user_id,omitempty"` + // CategoryID holds the value of the "category_id" field. + CategoryID uuid.UUID `json:"category_id,omitempty"` + // PrimaryLocalityID holds the value of the "primary_locality_id" field. + PrimaryLocalityID uuid.UUID `json:"primary_locality_id,omitempty"` + // Title holds the value of the "title" field. + Title string `json:"title,omitempty"` + // Description holds the value of the "description" field. + Description string `json:"description,omitempty"` + // PriceType holds the value of the "price_type" field. + PriceType listing.PriceType `json:"price_type,omitempty"` + // PriceMinor holds the value of the "price_minor" field. + PriceMinor *int `json:"price_minor,omitempty"` + // Currency holds the value of the "currency" field. + Currency string `json:"currency,omitempty"` + // TravelsToCustomer holds the value of the "travels_to_customer" field. + TravelsToCustomer bool `json:"travels_to_customer,omitempty"` + // ReceivesCustomer holds the value of the "receives_customer" field. + ReceivesCustomer bool `json:"receives_customer,omitempty"` + // RemoteServices holds the value of the "remote_services" field. + RemoteServices bool `json:"remote_services,omitempty"` + // State holds the value of the "state" field. + State listing.State `json:"state,omitempty"` + // Revision holds the value of the "revision" field. + Revision int `json:"revision,omitempty"` + // CreatedAt holds the value of the "created_at" field. + CreatedAt time.Time `json:"created_at,omitempty"` + // UpdatedAt holds the value of the "updated_at" field. + UpdatedAt time.Time `json:"updated_at,omitempty"` + selectValues sql.SelectValues +} + +// scanValues returns the types for scanning values from sql.Rows. +func (*Listing) scanValues(columns []string) ([]any, error) { + values := make([]any, len(columns)) + for i := range columns { + switch columns[i] { + case listing.FieldTravelsToCustomer, listing.FieldReceivesCustomer, listing.FieldRemoteServices: + values[i] = new(sql.NullBool) + case listing.FieldPriceMinor, listing.FieldRevision: + values[i] = new(sql.NullInt64) + case listing.FieldTitle, listing.FieldDescription, listing.FieldPriceType, listing.FieldCurrency, listing.FieldState: + values[i] = new(sql.NullString) + case listing.FieldCreatedAt, listing.FieldUpdatedAt: + values[i] = new(sql.NullTime) + case listing.FieldID, listing.FieldInternalUserID, listing.FieldCategoryID, listing.FieldPrimaryLocalityID: + 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 Listing fields. +func (_m *Listing) 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 listing.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 listing.FieldInternalUserID: + if value, ok := values[i].(*uuid.UUID); !ok { + return fmt.Errorf("unexpected type %T for field internal_user_id", values[i]) + } else if value != nil { + _m.InternalUserID = *value + } + case listing.FieldCategoryID: + if value, ok := values[i].(*uuid.UUID); !ok { + return fmt.Errorf("unexpected type %T for field category_id", values[i]) + } else if value != nil { + _m.CategoryID = *value + } + case listing.FieldPrimaryLocalityID: + if value, ok := values[i].(*uuid.UUID); !ok { + return fmt.Errorf("unexpected type %T for field primary_locality_id", values[i]) + } else if value != nil { + _m.PrimaryLocalityID = *value + } + case listing.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 listing.FieldDescription: + if value, ok := values[i].(*sql.NullString); !ok { + return fmt.Errorf("unexpected type %T for field description", values[i]) + } else if value.Valid { + _m.Description = value.String + } + case listing.FieldPriceType: + if value, ok := values[i].(*sql.NullString); !ok { + return fmt.Errorf("unexpected type %T for field price_type", values[i]) + } else if value.Valid { + _m.PriceType = listing.PriceType(value.String) + } + case listing.FieldPriceMinor: + if value, ok := values[i].(*sql.NullInt64); !ok { + return fmt.Errorf("unexpected type %T for field price_minor", values[i]) + } else if value.Valid { + _m.PriceMinor = new(int) + *_m.PriceMinor = int(value.Int64) + } + case listing.FieldCurrency: + if value, ok := values[i].(*sql.NullString); !ok { + return fmt.Errorf("unexpected type %T for field currency", values[i]) + } else if value.Valid { + _m.Currency = value.String + } + case listing.FieldTravelsToCustomer: + if value, ok := values[i].(*sql.NullBool); !ok { + return fmt.Errorf("unexpected type %T for field travels_to_customer", values[i]) + } else if value.Valid { + _m.TravelsToCustomer = value.Bool + } + case listing.FieldReceivesCustomer: + if value, ok := values[i].(*sql.NullBool); !ok { + return fmt.Errorf("unexpected type %T for field receives_customer", values[i]) + } else if value.Valid { + _m.ReceivesCustomer = value.Bool + } + case listing.FieldRemoteServices: + if value, ok := values[i].(*sql.NullBool); !ok { + return fmt.Errorf("unexpected type %T for field remote_services", values[i]) + } else if value.Valid { + _m.RemoteServices = value.Bool + } + case listing.FieldState: + if value, ok := values[i].(*sql.NullString); !ok { + return fmt.Errorf("unexpected type %T for field state", values[i]) + } else if value.Valid { + _m.State = listing.State(value.String) + } + case listing.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) + } + case listing.FieldCreatedAt: + if value, ok := values[i].(*sql.NullTime); !ok { + return fmt.Errorf("unexpected type %T for field created_at", values[i]) + } else if value.Valid { + _m.CreatedAt = value.Time + } + case listing.FieldUpdatedAt: + if value, ok := values[i].(*sql.NullTime); !ok { + return fmt.Errorf("unexpected type %T for field updated_at", values[i]) + } else if value.Valid { + _m.UpdatedAt = value.Time + } + default: + _m.selectValues.Set(columns[i], values[i]) + } + } + return nil +} + +// Value returns the ent.Value that was dynamically selected and assigned to the Listing. +// This includes values selected through modifiers, order, etc. +func (_m *Listing) Value(name string) (ent.Value, error) { + return _m.selectValues.Get(name) +} + +// Update returns a builder for updating this Listing. +// Note that you need to call Listing.Unwrap() before calling this method if this Listing +// was returned from a transaction, and the transaction was committed or rolled back. +func (_m *Listing) Update() *ListingUpdateOne { + return NewListingClient(_m.config).UpdateOne(_m) +} + +// Unwrap unwraps the Listing 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 *Listing) Unwrap() *Listing { + _tx, ok := _m.config.driver.(*txDriver) + if !ok { + panic("ent: Listing is not a transactional entity") + } + _m.config.driver = _tx.drv + return _m +} + +// String implements the fmt.Stringer. +func (_m *Listing) String() string { + var builder strings.Builder + builder.WriteString("Listing(") + builder.WriteString(fmt.Sprintf("id=%v, ", _m.ID)) + builder.WriteString("internal_user_id=") + builder.WriteString(fmt.Sprintf("%v", _m.InternalUserID)) + builder.WriteString(", ") + builder.WriteString("category_id=") + builder.WriteString(fmt.Sprintf("%v", _m.CategoryID)) + builder.WriteString(", ") + builder.WriteString("primary_locality_id=") + builder.WriteString(fmt.Sprintf("%v", _m.PrimaryLocalityID)) + builder.WriteString(", ") + builder.WriteString("title=") + builder.WriteString(_m.Title) + builder.WriteString(", ") + builder.WriteString("description=") + builder.WriteString(_m.Description) + builder.WriteString(", ") + builder.WriteString("price_type=") + builder.WriteString(fmt.Sprintf("%v", _m.PriceType)) + builder.WriteString(", ") + if v := _m.PriceMinor; v != nil { + builder.WriteString("price_minor=") + builder.WriteString(fmt.Sprintf("%v", *v)) + } + builder.WriteString(", ") + builder.WriteString("currency=") + builder.WriteString(_m.Currency) + builder.WriteString(", ") + builder.WriteString("travels_to_customer=") + builder.WriteString(fmt.Sprintf("%v", _m.TravelsToCustomer)) + builder.WriteString(", ") + builder.WriteString("receives_customer=") + builder.WriteString(fmt.Sprintf("%v", _m.ReceivesCustomer)) + builder.WriteString(", ") + builder.WriteString("remote_services=") + builder.WriteString(fmt.Sprintf("%v", _m.RemoteServices)) + builder.WriteString(", ") + builder.WriteString("state=") + builder.WriteString(fmt.Sprintf("%v", _m.State)) + builder.WriteString(", ") + builder.WriteString("revision=") + builder.WriteString(fmt.Sprintf("%v", _m.Revision)) + builder.WriteString(", ") + builder.WriteString("created_at=") + builder.WriteString(_m.CreatedAt.Format(time.ANSIC)) + builder.WriteString(", ") + builder.WriteString("updated_at=") + builder.WriteString(_m.UpdatedAt.Format(time.ANSIC)) + builder.WriteByte(')') + return builder.String() +} + +// Listings is a parsable slice of Listing. +type Listings []*Listing diff --git a/backend/ent/listing/listing.go b/backend/ent/listing/listing.go new file mode 100644 index 0000000..56cff7c --- /dev/null +++ b/backend/ent/listing/listing.go @@ -0,0 +1,250 @@ +// Code generated by ent, DO NOT EDIT. + +package listing + +import ( + "fmt" + "time" + + "entgo.io/ent/dialect/sql" + "github.com/google/uuid" +) + +const ( + // Label holds the string label denoting the listing type in the database. + Label = "listing" + // FieldID holds the string denoting the id field in the database. + FieldID = "id" + // FieldInternalUserID holds the string denoting the internal_user_id field in the database. + FieldInternalUserID = "internal_user_id" + // FieldCategoryID holds the string denoting the category_id field in the database. + FieldCategoryID = "category_id" + // FieldPrimaryLocalityID holds the string denoting the primary_locality_id field in the database. + FieldPrimaryLocalityID = "primary_locality_id" + // FieldTitle holds the string denoting the title field in the database. + FieldTitle = "title" + // FieldDescription holds the string denoting the description field in the database. + FieldDescription = "description" + // FieldPriceType holds the string denoting the price_type field in the database. + FieldPriceType = "price_type" + // FieldPriceMinor holds the string denoting the price_minor field in the database. + FieldPriceMinor = "price_minor" + // FieldCurrency holds the string denoting the currency field in the database. + FieldCurrency = "currency" + // FieldTravelsToCustomer holds the string denoting the travels_to_customer field in the database. + FieldTravelsToCustomer = "travels_to_customer" + // FieldReceivesCustomer holds the string denoting the receives_customer field in the database. + FieldReceivesCustomer = "receives_customer" + // FieldRemoteServices holds the string denoting the remote_services field in the database. + FieldRemoteServices = "remote_services" + // FieldState holds the string denoting the state field in the database. + FieldState = "state" + // FieldRevision holds the string denoting the revision field in the database. + FieldRevision = "revision" + // FieldCreatedAt holds the string denoting the created_at field in the database. + FieldCreatedAt = "created_at" + // FieldUpdatedAt holds the string denoting the updated_at field in the database. + FieldUpdatedAt = "updated_at" + // Table holds the table name of the listing in the database. + Table = "listings" +) + +// Columns holds all SQL columns for listing fields. +var Columns = []string{ + FieldID, + FieldInternalUserID, + FieldCategoryID, + FieldPrimaryLocalityID, + FieldTitle, + FieldDescription, + FieldPriceType, + FieldPriceMinor, + FieldCurrency, + FieldTravelsToCustomer, + FieldReceivesCustomer, + FieldRemoteServices, + FieldState, + FieldRevision, + FieldCreatedAt, + FieldUpdatedAt, +} + +// 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 + // DescriptionValidator is a validator for the "description" field. It is called by the builders before save. + DescriptionValidator func(string) error + // PriceMinorValidator is a validator for the "price_minor" field. It is called by the builders before save. + PriceMinorValidator func(int) error + // DefaultCurrency holds the default value on creation for the "currency" field. + DefaultCurrency string + // CurrencyValidator is a validator for the "currency" field. It is called by the builders before save. + CurrencyValidator func(string) error + // DefaultTravelsToCustomer holds the default value on creation for the "travels_to_customer" field. + DefaultTravelsToCustomer bool + // DefaultReceivesCustomer holds the default value on creation for the "receives_customer" field. + DefaultReceivesCustomer bool + // DefaultRemoteServices holds the default value on creation for the "remote_services" field. + DefaultRemoteServices bool + // 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 + // DefaultCreatedAt holds the default value on creation for the "created_at" field. + DefaultCreatedAt func() time.Time + // DefaultUpdatedAt holds the default value on creation for the "updated_at" field. + DefaultUpdatedAt func() time.Time + // UpdateDefaultUpdatedAt holds the default value on update for the "updated_at" field. + UpdateDefaultUpdatedAt func() time.Time + // DefaultID holds the default value on creation for the "id" field. + DefaultID func() uuid.UUID +) + +// PriceType defines the type for the "price_type" enum field. +type PriceType string + +// PriceType values. +const ( + PriceTypeFixed PriceType = "fixed" + PriceTypeHourly PriceType = "hourly" + PriceTypeDaily PriceType = "daily" + PriceTypeQuote PriceType = "quote" + PriceTypeNegotiable PriceType = "negotiable" +) + +func (pt PriceType) String() string { + return string(pt) +} + +// PriceTypeValidator is a validator for the "price_type" field enum values. It is called by the builders before save. +func PriceTypeValidator(pt PriceType) error { + switch pt { + case PriceTypeFixed, PriceTypeHourly, PriceTypeDaily, PriceTypeQuote, PriceTypeNegotiable: + return nil + default: + return fmt.Errorf("listing: invalid enum value for price_type field: %q", pt) + } +} + +// State defines the type for the "state" enum field. +type State string + +// StateDraft is the default value of the State enum. +const DefaultState = StateDraft + +// State values. +const ( + StateDraft State = "draft" + StatePendingReview State = "pending_review" + StateActive State = "active" + StateRejected State = "rejected" + StatePaused State = "paused" + StateArchived State = "archived" +) + +func (s State) String() string { + return string(s) +} + +// StateValidator is a validator for the "state" field enum values. It is called by the builders before save. +func StateValidator(s State) error { + switch s { + case StateDraft, StatePendingReview, StateActive, StateRejected, StatePaused, StateArchived: + return nil + default: + return fmt.Errorf("listing: invalid enum value for state field: %q", s) + } +} + +// OrderOption defines the ordering options for the Listing 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() +} + +// ByInternalUserID orders the results by the internal_user_id field. +func ByInternalUserID(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldInternalUserID, opts...).ToFunc() +} + +// ByCategoryID orders the results by the category_id field. +func ByCategoryID(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldCategoryID, opts...).ToFunc() +} + +// ByPrimaryLocalityID orders the results by the primary_locality_id field. +func ByPrimaryLocalityID(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldPrimaryLocalityID, opts...).ToFunc() +} + +// ByTitle orders the results by the title field. +func ByTitle(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldTitle, opts...).ToFunc() +} + +// ByDescription orders the results by the description field. +func ByDescription(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldDescription, opts...).ToFunc() +} + +// ByPriceType orders the results by the price_type field. +func ByPriceType(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldPriceType, opts...).ToFunc() +} + +// ByPriceMinor orders the results by the price_minor field. +func ByPriceMinor(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldPriceMinor, opts...).ToFunc() +} + +// ByCurrency orders the results by the currency field. +func ByCurrency(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldCurrency, opts...).ToFunc() +} + +// ByTravelsToCustomer orders the results by the travels_to_customer field. +func ByTravelsToCustomer(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldTravelsToCustomer, opts...).ToFunc() +} + +// ByReceivesCustomer orders the results by the receives_customer field. +func ByReceivesCustomer(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldReceivesCustomer, opts...).ToFunc() +} + +// ByRemoteServices orders the results by the remote_services field. +func ByRemoteServices(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldRemoteServices, opts...).ToFunc() +} + +// ByState orders the results by the state field. +func ByState(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldState, opts...).ToFunc() +} + +// ByRevision orders the results by the revision field. +func ByRevision(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldRevision, opts...).ToFunc() +} + +// ByCreatedAt orders the results by the created_at field. +func ByCreatedAt(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldCreatedAt, opts...).ToFunc() +} + +// ByUpdatedAt orders the results by the updated_at field. +func ByUpdatedAt(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldUpdatedAt, opts...).ToFunc() +} diff --git a/backend/ent/listing/where.go b/backend/ent/listing/where.go new file mode 100644 index 0000000..8c62b6b --- /dev/null +++ b/backend/ent/listing/where.go @@ -0,0 +1,691 @@ +// Code generated by ent, DO NOT EDIT. + +package listing + +import ( + "time" + + "entgo.io/ent/dialect/sql" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/predicate" + "github.com/google/uuid" +) + +// ID filters vertices based on their ID field. +func ID(id uuid.UUID) predicate.Listing { + return predicate.Listing(sql.FieldEQ(FieldID, id)) +} + +// IDEQ applies the EQ predicate on the ID field. +func IDEQ(id uuid.UUID) predicate.Listing { + return predicate.Listing(sql.FieldEQ(FieldID, id)) +} + +// IDNEQ applies the NEQ predicate on the ID field. +func IDNEQ(id uuid.UUID) predicate.Listing { + return predicate.Listing(sql.FieldNEQ(FieldID, id)) +} + +// IDIn applies the In predicate on the ID field. +func IDIn(ids ...uuid.UUID) predicate.Listing { + return predicate.Listing(sql.FieldIn(FieldID, ids...)) +} + +// IDNotIn applies the NotIn predicate on the ID field. +func IDNotIn(ids ...uuid.UUID) predicate.Listing { + return predicate.Listing(sql.FieldNotIn(FieldID, ids...)) +} + +// IDGT applies the GT predicate on the ID field. +func IDGT(id uuid.UUID) predicate.Listing { + return predicate.Listing(sql.FieldGT(FieldID, id)) +} + +// IDGTE applies the GTE predicate on the ID field. +func IDGTE(id uuid.UUID) predicate.Listing { + return predicate.Listing(sql.FieldGTE(FieldID, id)) +} + +// IDLT applies the LT predicate on the ID field. +func IDLT(id uuid.UUID) predicate.Listing { + return predicate.Listing(sql.FieldLT(FieldID, id)) +} + +// IDLTE applies the LTE predicate on the ID field. +func IDLTE(id uuid.UUID) predicate.Listing { + return predicate.Listing(sql.FieldLTE(FieldID, id)) +} + +// InternalUserID applies equality check predicate on the "internal_user_id" field. It's identical to InternalUserIDEQ. +func InternalUserID(v uuid.UUID) predicate.Listing { + return predicate.Listing(sql.FieldEQ(FieldInternalUserID, v)) +} + +// CategoryID applies equality check predicate on the "category_id" field. It's identical to CategoryIDEQ. +func CategoryID(v uuid.UUID) predicate.Listing { + return predicate.Listing(sql.FieldEQ(FieldCategoryID, v)) +} + +// PrimaryLocalityID applies equality check predicate on the "primary_locality_id" field. It's identical to PrimaryLocalityIDEQ. +func PrimaryLocalityID(v uuid.UUID) predicate.Listing { + return predicate.Listing(sql.FieldEQ(FieldPrimaryLocalityID, v)) +} + +// Title applies equality check predicate on the "title" field. It's identical to TitleEQ. +func Title(v string) predicate.Listing { + return predicate.Listing(sql.FieldEQ(FieldTitle, v)) +} + +// Description applies equality check predicate on the "description" field. It's identical to DescriptionEQ. +func Description(v string) predicate.Listing { + return predicate.Listing(sql.FieldEQ(FieldDescription, v)) +} + +// PriceMinor applies equality check predicate on the "price_minor" field. It's identical to PriceMinorEQ. +func PriceMinor(v int) predicate.Listing { + return predicate.Listing(sql.FieldEQ(FieldPriceMinor, v)) +} + +// Currency applies equality check predicate on the "currency" field. It's identical to CurrencyEQ. +func Currency(v string) predicate.Listing { + return predicate.Listing(sql.FieldEQ(FieldCurrency, v)) +} + +// TravelsToCustomer applies equality check predicate on the "travels_to_customer" field. It's identical to TravelsToCustomerEQ. +func TravelsToCustomer(v bool) predicate.Listing { + return predicate.Listing(sql.FieldEQ(FieldTravelsToCustomer, v)) +} + +// ReceivesCustomer applies equality check predicate on the "receives_customer" field. It's identical to ReceivesCustomerEQ. +func ReceivesCustomer(v bool) predicate.Listing { + return predicate.Listing(sql.FieldEQ(FieldReceivesCustomer, v)) +} + +// RemoteServices applies equality check predicate on the "remote_services" field. It's identical to RemoteServicesEQ. +func RemoteServices(v bool) predicate.Listing { + return predicate.Listing(sql.FieldEQ(FieldRemoteServices, v)) +} + +// Revision applies equality check predicate on the "revision" field. It's identical to RevisionEQ. +func Revision(v int) predicate.Listing { + return predicate.Listing(sql.FieldEQ(FieldRevision, v)) +} + +// CreatedAt applies equality check predicate on the "created_at" field. It's identical to CreatedAtEQ. +func CreatedAt(v time.Time) predicate.Listing { + return predicate.Listing(sql.FieldEQ(FieldCreatedAt, v)) +} + +// UpdatedAt applies equality check predicate on the "updated_at" field. It's identical to UpdatedAtEQ. +func UpdatedAt(v time.Time) predicate.Listing { + return predicate.Listing(sql.FieldEQ(FieldUpdatedAt, v)) +} + +// InternalUserIDEQ applies the EQ predicate on the "internal_user_id" field. +func InternalUserIDEQ(v uuid.UUID) predicate.Listing { + return predicate.Listing(sql.FieldEQ(FieldInternalUserID, v)) +} + +// InternalUserIDNEQ applies the NEQ predicate on the "internal_user_id" field. +func InternalUserIDNEQ(v uuid.UUID) predicate.Listing { + return predicate.Listing(sql.FieldNEQ(FieldInternalUserID, v)) +} + +// InternalUserIDIn applies the In predicate on the "internal_user_id" field. +func InternalUserIDIn(vs ...uuid.UUID) predicate.Listing { + return predicate.Listing(sql.FieldIn(FieldInternalUserID, vs...)) +} + +// InternalUserIDNotIn applies the NotIn predicate on the "internal_user_id" field. +func InternalUserIDNotIn(vs ...uuid.UUID) predicate.Listing { + return predicate.Listing(sql.FieldNotIn(FieldInternalUserID, vs...)) +} + +// InternalUserIDGT applies the GT predicate on the "internal_user_id" field. +func InternalUserIDGT(v uuid.UUID) predicate.Listing { + return predicate.Listing(sql.FieldGT(FieldInternalUserID, v)) +} + +// InternalUserIDGTE applies the GTE predicate on the "internal_user_id" field. +func InternalUserIDGTE(v uuid.UUID) predicate.Listing { + return predicate.Listing(sql.FieldGTE(FieldInternalUserID, v)) +} + +// InternalUserIDLT applies the LT predicate on the "internal_user_id" field. +func InternalUserIDLT(v uuid.UUID) predicate.Listing { + return predicate.Listing(sql.FieldLT(FieldInternalUserID, v)) +} + +// InternalUserIDLTE applies the LTE predicate on the "internal_user_id" field. +func InternalUserIDLTE(v uuid.UUID) predicate.Listing { + return predicate.Listing(sql.FieldLTE(FieldInternalUserID, v)) +} + +// CategoryIDEQ applies the EQ predicate on the "category_id" field. +func CategoryIDEQ(v uuid.UUID) predicate.Listing { + return predicate.Listing(sql.FieldEQ(FieldCategoryID, v)) +} + +// CategoryIDNEQ applies the NEQ predicate on the "category_id" field. +func CategoryIDNEQ(v uuid.UUID) predicate.Listing { + return predicate.Listing(sql.FieldNEQ(FieldCategoryID, v)) +} + +// CategoryIDIn applies the In predicate on the "category_id" field. +func CategoryIDIn(vs ...uuid.UUID) predicate.Listing { + return predicate.Listing(sql.FieldIn(FieldCategoryID, vs...)) +} + +// CategoryIDNotIn applies the NotIn predicate on the "category_id" field. +func CategoryIDNotIn(vs ...uuid.UUID) predicate.Listing { + return predicate.Listing(sql.FieldNotIn(FieldCategoryID, vs...)) +} + +// CategoryIDGT applies the GT predicate on the "category_id" field. +func CategoryIDGT(v uuid.UUID) predicate.Listing { + return predicate.Listing(sql.FieldGT(FieldCategoryID, v)) +} + +// CategoryIDGTE applies the GTE predicate on the "category_id" field. +func CategoryIDGTE(v uuid.UUID) predicate.Listing { + return predicate.Listing(sql.FieldGTE(FieldCategoryID, v)) +} + +// CategoryIDLT applies the LT predicate on the "category_id" field. +func CategoryIDLT(v uuid.UUID) predicate.Listing { + return predicate.Listing(sql.FieldLT(FieldCategoryID, v)) +} + +// CategoryIDLTE applies the LTE predicate on the "category_id" field. +func CategoryIDLTE(v uuid.UUID) predicate.Listing { + return predicate.Listing(sql.FieldLTE(FieldCategoryID, v)) +} + +// PrimaryLocalityIDEQ applies the EQ predicate on the "primary_locality_id" field. +func PrimaryLocalityIDEQ(v uuid.UUID) predicate.Listing { + return predicate.Listing(sql.FieldEQ(FieldPrimaryLocalityID, v)) +} + +// PrimaryLocalityIDNEQ applies the NEQ predicate on the "primary_locality_id" field. +func PrimaryLocalityIDNEQ(v uuid.UUID) predicate.Listing { + return predicate.Listing(sql.FieldNEQ(FieldPrimaryLocalityID, v)) +} + +// PrimaryLocalityIDIn applies the In predicate on the "primary_locality_id" field. +func PrimaryLocalityIDIn(vs ...uuid.UUID) predicate.Listing { + return predicate.Listing(sql.FieldIn(FieldPrimaryLocalityID, vs...)) +} + +// PrimaryLocalityIDNotIn applies the NotIn predicate on the "primary_locality_id" field. +func PrimaryLocalityIDNotIn(vs ...uuid.UUID) predicate.Listing { + return predicate.Listing(sql.FieldNotIn(FieldPrimaryLocalityID, vs...)) +} + +// PrimaryLocalityIDGT applies the GT predicate on the "primary_locality_id" field. +func PrimaryLocalityIDGT(v uuid.UUID) predicate.Listing { + return predicate.Listing(sql.FieldGT(FieldPrimaryLocalityID, v)) +} + +// PrimaryLocalityIDGTE applies the GTE predicate on the "primary_locality_id" field. +func PrimaryLocalityIDGTE(v uuid.UUID) predicate.Listing { + return predicate.Listing(sql.FieldGTE(FieldPrimaryLocalityID, v)) +} + +// PrimaryLocalityIDLT applies the LT predicate on the "primary_locality_id" field. +func PrimaryLocalityIDLT(v uuid.UUID) predicate.Listing { + return predicate.Listing(sql.FieldLT(FieldPrimaryLocalityID, v)) +} + +// PrimaryLocalityIDLTE applies the LTE predicate on the "primary_locality_id" field. +func PrimaryLocalityIDLTE(v uuid.UUID) predicate.Listing { + return predicate.Listing(sql.FieldLTE(FieldPrimaryLocalityID, v)) +} + +// TitleEQ applies the EQ predicate on the "title" field. +func TitleEQ(v string) predicate.Listing { + return predicate.Listing(sql.FieldEQ(FieldTitle, v)) +} + +// TitleNEQ applies the NEQ predicate on the "title" field. +func TitleNEQ(v string) predicate.Listing { + return predicate.Listing(sql.FieldNEQ(FieldTitle, v)) +} + +// TitleIn applies the In predicate on the "title" field. +func TitleIn(vs ...string) predicate.Listing { + return predicate.Listing(sql.FieldIn(FieldTitle, vs...)) +} + +// TitleNotIn applies the NotIn predicate on the "title" field. +func TitleNotIn(vs ...string) predicate.Listing { + return predicate.Listing(sql.FieldNotIn(FieldTitle, vs...)) +} + +// TitleGT applies the GT predicate on the "title" field. +func TitleGT(v string) predicate.Listing { + return predicate.Listing(sql.FieldGT(FieldTitle, v)) +} + +// TitleGTE applies the GTE predicate on the "title" field. +func TitleGTE(v string) predicate.Listing { + return predicate.Listing(sql.FieldGTE(FieldTitle, v)) +} + +// TitleLT applies the LT predicate on the "title" field. +func TitleLT(v string) predicate.Listing { + return predicate.Listing(sql.FieldLT(FieldTitle, v)) +} + +// TitleLTE applies the LTE predicate on the "title" field. +func TitleLTE(v string) predicate.Listing { + return predicate.Listing(sql.FieldLTE(FieldTitle, v)) +} + +// TitleContains applies the Contains predicate on the "title" field. +func TitleContains(v string) predicate.Listing { + return predicate.Listing(sql.FieldContains(FieldTitle, v)) +} + +// TitleHasPrefix applies the HasPrefix predicate on the "title" field. +func TitleHasPrefix(v string) predicate.Listing { + return predicate.Listing(sql.FieldHasPrefix(FieldTitle, v)) +} + +// TitleHasSuffix applies the HasSuffix predicate on the "title" field. +func TitleHasSuffix(v string) predicate.Listing { + return predicate.Listing(sql.FieldHasSuffix(FieldTitle, v)) +} + +// TitleEqualFold applies the EqualFold predicate on the "title" field. +func TitleEqualFold(v string) predicate.Listing { + return predicate.Listing(sql.FieldEqualFold(FieldTitle, v)) +} + +// TitleContainsFold applies the ContainsFold predicate on the "title" field. +func TitleContainsFold(v string) predicate.Listing { + return predicate.Listing(sql.FieldContainsFold(FieldTitle, v)) +} + +// DescriptionEQ applies the EQ predicate on the "description" field. +func DescriptionEQ(v string) predicate.Listing { + return predicate.Listing(sql.FieldEQ(FieldDescription, v)) +} + +// DescriptionNEQ applies the NEQ predicate on the "description" field. +func DescriptionNEQ(v string) predicate.Listing { + return predicate.Listing(sql.FieldNEQ(FieldDescription, v)) +} + +// DescriptionIn applies the In predicate on the "description" field. +func DescriptionIn(vs ...string) predicate.Listing { + return predicate.Listing(sql.FieldIn(FieldDescription, vs...)) +} + +// DescriptionNotIn applies the NotIn predicate on the "description" field. +func DescriptionNotIn(vs ...string) predicate.Listing { + return predicate.Listing(sql.FieldNotIn(FieldDescription, vs...)) +} + +// DescriptionGT applies the GT predicate on the "description" field. +func DescriptionGT(v string) predicate.Listing { + return predicate.Listing(sql.FieldGT(FieldDescription, v)) +} + +// DescriptionGTE applies the GTE predicate on the "description" field. +func DescriptionGTE(v string) predicate.Listing { + return predicate.Listing(sql.FieldGTE(FieldDescription, v)) +} + +// DescriptionLT applies the LT predicate on the "description" field. +func DescriptionLT(v string) predicate.Listing { + return predicate.Listing(sql.FieldLT(FieldDescription, v)) +} + +// DescriptionLTE applies the LTE predicate on the "description" field. +func DescriptionLTE(v string) predicate.Listing { + return predicate.Listing(sql.FieldLTE(FieldDescription, v)) +} + +// DescriptionContains applies the Contains predicate on the "description" field. +func DescriptionContains(v string) predicate.Listing { + return predicate.Listing(sql.FieldContains(FieldDescription, v)) +} + +// DescriptionHasPrefix applies the HasPrefix predicate on the "description" field. +func DescriptionHasPrefix(v string) predicate.Listing { + return predicate.Listing(sql.FieldHasPrefix(FieldDescription, v)) +} + +// DescriptionHasSuffix applies the HasSuffix predicate on the "description" field. +func DescriptionHasSuffix(v string) predicate.Listing { + return predicate.Listing(sql.FieldHasSuffix(FieldDescription, v)) +} + +// DescriptionEqualFold applies the EqualFold predicate on the "description" field. +func DescriptionEqualFold(v string) predicate.Listing { + return predicate.Listing(sql.FieldEqualFold(FieldDescription, v)) +} + +// DescriptionContainsFold applies the ContainsFold predicate on the "description" field. +func DescriptionContainsFold(v string) predicate.Listing { + return predicate.Listing(sql.FieldContainsFold(FieldDescription, v)) +} + +// PriceTypeEQ applies the EQ predicate on the "price_type" field. +func PriceTypeEQ(v PriceType) predicate.Listing { + return predicate.Listing(sql.FieldEQ(FieldPriceType, v)) +} + +// PriceTypeNEQ applies the NEQ predicate on the "price_type" field. +func PriceTypeNEQ(v PriceType) predicate.Listing { + return predicate.Listing(sql.FieldNEQ(FieldPriceType, v)) +} + +// PriceTypeIn applies the In predicate on the "price_type" field. +func PriceTypeIn(vs ...PriceType) predicate.Listing { + return predicate.Listing(sql.FieldIn(FieldPriceType, vs...)) +} + +// PriceTypeNotIn applies the NotIn predicate on the "price_type" field. +func PriceTypeNotIn(vs ...PriceType) predicate.Listing { + return predicate.Listing(sql.FieldNotIn(FieldPriceType, vs...)) +} + +// PriceMinorEQ applies the EQ predicate on the "price_minor" field. +func PriceMinorEQ(v int) predicate.Listing { + return predicate.Listing(sql.FieldEQ(FieldPriceMinor, v)) +} + +// PriceMinorNEQ applies the NEQ predicate on the "price_minor" field. +func PriceMinorNEQ(v int) predicate.Listing { + return predicate.Listing(sql.FieldNEQ(FieldPriceMinor, v)) +} + +// PriceMinorIn applies the In predicate on the "price_minor" field. +func PriceMinorIn(vs ...int) predicate.Listing { + return predicate.Listing(sql.FieldIn(FieldPriceMinor, vs...)) +} + +// PriceMinorNotIn applies the NotIn predicate on the "price_minor" field. +func PriceMinorNotIn(vs ...int) predicate.Listing { + return predicate.Listing(sql.FieldNotIn(FieldPriceMinor, vs...)) +} + +// PriceMinorGT applies the GT predicate on the "price_minor" field. +func PriceMinorGT(v int) predicate.Listing { + return predicate.Listing(sql.FieldGT(FieldPriceMinor, v)) +} + +// PriceMinorGTE applies the GTE predicate on the "price_minor" field. +func PriceMinorGTE(v int) predicate.Listing { + return predicate.Listing(sql.FieldGTE(FieldPriceMinor, v)) +} + +// PriceMinorLT applies the LT predicate on the "price_minor" field. +func PriceMinorLT(v int) predicate.Listing { + return predicate.Listing(sql.FieldLT(FieldPriceMinor, v)) +} + +// PriceMinorLTE applies the LTE predicate on the "price_minor" field. +func PriceMinorLTE(v int) predicate.Listing { + return predicate.Listing(sql.FieldLTE(FieldPriceMinor, v)) +} + +// PriceMinorIsNil applies the IsNil predicate on the "price_minor" field. +func PriceMinorIsNil() predicate.Listing { + return predicate.Listing(sql.FieldIsNull(FieldPriceMinor)) +} + +// PriceMinorNotNil applies the NotNil predicate on the "price_minor" field. +func PriceMinorNotNil() predicate.Listing { + return predicate.Listing(sql.FieldNotNull(FieldPriceMinor)) +} + +// CurrencyEQ applies the EQ predicate on the "currency" field. +func CurrencyEQ(v string) predicate.Listing { + return predicate.Listing(sql.FieldEQ(FieldCurrency, v)) +} + +// CurrencyNEQ applies the NEQ predicate on the "currency" field. +func CurrencyNEQ(v string) predicate.Listing { + return predicate.Listing(sql.FieldNEQ(FieldCurrency, v)) +} + +// CurrencyIn applies the In predicate on the "currency" field. +func CurrencyIn(vs ...string) predicate.Listing { + return predicate.Listing(sql.FieldIn(FieldCurrency, vs...)) +} + +// CurrencyNotIn applies the NotIn predicate on the "currency" field. +func CurrencyNotIn(vs ...string) predicate.Listing { + return predicate.Listing(sql.FieldNotIn(FieldCurrency, vs...)) +} + +// CurrencyGT applies the GT predicate on the "currency" field. +func CurrencyGT(v string) predicate.Listing { + return predicate.Listing(sql.FieldGT(FieldCurrency, v)) +} + +// CurrencyGTE applies the GTE predicate on the "currency" field. +func CurrencyGTE(v string) predicate.Listing { + return predicate.Listing(sql.FieldGTE(FieldCurrency, v)) +} + +// CurrencyLT applies the LT predicate on the "currency" field. +func CurrencyLT(v string) predicate.Listing { + return predicate.Listing(sql.FieldLT(FieldCurrency, v)) +} + +// CurrencyLTE applies the LTE predicate on the "currency" field. +func CurrencyLTE(v string) predicate.Listing { + return predicate.Listing(sql.FieldLTE(FieldCurrency, v)) +} + +// CurrencyContains applies the Contains predicate on the "currency" field. +func CurrencyContains(v string) predicate.Listing { + return predicate.Listing(sql.FieldContains(FieldCurrency, v)) +} + +// CurrencyHasPrefix applies the HasPrefix predicate on the "currency" field. +func CurrencyHasPrefix(v string) predicate.Listing { + return predicate.Listing(sql.FieldHasPrefix(FieldCurrency, v)) +} + +// CurrencyHasSuffix applies the HasSuffix predicate on the "currency" field. +func CurrencyHasSuffix(v string) predicate.Listing { + return predicate.Listing(sql.FieldHasSuffix(FieldCurrency, v)) +} + +// CurrencyEqualFold applies the EqualFold predicate on the "currency" field. +func CurrencyEqualFold(v string) predicate.Listing { + return predicate.Listing(sql.FieldEqualFold(FieldCurrency, v)) +} + +// CurrencyContainsFold applies the ContainsFold predicate on the "currency" field. +func CurrencyContainsFold(v string) predicate.Listing { + return predicate.Listing(sql.FieldContainsFold(FieldCurrency, v)) +} + +// TravelsToCustomerEQ applies the EQ predicate on the "travels_to_customer" field. +func TravelsToCustomerEQ(v bool) predicate.Listing { + return predicate.Listing(sql.FieldEQ(FieldTravelsToCustomer, v)) +} + +// TravelsToCustomerNEQ applies the NEQ predicate on the "travels_to_customer" field. +func TravelsToCustomerNEQ(v bool) predicate.Listing { + return predicate.Listing(sql.FieldNEQ(FieldTravelsToCustomer, v)) +} + +// ReceivesCustomerEQ applies the EQ predicate on the "receives_customer" field. +func ReceivesCustomerEQ(v bool) predicate.Listing { + return predicate.Listing(sql.FieldEQ(FieldReceivesCustomer, v)) +} + +// ReceivesCustomerNEQ applies the NEQ predicate on the "receives_customer" field. +func ReceivesCustomerNEQ(v bool) predicate.Listing { + return predicate.Listing(sql.FieldNEQ(FieldReceivesCustomer, v)) +} + +// RemoteServicesEQ applies the EQ predicate on the "remote_services" field. +func RemoteServicesEQ(v bool) predicate.Listing { + return predicate.Listing(sql.FieldEQ(FieldRemoteServices, v)) +} + +// RemoteServicesNEQ applies the NEQ predicate on the "remote_services" field. +func RemoteServicesNEQ(v bool) predicate.Listing { + return predicate.Listing(sql.FieldNEQ(FieldRemoteServices, v)) +} + +// StateEQ applies the EQ predicate on the "state" field. +func StateEQ(v State) predicate.Listing { + return predicate.Listing(sql.FieldEQ(FieldState, v)) +} + +// StateNEQ applies the NEQ predicate on the "state" field. +func StateNEQ(v State) predicate.Listing { + return predicate.Listing(sql.FieldNEQ(FieldState, v)) +} + +// StateIn applies the In predicate on the "state" field. +func StateIn(vs ...State) predicate.Listing { + return predicate.Listing(sql.FieldIn(FieldState, vs...)) +} + +// StateNotIn applies the NotIn predicate on the "state" field. +func StateNotIn(vs ...State) predicate.Listing { + return predicate.Listing(sql.FieldNotIn(FieldState, vs...)) +} + +// RevisionEQ applies the EQ predicate on the "revision" field. +func RevisionEQ(v int) predicate.Listing { + return predicate.Listing(sql.FieldEQ(FieldRevision, v)) +} + +// RevisionNEQ applies the NEQ predicate on the "revision" field. +func RevisionNEQ(v int) predicate.Listing { + return predicate.Listing(sql.FieldNEQ(FieldRevision, v)) +} + +// RevisionIn applies the In predicate on the "revision" field. +func RevisionIn(vs ...int) predicate.Listing { + return predicate.Listing(sql.FieldIn(FieldRevision, vs...)) +} + +// RevisionNotIn applies the NotIn predicate on the "revision" field. +func RevisionNotIn(vs ...int) predicate.Listing { + return predicate.Listing(sql.FieldNotIn(FieldRevision, vs...)) +} + +// RevisionGT applies the GT predicate on the "revision" field. +func RevisionGT(v int) predicate.Listing { + return predicate.Listing(sql.FieldGT(FieldRevision, v)) +} + +// RevisionGTE applies the GTE predicate on the "revision" field. +func RevisionGTE(v int) predicate.Listing { + return predicate.Listing(sql.FieldGTE(FieldRevision, v)) +} + +// RevisionLT applies the LT predicate on the "revision" field. +func RevisionLT(v int) predicate.Listing { + return predicate.Listing(sql.FieldLT(FieldRevision, v)) +} + +// RevisionLTE applies the LTE predicate on the "revision" field. +func RevisionLTE(v int) predicate.Listing { + return predicate.Listing(sql.FieldLTE(FieldRevision, v)) +} + +// CreatedAtEQ applies the EQ predicate on the "created_at" field. +func CreatedAtEQ(v time.Time) predicate.Listing { + return predicate.Listing(sql.FieldEQ(FieldCreatedAt, v)) +} + +// CreatedAtNEQ applies the NEQ predicate on the "created_at" field. +func CreatedAtNEQ(v time.Time) predicate.Listing { + return predicate.Listing(sql.FieldNEQ(FieldCreatedAt, v)) +} + +// CreatedAtIn applies the In predicate on the "created_at" field. +func CreatedAtIn(vs ...time.Time) predicate.Listing { + return predicate.Listing(sql.FieldIn(FieldCreatedAt, vs...)) +} + +// CreatedAtNotIn applies the NotIn predicate on the "created_at" field. +func CreatedAtNotIn(vs ...time.Time) predicate.Listing { + return predicate.Listing(sql.FieldNotIn(FieldCreatedAt, vs...)) +} + +// CreatedAtGT applies the GT predicate on the "created_at" field. +func CreatedAtGT(v time.Time) predicate.Listing { + return predicate.Listing(sql.FieldGT(FieldCreatedAt, v)) +} + +// CreatedAtGTE applies the GTE predicate on the "created_at" field. +func CreatedAtGTE(v time.Time) predicate.Listing { + return predicate.Listing(sql.FieldGTE(FieldCreatedAt, v)) +} + +// CreatedAtLT applies the LT predicate on the "created_at" field. +func CreatedAtLT(v time.Time) predicate.Listing { + return predicate.Listing(sql.FieldLT(FieldCreatedAt, v)) +} + +// CreatedAtLTE applies the LTE predicate on the "created_at" field. +func CreatedAtLTE(v time.Time) predicate.Listing { + return predicate.Listing(sql.FieldLTE(FieldCreatedAt, v)) +} + +// UpdatedAtEQ applies the EQ predicate on the "updated_at" field. +func UpdatedAtEQ(v time.Time) predicate.Listing { + return predicate.Listing(sql.FieldEQ(FieldUpdatedAt, v)) +} + +// UpdatedAtNEQ applies the NEQ predicate on the "updated_at" field. +func UpdatedAtNEQ(v time.Time) predicate.Listing { + return predicate.Listing(sql.FieldNEQ(FieldUpdatedAt, v)) +} + +// UpdatedAtIn applies the In predicate on the "updated_at" field. +func UpdatedAtIn(vs ...time.Time) predicate.Listing { + return predicate.Listing(sql.FieldIn(FieldUpdatedAt, vs...)) +} + +// UpdatedAtNotIn applies the NotIn predicate on the "updated_at" field. +func UpdatedAtNotIn(vs ...time.Time) predicate.Listing { + return predicate.Listing(sql.FieldNotIn(FieldUpdatedAt, vs...)) +} + +// UpdatedAtGT applies the GT predicate on the "updated_at" field. +func UpdatedAtGT(v time.Time) predicate.Listing { + return predicate.Listing(sql.FieldGT(FieldUpdatedAt, v)) +} + +// UpdatedAtGTE applies the GTE predicate on the "updated_at" field. +func UpdatedAtGTE(v time.Time) predicate.Listing { + return predicate.Listing(sql.FieldGTE(FieldUpdatedAt, v)) +} + +// UpdatedAtLT applies the LT predicate on the "updated_at" field. +func UpdatedAtLT(v time.Time) predicate.Listing { + return predicate.Listing(sql.FieldLT(FieldUpdatedAt, v)) +} + +// UpdatedAtLTE applies the LTE predicate on the "updated_at" field. +func UpdatedAtLTE(v time.Time) predicate.Listing { + return predicate.Listing(sql.FieldLTE(FieldUpdatedAt, v)) +} + +// And groups predicates with the AND operator between them. +func And(predicates ...predicate.Listing) predicate.Listing { + return predicate.Listing(sql.AndPredicates(predicates...)) +} + +// Or groups predicates with the OR operator between them. +func Or(predicates ...predicate.Listing) predicate.Listing { + return predicate.Listing(sql.OrPredicates(predicates...)) +} + +// Not applies the not operator on the given predicate. +func Not(p predicate.Listing) predicate.Listing { + return predicate.Listing(sql.NotPredicates(p)) +} diff --git a/backend/ent/listing_create.go b/backend/ent/listing_create.go new file mode 100644 index 0000000..70c4274 --- /dev/null +++ b/backend/ent/listing_create.go @@ -0,0 +1,532 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "context" + "errors" + "fmt" + "time" + + "entgo.io/ent/dialect/sql/sqlgraph" + "entgo.io/ent/schema/field" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/listing" + "github.com/google/uuid" +) + +// ListingCreate is the builder for creating a Listing entity. +type ListingCreate struct { + config + mutation *ListingMutation + hooks []Hook +} + +// SetInternalUserID sets the "internal_user_id" field. +func (_c *ListingCreate) SetInternalUserID(v uuid.UUID) *ListingCreate { + _c.mutation.SetInternalUserID(v) + return _c +} + +// SetCategoryID sets the "category_id" field. +func (_c *ListingCreate) SetCategoryID(v uuid.UUID) *ListingCreate { + _c.mutation.SetCategoryID(v) + return _c +} + +// SetPrimaryLocalityID sets the "primary_locality_id" field. +func (_c *ListingCreate) SetPrimaryLocalityID(v uuid.UUID) *ListingCreate { + _c.mutation.SetPrimaryLocalityID(v) + return _c +} + +// SetTitle sets the "title" field. +func (_c *ListingCreate) SetTitle(v string) *ListingCreate { + _c.mutation.SetTitle(v) + return _c +} + +// SetDescription sets the "description" field. +func (_c *ListingCreate) SetDescription(v string) *ListingCreate { + _c.mutation.SetDescription(v) + return _c +} + +// SetPriceType sets the "price_type" field. +func (_c *ListingCreate) SetPriceType(v listing.PriceType) *ListingCreate { + _c.mutation.SetPriceType(v) + return _c +} + +// SetPriceMinor sets the "price_minor" field. +func (_c *ListingCreate) SetPriceMinor(v int) *ListingCreate { + _c.mutation.SetPriceMinor(v) + return _c +} + +// SetNillablePriceMinor sets the "price_minor" field if the given value is not nil. +func (_c *ListingCreate) SetNillablePriceMinor(v *int) *ListingCreate { + if v != nil { + _c.SetPriceMinor(*v) + } + return _c +} + +// SetCurrency sets the "currency" field. +func (_c *ListingCreate) SetCurrency(v string) *ListingCreate { + _c.mutation.SetCurrency(v) + return _c +} + +// SetNillableCurrency sets the "currency" field if the given value is not nil. +func (_c *ListingCreate) SetNillableCurrency(v *string) *ListingCreate { + if v != nil { + _c.SetCurrency(*v) + } + return _c +} + +// SetTravelsToCustomer sets the "travels_to_customer" field. +func (_c *ListingCreate) SetTravelsToCustomer(v bool) *ListingCreate { + _c.mutation.SetTravelsToCustomer(v) + return _c +} + +// SetNillableTravelsToCustomer sets the "travels_to_customer" field if the given value is not nil. +func (_c *ListingCreate) SetNillableTravelsToCustomer(v *bool) *ListingCreate { + if v != nil { + _c.SetTravelsToCustomer(*v) + } + return _c +} + +// SetReceivesCustomer sets the "receives_customer" field. +func (_c *ListingCreate) SetReceivesCustomer(v bool) *ListingCreate { + _c.mutation.SetReceivesCustomer(v) + return _c +} + +// SetNillableReceivesCustomer sets the "receives_customer" field if the given value is not nil. +func (_c *ListingCreate) SetNillableReceivesCustomer(v *bool) *ListingCreate { + if v != nil { + _c.SetReceivesCustomer(*v) + } + return _c +} + +// SetRemoteServices sets the "remote_services" field. +func (_c *ListingCreate) SetRemoteServices(v bool) *ListingCreate { + _c.mutation.SetRemoteServices(v) + return _c +} + +// SetNillableRemoteServices sets the "remote_services" field if the given value is not nil. +func (_c *ListingCreate) SetNillableRemoteServices(v *bool) *ListingCreate { + if v != nil { + _c.SetRemoteServices(*v) + } + return _c +} + +// SetState sets the "state" field. +func (_c *ListingCreate) SetState(v listing.State) *ListingCreate { + _c.mutation.SetState(v) + return _c +} + +// SetNillableState sets the "state" field if the given value is not nil. +func (_c *ListingCreate) SetNillableState(v *listing.State) *ListingCreate { + if v != nil { + _c.SetState(*v) + } + return _c +} + +// SetRevision sets the "revision" field. +func (_c *ListingCreate) SetRevision(v int) *ListingCreate { + _c.mutation.SetRevision(v) + return _c +} + +// SetNillableRevision sets the "revision" field if the given value is not nil. +func (_c *ListingCreate) SetNillableRevision(v *int) *ListingCreate { + if v != nil { + _c.SetRevision(*v) + } + return _c +} + +// SetCreatedAt sets the "created_at" field. +func (_c *ListingCreate) SetCreatedAt(v time.Time) *ListingCreate { + _c.mutation.SetCreatedAt(v) + return _c +} + +// SetNillableCreatedAt sets the "created_at" field if the given value is not nil. +func (_c *ListingCreate) SetNillableCreatedAt(v *time.Time) *ListingCreate { + if v != nil { + _c.SetCreatedAt(*v) + } + return _c +} + +// SetUpdatedAt sets the "updated_at" field. +func (_c *ListingCreate) SetUpdatedAt(v time.Time) *ListingCreate { + _c.mutation.SetUpdatedAt(v) + return _c +} + +// SetNillableUpdatedAt sets the "updated_at" field if the given value is not nil. +func (_c *ListingCreate) SetNillableUpdatedAt(v *time.Time) *ListingCreate { + if v != nil { + _c.SetUpdatedAt(*v) + } + return _c +} + +// SetID sets the "id" field. +func (_c *ListingCreate) SetID(v uuid.UUID) *ListingCreate { + _c.mutation.SetID(v) + return _c +} + +// SetNillableID sets the "id" field if the given value is not nil. +func (_c *ListingCreate) SetNillableID(v *uuid.UUID) *ListingCreate { + if v != nil { + _c.SetID(*v) + } + return _c +} + +// Mutation returns the ListingMutation object of the builder. +func (_c *ListingCreate) Mutation() *ListingMutation { + return _c.mutation +} + +// Save creates the Listing in the database. +func (_c *ListingCreate) Save(ctx context.Context) (*Listing, error) { + _c.defaults() + return withHooks(ctx, _c.sqlSave, _c.mutation, _c.hooks) +} + +// SaveX calls Save and panics if Save returns an error. +func (_c *ListingCreate) SaveX(ctx context.Context) *Listing { + v, err := _c.Save(ctx) + if err != nil { + panic(err) + } + return v +} + +// Exec executes the query. +func (_c *ListingCreate) Exec(ctx context.Context) error { + _, err := _c.Save(ctx) + return err +} + +// ExecX is like Exec, but panics if an error occurs. +func (_c *ListingCreate) 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 *ListingCreate) defaults() { + if _, ok := _c.mutation.Currency(); !ok { + v := listing.DefaultCurrency + _c.mutation.SetCurrency(v) + } + if _, ok := _c.mutation.TravelsToCustomer(); !ok { + v := listing.DefaultTravelsToCustomer + _c.mutation.SetTravelsToCustomer(v) + } + if _, ok := _c.mutation.ReceivesCustomer(); !ok { + v := listing.DefaultReceivesCustomer + _c.mutation.SetReceivesCustomer(v) + } + if _, ok := _c.mutation.RemoteServices(); !ok { + v := listing.DefaultRemoteServices + _c.mutation.SetRemoteServices(v) + } + if _, ok := _c.mutation.State(); !ok { + v := listing.DefaultState + _c.mutation.SetState(v) + } + if _, ok := _c.mutation.Revision(); !ok { + v := listing.DefaultRevision + _c.mutation.SetRevision(v) + } + if _, ok := _c.mutation.CreatedAt(); !ok { + v := listing.DefaultCreatedAt() + _c.mutation.SetCreatedAt(v) + } + if _, ok := _c.mutation.UpdatedAt(); !ok { + v := listing.DefaultUpdatedAt() + _c.mutation.SetUpdatedAt(v) + } + if _, ok := _c.mutation.ID(); !ok { + v := listing.DefaultID() + _c.mutation.SetID(v) + } +} + +// check runs all checks and user-defined validators on the builder. +func (_c *ListingCreate) check() error { + if _, ok := _c.mutation.InternalUserID(); !ok { + return &ValidationError{Name: "internal_user_id", err: errors.New(`ent: missing required field "Listing.internal_user_id"`)} + } + if _, ok := _c.mutation.CategoryID(); !ok { + return &ValidationError{Name: "category_id", err: errors.New(`ent: missing required field "Listing.category_id"`)} + } + if _, ok := _c.mutation.PrimaryLocalityID(); !ok { + return &ValidationError{Name: "primary_locality_id", err: errors.New(`ent: missing required field "Listing.primary_locality_id"`)} + } + if _, ok := _c.mutation.Title(); !ok { + return &ValidationError{Name: "title", err: errors.New(`ent: missing required field "Listing.title"`)} + } + if v, ok := _c.mutation.Title(); ok { + if err := listing.TitleValidator(v); err != nil { + return &ValidationError{Name: "title", err: fmt.Errorf(`ent: validator failed for field "Listing.title": %w`, err)} + } + } + if _, ok := _c.mutation.Description(); !ok { + return &ValidationError{Name: "description", err: errors.New(`ent: missing required field "Listing.description"`)} + } + if v, ok := _c.mutation.Description(); ok { + if err := listing.DescriptionValidator(v); err != nil { + return &ValidationError{Name: "description", err: fmt.Errorf(`ent: validator failed for field "Listing.description": %w`, err)} + } + } + if _, ok := _c.mutation.PriceType(); !ok { + return &ValidationError{Name: "price_type", err: errors.New(`ent: missing required field "Listing.price_type"`)} + } + if v, ok := _c.mutation.PriceType(); ok { + if err := listing.PriceTypeValidator(v); err != nil { + return &ValidationError{Name: "price_type", err: fmt.Errorf(`ent: validator failed for field "Listing.price_type": %w`, err)} + } + } + if v, ok := _c.mutation.PriceMinor(); ok { + if err := listing.PriceMinorValidator(v); err != nil { + return &ValidationError{Name: "price_minor", err: fmt.Errorf(`ent: validator failed for field "Listing.price_minor": %w`, err)} + } + } + if _, ok := _c.mutation.Currency(); !ok { + return &ValidationError{Name: "currency", err: errors.New(`ent: missing required field "Listing.currency"`)} + } + if v, ok := _c.mutation.Currency(); ok { + if err := listing.CurrencyValidator(v); err != nil { + return &ValidationError{Name: "currency", err: fmt.Errorf(`ent: validator failed for field "Listing.currency": %w`, err)} + } + } + if _, ok := _c.mutation.TravelsToCustomer(); !ok { + return &ValidationError{Name: "travels_to_customer", err: errors.New(`ent: missing required field "Listing.travels_to_customer"`)} + } + if _, ok := _c.mutation.ReceivesCustomer(); !ok { + return &ValidationError{Name: "receives_customer", err: errors.New(`ent: missing required field "Listing.receives_customer"`)} + } + if _, ok := _c.mutation.RemoteServices(); !ok { + return &ValidationError{Name: "remote_services", err: errors.New(`ent: missing required field "Listing.remote_services"`)} + } + if _, ok := _c.mutation.State(); !ok { + return &ValidationError{Name: "state", err: errors.New(`ent: missing required field "Listing.state"`)} + } + if v, ok := _c.mutation.State(); ok { + if err := listing.StateValidator(v); err != nil { + return &ValidationError{Name: "state", err: fmt.Errorf(`ent: validator failed for field "Listing.state": %w`, err)} + } + } + if _, ok := _c.mutation.Revision(); !ok { + return &ValidationError{Name: "revision", err: errors.New(`ent: missing required field "Listing.revision"`)} + } + if v, ok := _c.mutation.Revision(); ok { + if err := listing.RevisionValidator(v); err != nil { + return &ValidationError{Name: "revision", err: fmt.Errorf(`ent: validator failed for field "Listing.revision": %w`, err)} + } + } + if _, ok := _c.mutation.CreatedAt(); !ok { + return &ValidationError{Name: "created_at", err: errors.New(`ent: missing required field "Listing.created_at"`)} + } + if _, ok := _c.mutation.UpdatedAt(); !ok { + return &ValidationError{Name: "updated_at", err: errors.New(`ent: missing required field "Listing.updated_at"`)} + } + return nil +} + +func (_c *ListingCreate) sqlSave(ctx context.Context) (*Listing, 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 *ListingCreate) createSpec() (*Listing, *sqlgraph.CreateSpec) { + var ( + _node = &Listing{config: _c.config} + _spec = sqlgraph.NewCreateSpec(listing.Table, sqlgraph.NewFieldSpec(listing.FieldID, field.TypeUUID)) + ) + if id, ok := _c.mutation.ID(); ok { + _node.ID = id + _spec.ID.Value = &id + } + if value, ok := _c.mutation.InternalUserID(); ok { + _spec.SetField(listing.FieldInternalUserID, field.TypeUUID, value) + _node.InternalUserID = value + } + if value, ok := _c.mutation.CategoryID(); ok { + _spec.SetField(listing.FieldCategoryID, field.TypeUUID, value) + _node.CategoryID = value + } + if value, ok := _c.mutation.PrimaryLocalityID(); ok { + _spec.SetField(listing.FieldPrimaryLocalityID, field.TypeUUID, value) + _node.PrimaryLocalityID = value + } + if value, ok := _c.mutation.Title(); ok { + _spec.SetField(listing.FieldTitle, field.TypeString, value) + _node.Title = value + } + if value, ok := _c.mutation.Description(); ok { + _spec.SetField(listing.FieldDescription, field.TypeString, value) + _node.Description = value + } + if value, ok := _c.mutation.PriceType(); ok { + _spec.SetField(listing.FieldPriceType, field.TypeEnum, value) + _node.PriceType = value + } + if value, ok := _c.mutation.PriceMinor(); ok { + _spec.SetField(listing.FieldPriceMinor, field.TypeInt, value) + _node.PriceMinor = &value + } + if value, ok := _c.mutation.Currency(); ok { + _spec.SetField(listing.FieldCurrency, field.TypeString, value) + _node.Currency = value + } + if value, ok := _c.mutation.TravelsToCustomer(); ok { + _spec.SetField(listing.FieldTravelsToCustomer, field.TypeBool, value) + _node.TravelsToCustomer = value + } + if value, ok := _c.mutation.ReceivesCustomer(); ok { + _spec.SetField(listing.FieldReceivesCustomer, field.TypeBool, value) + _node.ReceivesCustomer = value + } + if value, ok := _c.mutation.RemoteServices(); ok { + _spec.SetField(listing.FieldRemoteServices, field.TypeBool, value) + _node.RemoteServices = value + } + if value, ok := _c.mutation.State(); ok { + _spec.SetField(listing.FieldState, field.TypeEnum, value) + _node.State = value + } + if value, ok := _c.mutation.Revision(); ok { + _spec.SetField(listing.FieldRevision, field.TypeInt, value) + _node.Revision = value + } + if value, ok := _c.mutation.CreatedAt(); ok { + _spec.SetField(listing.FieldCreatedAt, field.TypeTime, value) + _node.CreatedAt = value + } + if value, ok := _c.mutation.UpdatedAt(); ok { + _spec.SetField(listing.FieldUpdatedAt, field.TypeTime, value) + _node.UpdatedAt = value + } + return _node, _spec +} + +// ListingCreateBulk is the builder for creating many Listing entities in bulk. +type ListingCreateBulk struct { + config + err error + builders []*ListingCreate +} + +// Save creates the Listing entities in the database. +func (_c *ListingCreateBulk) Save(ctx context.Context) ([]*Listing, error) { + if _c.err != nil { + return nil, _c.err + } + specs := make([]*sqlgraph.CreateSpec, len(_c.builders)) + nodes := make([]*Listing, 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.(*ListingMutation) + 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 *ListingCreateBulk) SaveX(ctx context.Context) []*Listing { + v, err := _c.Save(ctx) + if err != nil { + panic(err) + } + return v +} + +// Exec executes the query. +func (_c *ListingCreateBulk) Exec(ctx context.Context) error { + _, err := _c.Save(ctx) + return err +} + +// ExecX is like Exec, but panics if an error occurs. +func (_c *ListingCreateBulk) ExecX(ctx context.Context) { + if err := _c.Exec(ctx); err != nil { + panic(err) + } +} diff --git a/backend/ent/listing_delete.go b/backend/ent/listing_delete.go new file mode 100644 index 0000000..a2f775b --- /dev/null +++ b/backend/ent/listing_delete.go @@ -0,0 +1,88 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "context" + + "entgo.io/ent/dialect/sql" + "entgo.io/ent/dialect/sql/sqlgraph" + "entgo.io/ent/schema/field" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/listing" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/predicate" +) + +// ListingDelete is the builder for deleting a Listing entity. +type ListingDelete struct { + config + hooks []Hook + mutation *ListingMutation +} + +// Where appends a list predicates to the ListingDelete builder. +func (_d *ListingDelete) Where(ps ...predicate.Listing) *ListingDelete { + _d.mutation.Where(ps...) + return _d +} + +// Exec executes the deletion query and returns how many vertices were deleted. +func (_d *ListingDelete) 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 *ListingDelete) ExecX(ctx context.Context) int { + n, err := _d.Exec(ctx) + if err != nil { + panic(err) + } + return n +} + +func (_d *ListingDelete) sqlExec(ctx context.Context) (int, error) { + _spec := sqlgraph.NewDeleteSpec(listing.Table, sqlgraph.NewFieldSpec(listing.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 +} + +// ListingDeleteOne is the builder for deleting a single Listing entity. +type ListingDeleteOne struct { + _d *ListingDelete +} + +// Where appends a list predicates to the ListingDelete builder. +func (_d *ListingDeleteOne) Where(ps ...predicate.Listing) *ListingDeleteOne { + _d._d.mutation.Where(ps...) + return _d +} + +// Exec executes the deletion query. +func (_d *ListingDeleteOne) Exec(ctx context.Context) error { + n, err := _d._d.Exec(ctx) + switch { + case err != nil: + return err + case n == 0: + return &NotFoundError{listing.Label} + default: + return nil + } +} + +// ExecX is like Exec, but panics if an error occurs. +func (_d *ListingDeleteOne) ExecX(ctx context.Context) { + if err := _d.Exec(ctx); err != nil { + panic(err) + } +} diff --git a/backend/ent/listing_query.go b/backend/ent/listing_query.go new file mode 100644 index 0000000..fd0f241 --- /dev/null +++ b/backend/ent/listing_query.go @@ -0,0 +1,528 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "context" + "fmt" + "math" + + "entgo.io/ent" + "entgo.io/ent/dialect/sql" + "entgo.io/ent/dialect/sql/sqlgraph" + "entgo.io/ent/schema/field" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/listing" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/predicate" + "github.com/google/uuid" +) + +// ListingQuery is the builder for querying Listing entities. +type ListingQuery struct { + config + ctx *QueryContext + order []listing.OrderOption + inters []Interceptor + predicates []predicate.Listing + // intermediate query (i.e. traversal path). + sql *sql.Selector + path func(context.Context) (*sql.Selector, error) +} + +// Where adds a new predicate for the ListingQuery builder. +func (_q *ListingQuery) Where(ps ...predicate.Listing) *ListingQuery { + _q.predicates = append(_q.predicates, ps...) + return _q +} + +// Limit the number of records to be returned by this query. +func (_q *ListingQuery) Limit(limit int) *ListingQuery { + _q.ctx.Limit = &limit + return _q +} + +// Offset to start from. +func (_q *ListingQuery) Offset(offset int) *ListingQuery { + _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 *ListingQuery) Unique(unique bool) *ListingQuery { + _q.ctx.Unique = &unique + return _q +} + +// Order specifies how the records should be ordered. +func (_q *ListingQuery) Order(o ...listing.OrderOption) *ListingQuery { + _q.order = append(_q.order, o...) + return _q +} + +// First returns the first Listing entity from the query. +// Returns a *NotFoundError when no Listing was found. +func (_q *ListingQuery) First(ctx context.Context) (*Listing, 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{listing.Label} + } + return nodes[0], nil +} + +// FirstX is like First, but panics if an error occurs. +func (_q *ListingQuery) FirstX(ctx context.Context) *Listing { + node, err := _q.First(ctx) + if err != nil && !IsNotFound(err) { + panic(err) + } + return node +} + +// FirstID returns the first Listing ID from the query. +// Returns a *NotFoundError when no Listing ID was found. +func (_q *ListingQuery) 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{listing.Label} + return + } + return ids[0], nil +} + +// FirstIDX is like FirstID, but panics if an error occurs. +func (_q *ListingQuery) FirstIDX(ctx context.Context) uuid.UUID { + id, err := _q.FirstID(ctx) + if err != nil && !IsNotFound(err) { + panic(err) + } + return id +} + +// Only returns a single Listing entity found by the query, ensuring it only returns one. +// Returns a *NotSingularError when more than one Listing entity is found. +// Returns a *NotFoundError when no Listing entities are found. +func (_q *ListingQuery) Only(ctx context.Context) (*Listing, 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{listing.Label} + default: + return nil, &NotSingularError{listing.Label} + } +} + +// OnlyX is like Only, but panics if an error occurs. +func (_q *ListingQuery) OnlyX(ctx context.Context) *Listing { + node, err := _q.Only(ctx) + if err != nil { + panic(err) + } + return node +} + +// OnlyID is like Only, but returns the only Listing ID in the query. +// Returns a *NotSingularError when more than one Listing ID is found. +// Returns a *NotFoundError when no entities are found. +func (_q *ListingQuery) 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{listing.Label} + default: + err = &NotSingularError{listing.Label} + } + return +} + +// OnlyIDX is like OnlyID, but panics if an error occurs. +func (_q *ListingQuery) 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 Listings. +func (_q *ListingQuery) All(ctx context.Context) ([]*Listing, error) { + ctx = setContextOp(ctx, _q.ctx, ent.OpQueryAll) + if err := _q.prepareQuery(ctx); err != nil { + return nil, err + } + qr := querierAll[[]*Listing, *ListingQuery]() + return withInterceptors[[]*Listing](ctx, _q, qr, _q.inters) +} + +// AllX is like All, but panics if an error occurs. +func (_q *ListingQuery) AllX(ctx context.Context) []*Listing { + nodes, err := _q.All(ctx) + if err != nil { + panic(err) + } + return nodes +} + +// IDs executes the query and returns a list of Listing IDs. +func (_q *ListingQuery) 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(listing.FieldID).Scan(ctx, &ids); err != nil { + return nil, err + } + return ids, nil +} + +// IDsX is like IDs, but panics if an error occurs. +func (_q *ListingQuery) 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 *ListingQuery) 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[*ListingQuery](), _q.inters) +} + +// CountX is like Count, but panics if an error occurs. +func (_q *ListingQuery) 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 *ListingQuery) 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 *ListingQuery) ExistX(ctx context.Context) bool { + exist, err := _q.Exist(ctx) + if err != nil { + panic(err) + } + return exist +} + +// Clone returns a duplicate of the ListingQuery 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 *ListingQuery) Clone() *ListingQuery { + if _q == nil { + return nil + } + return &ListingQuery{ + config: _q.config, + ctx: _q.ctx.Clone(), + order: append([]listing.OrderOption{}, _q.order...), + inters: append([]Interceptor{}, _q.inters...), + predicates: append([]predicate.Listing{}, _q.predicates...), + // clone intermediate query. + sql: _q.sql.Clone(), + path: _q.path, + } +} + +// GroupBy is used to group vertices by one or more fields/columns. +// It is often used with aggregate functions, like: count, max, mean, min, sum. +// +// Example: +// +// var v []struct { +// InternalUserID uuid.UUID `json:"internal_user_id,omitempty"` +// Count int `json:"count,omitempty"` +// } +// +// client.Listing.Query(). +// GroupBy(listing.FieldInternalUserID). +// Aggregate(ent.Count()). +// Scan(ctx, &v) +func (_q *ListingQuery) GroupBy(field string, fields ...string) *ListingGroupBy { + _q.ctx.Fields = append([]string{field}, fields...) + grbuild := &ListingGroupBy{build: _q} + grbuild.flds = &_q.ctx.Fields + grbuild.label = listing.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 { +// InternalUserID uuid.UUID `json:"internal_user_id,omitempty"` +// } +// +// client.Listing.Query(). +// Select(listing.FieldInternalUserID). +// Scan(ctx, &v) +func (_q *ListingQuery) Select(fields ...string) *ListingSelect { + _q.ctx.Fields = append(_q.ctx.Fields, fields...) + sbuild := &ListingSelect{ListingQuery: _q} + sbuild.label = listing.Label + sbuild.flds, sbuild.scan = &_q.ctx.Fields, sbuild.Scan + return sbuild +} + +// Aggregate returns a ListingSelect configured with the given aggregations. +func (_q *ListingQuery) Aggregate(fns ...AggregateFunc) *ListingSelect { + return _q.Select().Aggregate(fns...) +} + +func (_q *ListingQuery) 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 !listing.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 *ListingQuery) sqlAll(ctx context.Context, hooks ...queryHook) ([]*Listing, error) { + var ( + nodes = []*Listing{} + _spec = _q.querySpec() + ) + _spec.ScanValues = func(columns []string) ([]any, error) { + return (*Listing).scanValues(nil, columns) + } + _spec.Assign = func(columns []string, values []any) error { + node := &Listing{config: _q.config} + nodes = append(nodes, node) + return node.assignValues(columns, values) + } + for i := range hooks { + hooks[i](ctx, _spec) + } + if err := sqlgraph.QueryNodes(ctx, _q.driver, _spec); err != nil { + return nil, err + } + if len(nodes) == 0 { + return nodes, nil + } + return nodes, nil +} + +func (_q *ListingQuery) sqlCount(ctx context.Context) (int, error) { + _spec := _q.querySpec() + _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 *ListingQuery) querySpec() *sqlgraph.QuerySpec { + _spec := sqlgraph.NewQuerySpec(listing.Table, listing.Columns, sqlgraph.NewFieldSpec(listing.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, listing.FieldID) + for i := range fields { + if fields[i] != listing.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 *ListingQuery) sqlQuery(ctx context.Context) *sql.Selector { + builder := sql.Dialect(_q.driver.Dialect()) + t1 := builder.Table(listing.Table) + columns := _q.ctx.Fields + if len(columns) == 0 { + columns = listing.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 _, 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 +} + +// ListingGroupBy is the group-by builder for Listing entities. +type ListingGroupBy struct { + selector + build *ListingQuery +} + +// Aggregate adds the given aggregation functions to the group-by query. +func (_g *ListingGroupBy) Aggregate(fns ...AggregateFunc) *ListingGroupBy { + _g.fns = append(_g.fns, fns...) + return _g +} + +// Scan applies the selector query and scans the result into the given value. +func (_g *ListingGroupBy) 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[*ListingQuery, *ListingGroupBy](ctx, _g.build, _g, _g.build.inters, v) +} + +func (_g *ListingGroupBy) sqlScan(ctx context.Context, root *ListingQuery, 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) +} + +// ListingSelect is the builder for selecting fields of Listing entities. +type ListingSelect struct { + *ListingQuery + selector +} + +// Aggregate adds the given aggregation functions to the selector query. +func (_s *ListingSelect) Aggregate(fns ...AggregateFunc) *ListingSelect { + _s.fns = append(_s.fns, fns...) + return _s +} + +// Scan applies the selector query and scans the result into the given value. +func (_s *ListingSelect) 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[*ListingQuery, *ListingSelect](ctx, _s.ListingQuery, _s, _s.inters, v) +} + +func (_s *ListingSelect) sqlScan(ctx context.Context, root *ListingQuery, 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/backend/ent/listing_update.go b/backend/ent/listing_update.go new file mode 100644 index 0000000..3b49aa7 --- /dev/null +++ b/backend/ent/listing_update.go @@ -0,0 +1,721 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "context" + "errors" + "fmt" + "time" + + "entgo.io/ent/dialect/sql" + "entgo.io/ent/dialect/sql/sqlgraph" + "entgo.io/ent/schema/field" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/listing" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/predicate" + "github.com/google/uuid" +) + +// ListingUpdate is the builder for updating Listing entities. +type ListingUpdate struct { + config + hooks []Hook + mutation *ListingMutation +} + +// Where appends a list predicates to the ListingUpdate builder. +func (_u *ListingUpdate) Where(ps ...predicate.Listing) *ListingUpdate { + _u.mutation.Where(ps...) + return _u +} + +// SetCategoryID sets the "category_id" field. +func (_u *ListingUpdate) SetCategoryID(v uuid.UUID) *ListingUpdate { + _u.mutation.SetCategoryID(v) + return _u +} + +// SetNillableCategoryID sets the "category_id" field if the given value is not nil. +func (_u *ListingUpdate) SetNillableCategoryID(v *uuid.UUID) *ListingUpdate { + if v != nil { + _u.SetCategoryID(*v) + } + return _u +} + +// SetPrimaryLocalityID sets the "primary_locality_id" field. +func (_u *ListingUpdate) SetPrimaryLocalityID(v uuid.UUID) *ListingUpdate { + _u.mutation.SetPrimaryLocalityID(v) + return _u +} + +// SetNillablePrimaryLocalityID sets the "primary_locality_id" field if the given value is not nil. +func (_u *ListingUpdate) SetNillablePrimaryLocalityID(v *uuid.UUID) *ListingUpdate { + if v != nil { + _u.SetPrimaryLocalityID(*v) + } + return _u +} + +// SetTitle sets the "title" field. +func (_u *ListingUpdate) SetTitle(v string) *ListingUpdate { + _u.mutation.SetTitle(v) + return _u +} + +// SetNillableTitle sets the "title" field if the given value is not nil. +func (_u *ListingUpdate) SetNillableTitle(v *string) *ListingUpdate { + if v != nil { + _u.SetTitle(*v) + } + return _u +} + +// SetDescription sets the "description" field. +func (_u *ListingUpdate) SetDescription(v string) *ListingUpdate { + _u.mutation.SetDescription(v) + return _u +} + +// SetNillableDescription sets the "description" field if the given value is not nil. +func (_u *ListingUpdate) SetNillableDescription(v *string) *ListingUpdate { + if v != nil { + _u.SetDescription(*v) + } + return _u +} + +// SetPriceType sets the "price_type" field. +func (_u *ListingUpdate) SetPriceType(v listing.PriceType) *ListingUpdate { + _u.mutation.SetPriceType(v) + return _u +} + +// SetNillablePriceType sets the "price_type" field if the given value is not nil. +func (_u *ListingUpdate) SetNillablePriceType(v *listing.PriceType) *ListingUpdate { + if v != nil { + _u.SetPriceType(*v) + } + return _u +} + +// SetPriceMinor sets the "price_minor" field. +func (_u *ListingUpdate) SetPriceMinor(v int) *ListingUpdate { + _u.mutation.ResetPriceMinor() + _u.mutation.SetPriceMinor(v) + return _u +} + +// SetNillablePriceMinor sets the "price_minor" field if the given value is not nil. +func (_u *ListingUpdate) SetNillablePriceMinor(v *int) *ListingUpdate { + if v != nil { + _u.SetPriceMinor(*v) + } + return _u +} + +// AddPriceMinor adds value to the "price_minor" field. +func (_u *ListingUpdate) AddPriceMinor(v int) *ListingUpdate { + _u.mutation.AddPriceMinor(v) + return _u +} + +// ClearPriceMinor clears the value of the "price_minor" field. +func (_u *ListingUpdate) ClearPriceMinor() *ListingUpdate { + _u.mutation.ClearPriceMinor() + return _u +} + +// SetTravelsToCustomer sets the "travels_to_customer" field. +func (_u *ListingUpdate) SetTravelsToCustomer(v bool) *ListingUpdate { + _u.mutation.SetTravelsToCustomer(v) + return _u +} + +// SetNillableTravelsToCustomer sets the "travels_to_customer" field if the given value is not nil. +func (_u *ListingUpdate) SetNillableTravelsToCustomer(v *bool) *ListingUpdate { + if v != nil { + _u.SetTravelsToCustomer(*v) + } + return _u +} + +// SetReceivesCustomer sets the "receives_customer" field. +func (_u *ListingUpdate) SetReceivesCustomer(v bool) *ListingUpdate { + _u.mutation.SetReceivesCustomer(v) + return _u +} + +// SetNillableReceivesCustomer sets the "receives_customer" field if the given value is not nil. +func (_u *ListingUpdate) SetNillableReceivesCustomer(v *bool) *ListingUpdate { + if v != nil { + _u.SetReceivesCustomer(*v) + } + return _u +} + +// SetRemoteServices sets the "remote_services" field. +func (_u *ListingUpdate) SetRemoteServices(v bool) *ListingUpdate { + _u.mutation.SetRemoteServices(v) + return _u +} + +// SetNillableRemoteServices sets the "remote_services" field if the given value is not nil. +func (_u *ListingUpdate) SetNillableRemoteServices(v *bool) *ListingUpdate { + if v != nil { + _u.SetRemoteServices(*v) + } + return _u +} + +// SetState sets the "state" field. +func (_u *ListingUpdate) SetState(v listing.State) *ListingUpdate { + _u.mutation.SetState(v) + return _u +} + +// SetNillableState sets the "state" field if the given value is not nil. +func (_u *ListingUpdate) SetNillableState(v *listing.State) *ListingUpdate { + if v != nil { + _u.SetState(*v) + } + return _u +} + +// SetRevision sets the "revision" field. +func (_u *ListingUpdate) SetRevision(v int) *ListingUpdate { + _u.mutation.ResetRevision() + _u.mutation.SetRevision(v) + return _u +} + +// SetNillableRevision sets the "revision" field if the given value is not nil. +func (_u *ListingUpdate) SetNillableRevision(v *int) *ListingUpdate { + if v != nil { + _u.SetRevision(*v) + } + return _u +} + +// AddRevision adds value to the "revision" field. +func (_u *ListingUpdate) AddRevision(v int) *ListingUpdate { + _u.mutation.AddRevision(v) + return _u +} + +// SetUpdatedAt sets the "updated_at" field. +func (_u *ListingUpdate) SetUpdatedAt(v time.Time) *ListingUpdate { + _u.mutation.SetUpdatedAt(v) + return _u +} + +// Mutation returns the ListingMutation object of the builder. +func (_u *ListingUpdate) Mutation() *ListingMutation { + return _u.mutation +} + +// Save executes the query and returns the number of nodes affected by the update operation. +func (_u *ListingUpdate) Save(ctx context.Context) (int, error) { + _u.defaults() + return withHooks(ctx, _u.sqlSave, _u.mutation, _u.hooks) +} + +// SaveX is like Save, but panics if an error occurs. +func (_u *ListingUpdate) SaveX(ctx context.Context) int { + affected, err := _u.Save(ctx) + if err != nil { + panic(err) + } + return affected +} + +// Exec executes the query. +func (_u *ListingUpdate) Exec(ctx context.Context) error { + _, err := _u.Save(ctx) + return err +} + +// ExecX is like Exec, but panics if an error occurs. +func (_u *ListingUpdate) ExecX(ctx context.Context) { + if err := _u.Exec(ctx); err != nil { + panic(err) + } +} + +// defaults sets the default values of the builder before save. +func (_u *ListingUpdate) defaults() { + if _, ok := _u.mutation.UpdatedAt(); !ok { + v := listing.UpdateDefaultUpdatedAt() + _u.mutation.SetUpdatedAt(v) + } +} + +// check runs all checks and user-defined validators on the builder. +func (_u *ListingUpdate) check() error { + if v, ok := _u.mutation.Title(); ok { + if err := listing.TitleValidator(v); err != nil { + return &ValidationError{Name: "title", err: fmt.Errorf(`ent: validator failed for field "Listing.title": %w`, err)} + } + } + if v, ok := _u.mutation.Description(); ok { + if err := listing.DescriptionValidator(v); err != nil { + return &ValidationError{Name: "description", err: fmt.Errorf(`ent: validator failed for field "Listing.description": %w`, err)} + } + } + if v, ok := _u.mutation.PriceType(); ok { + if err := listing.PriceTypeValidator(v); err != nil { + return &ValidationError{Name: "price_type", err: fmt.Errorf(`ent: validator failed for field "Listing.price_type": %w`, err)} + } + } + if v, ok := _u.mutation.PriceMinor(); ok { + if err := listing.PriceMinorValidator(v); err != nil { + return &ValidationError{Name: "price_minor", err: fmt.Errorf(`ent: validator failed for field "Listing.price_minor": %w`, err)} + } + } + if v, ok := _u.mutation.State(); ok { + if err := listing.StateValidator(v); err != nil { + return &ValidationError{Name: "state", err: fmt.Errorf(`ent: validator failed for field "Listing.state": %w`, err)} + } + } + if v, ok := _u.mutation.Revision(); ok { + if err := listing.RevisionValidator(v); err != nil { + return &ValidationError{Name: "revision", err: fmt.Errorf(`ent: validator failed for field "Listing.revision": %w`, err)} + } + } + return nil +} + +func (_u *ListingUpdate) sqlSave(ctx context.Context) (_node int, err error) { + if err := _u.check(); err != nil { + return _node, err + } + _spec := sqlgraph.NewUpdateSpec(listing.Table, listing.Columns, sqlgraph.NewFieldSpec(listing.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.CategoryID(); ok { + _spec.SetField(listing.FieldCategoryID, field.TypeUUID, value) + } + if value, ok := _u.mutation.PrimaryLocalityID(); ok { + _spec.SetField(listing.FieldPrimaryLocalityID, field.TypeUUID, value) + } + if value, ok := _u.mutation.Title(); ok { + _spec.SetField(listing.FieldTitle, field.TypeString, value) + } + if value, ok := _u.mutation.Description(); ok { + _spec.SetField(listing.FieldDescription, field.TypeString, value) + } + if value, ok := _u.mutation.PriceType(); ok { + _spec.SetField(listing.FieldPriceType, field.TypeEnum, value) + } + if value, ok := _u.mutation.PriceMinor(); ok { + _spec.SetField(listing.FieldPriceMinor, field.TypeInt, value) + } + if value, ok := _u.mutation.AddedPriceMinor(); ok { + _spec.AddField(listing.FieldPriceMinor, field.TypeInt, value) + } + if _u.mutation.PriceMinorCleared() { + _spec.ClearField(listing.FieldPriceMinor, field.TypeInt) + } + if value, ok := _u.mutation.TravelsToCustomer(); ok { + _spec.SetField(listing.FieldTravelsToCustomer, field.TypeBool, value) + } + if value, ok := _u.mutation.ReceivesCustomer(); ok { + _spec.SetField(listing.FieldReceivesCustomer, field.TypeBool, value) + } + if value, ok := _u.mutation.RemoteServices(); ok { + _spec.SetField(listing.FieldRemoteServices, field.TypeBool, value) + } + if value, ok := _u.mutation.State(); ok { + _spec.SetField(listing.FieldState, field.TypeEnum, value) + } + if value, ok := _u.mutation.Revision(); ok { + _spec.SetField(listing.FieldRevision, field.TypeInt, value) + } + if value, ok := _u.mutation.AddedRevision(); ok { + _spec.AddField(listing.FieldRevision, field.TypeInt, value) + } + if value, ok := _u.mutation.UpdatedAt(); ok { + _spec.SetField(listing.FieldUpdatedAt, field.TypeTime, value) + } + if _node, err = sqlgraph.UpdateNodes(ctx, _u.driver, _spec); err != nil { + if _, ok := err.(*sqlgraph.NotFoundError); ok { + err = &NotFoundError{listing.Label} + } else if sqlgraph.IsConstraintError(err) { + err = &ConstraintError{msg: err.Error(), wrap: err} + } + return 0, err + } + _u.mutation.done = true + return _node, nil +} + +// ListingUpdateOne is the builder for updating a single Listing entity. +type ListingUpdateOne struct { + config + fields []string + hooks []Hook + mutation *ListingMutation +} + +// SetCategoryID sets the "category_id" field. +func (_u *ListingUpdateOne) SetCategoryID(v uuid.UUID) *ListingUpdateOne { + _u.mutation.SetCategoryID(v) + return _u +} + +// SetNillableCategoryID sets the "category_id" field if the given value is not nil. +func (_u *ListingUpdateOne) SetNillableCategoryID(v *uuid.UUID) *ListingUpdateOne { + if v != nil { + _u.SetCategoryID(*v) + } + return _u +} + +// SetPrimaryLocalityID sets the "primary_locality_id" field. +func (_u *ListingUpdateOne) SetPrimaryLocalityID(v uuid.UUID) *ListingUpdateOne { + _u.mutation.SetPrimaryLocalityID(v) + return _u +} + +// SetNillablePrimaryLocalityID sets the "primary_locality_id" field if the given value is not nil. +func (_u *ListingUpdateOne) SetNillablePrimaryLocalityID(v *uuid.UUID) *ListingUpdateOne { + if v != nil { + _u.SetPrimaryLocalityID(*v) + } + return _u +} + +// SetTitle sets the "title" field. +func (_u *ListingUpdateOne) SetTitle(v string) *ListingUpdateOne { + _u.mutation.SetTitle(v) + return _u +} + +// SetNillableTitle sets the "title" field if the given value is not nil. +func (_u *ListingUpdateOne) SetNillableTitle(v *string) *ListingUpdateOne { + if v != nil { + _u.SetTitle(*v) + } + return _u +} + +// SetDescription sets the "description" field. +func (_u *ListingUpdateOne) SetDescription(v string) *ListingUpdateOne { + _u.mutation.SetDescription(v) + return _u +} + +// SetNillableDescription sets the "description" field if the given value is not nil. +func (_u *ListingUpdateOne) SetNillableDescription(v *string) *ListingUpdateOne { + if v != nil { + _u.SetDescription(*v) + } + return _u +} + +// SetPriceType sets the "price_type" field. +func (_u *ListingUpdateOne) SetPriceType(v listing.PriceType) *ListingUpdateOne { + _u.mutation.SetPriceType(v) + return _u +} + +// SetNillablePriceType sets the "price_type" field if the given value is not nil. +func (_u *ListingUpdateOne) SetNillablePriceType(v *listing.PriceType) *ListingUpdateOne { + if v != nil { + _u.SetPriceType(*v) + } + return _u +} + +// SetPriceMinor sets the "price_minor" field. +func (_u *ListingUpdateOne) SetPriceMinor(v int) *ListingUpdateOne { + _u.mutation.ResetPriceMinor() + _u.mutation.SetPriceMinor(v) + return _u +} + +// SetNillablePriceMinor sets the "price_minor" field if the given value is not nil. +func (_u *ListingUpdateOne) SetNillablePriceMinor(v *int) *ListingUpdateOne { + if v != nil { + _u.SetPriceMinor(*v) + } + return _u +} + +// AddPriceMinor adds value to the "price_minor" field. +func (_u *ListingUpdateOne) AddPriceMinor(v int) *ListingUpdateOne { + _u.mutation.AddPriceMinor(v) + return _u +} + +// ClearPriceMinor clears the value of the "price_minor" field. +func (_u *ListingUpdateOne) ClearPriceMinor() *ListingUpdateOne { + _u.mutation.ClearPriceMinor() + return _u +} + +// SetTravelsToCustomer sets the "travels_to_customer" field. +func (_u *ListingUpdateOne) SetTravelsToCustomer(v bool) *ListingUpdateOne { + _u.mutation.SetTravelsToCustomer(v) + return _u +} + +// SetNillableTravelsToCustomer sets the "travels_to_customer" field if the given value is not nil. +func (_u *ListingUpdateOne) SetNillableTravelsToCustomer(v *bool) *ListingUpdateOne { + if v != nil { + _u.SetTravelsToCustomer(*v) + } + return _u +} + +// SetReceivesCustomer sets the "receives_customer" field. +func (_u *ListingUpdateOne) SetReceivesCustomer(v bool) *ListingUpdateOne { + _u.mutation.SetReceivesCustomer(v) + return _u +} + +// SetNillableReceivesCustomer sets the "receives_customer" field if the given value is not nil. +func (_u *ListingUpdateOne) SetNillableReceivesCustomer(v *bool) *ListingUpdateOne { + if v != nil { + _u.SetReceivesCustomer(*v) + } + return _u +} + +// SetRemoteServices sets the "remote_services" field. +func (_u *ListingUpdateOne) SetRemoteServices(v bool) *ListingUpdateOne { + _u.mutation.SetRemoteServices(v) + return _u +} + +// SetNillableRemoteServices sets the "remote_services" field if the given value is not nil. +func (_u *ListingUpdateOne) SetNillableRemoteServices(v *bool) *ListingUpdateOne { + if v != nil { + _u.SetRemoteServices(*v) + } + return _u +} + +// SetState sets the "state" field. +func (_u *ListingUpdateOne) SetState(v listing.State) *ListingUpdateOne { + _u.mutation.SetState(v) + return _u +} + +// SetNillableState sets the "state" field if the given value is not nil. +func (_u *ListingUpdateOne) SetNillableState(v *listing.State) *ListingUpdateOne { + if v != nil { + _u.SetState(*v) + } + return _u +} + +// SetRevision sets the "revision" field. +func (_u *ListingUpdateOne) SetRevision(v int) *ListingUpdateOne { + _u.mutation.ResetRevision() + _u.mutation.SetRevision(v) + return _u +} + +// SetNillableRevision sets the "revision" field if the given value is not nil. +func (_u *ListingUpdateOne) SetNillableRevision(v *int) *ListingUpdateOne { + if v != nil { + _u.SetRevision(*v) + } + return _u +} + +// AddRevision adds value to the "revision" field. +func (_u *ListingUpdateOne) AddRevision(v int) *ListingUpdateOne { + _u.mutation.AddRevision(v) + return _u +} + +// SetUpdatedAt sets the "updated_at" field. +func (_u *ListingUpdateOne) SetUpdatedAt(v time.Time) *ListingUpdateOne { + _u.mutation.SetUpdatedAt(v) + return _u +} + +// Mutation returns the ListingMutation object of the builder. +func (_u *ListingUpdateOne) Mutation() *ListingMutation { + return _u.mutation +} + +// Where appends a list predicates to the ListingUpdate builder. +func (_u *ListingUpdateOne) Where(ps ...predicate.Listing) *ListingUpdateOne { + _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 *ListingUpdateOne) Select(field string, fields ...string) *ListingUpdateOne { + _u.fields = append([]string{field}, fields...) + return _u +} + +// Save executes the query and returns the updated Listing entity. +func (_u *ListingUpdateOne) Save(ctx context.Context) (*Listing, error) { + _u.defaults() + return withHooks(ctx, _u.sqlSave, _u.mutation, _u.hooks) +} + +// SaveX is like Save, but panics if an error occurs. +func (_u *ListingUpdateOne) SaveX(ctx context.Context) *Listing { + node, err := _u.Save(ctx) + if err != nil { + panic(err) + } + return node +} + +// Exec executes the query on the entity. +func (_u *ListingUpdateOne) Exec(ctx context.Context) error { + _, err := _u.Save(ctx) + return err +} + +// ExecX is like Exec, but panics if an error occurs. +func (_u *ListingUpdateOne) ExecX(ctx context.Context) { + if err := _u.Exec(ctx); err != nil { + panic(err) + } +} + +// defaults sets the default values of the builder before save. +func (_u *ListingUpdateOne) defaults() { + if _, ok := _u.mutation.UpdatedAt(); !ok { + v := listing.UpdateDefaultUpdatedAt() + _u.mutation.SetUpdatedAt(v) + } +} + +// check runs all checks and user-defined validators on the builder. +func (_u *ListingUpdateOne) check() error { + if v, ok := _u.mutation.Title(); ok { + if err := listing.TitleValidator(v); err != nil { + return &ValidationError{Name: "title", err: fmt.Errorf(`ent: validator failed for field "Listing.title": %w`, err)} + } + } + if v, ok := _u.mutation.Description(); ok { + if err := listing.DescriptionValidator(v); err != nil { + return &ValidationError{Name: "description", err: fmt.Errorf(`ent: validator failed for field "Listing.description": %w`, err)} + } + } + if v, ok := _u.mutation.PriceType(); ok { + if err := listing.PriceTypeValidator(v); err != nil { + return &ValidationError{Name: "price_type", err: fmt.Errorf(`ent: validator failed for field "Listing.price_type": %w`, err)} + } + } + if v, ok := _u.mutation.PriceMinor(); ok { + if err := listing.PriceMinorValidator(v); err != nil { + return &ValidationError{Name: "price_minor", err: fmt.Errorf(`ent: validator failed for field "Listing.price_minor": %w`, err)} + } + } + if v, ok := _u.mutation.State(); ok { + if err := listing.StateValidator(v); err != nil { + return &ValidationError{Name: "state", err: fmt.Errorf(`ent: validator failed for field "Listing.state": %w`, err)} + } + } + if v, ok := _u.mutation.Revision(); ok { + if err := listing.RevisionValidator(v); err != nil { + return &ValidationError{Name: "revision", err: fmt.Errorf(`ent: validator failed for field "Listing.revision": %w`, err)} + } + } + return nil +} + +func (_u *ListingUpdateOne) sqlSave(ctx context.Context) (_node *Listing, err error) { + if err := _u.check(); err != nil { + return _node, err + } + _spec := sqlgraph.NewUpdateSpec(listing.Table, listing.Columns, sqlgraph.NewFieldSpec(listing.FieldID, field.TypeUUID)) + id, ok := _u.mutation.ID() + if !ok { + return nil, &ValidationError{Name: "id", err: errors.New(`ent: missing "Listing.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, listing.FieldID) + for _, f := range fields { + if !listing.ValidColumn(f) { + return nil, &ValidationError{Name: f, err: fmt.Errorf("ent: invalid field %q for query", f)} + } + if f != listing.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.CategoryID(); ok { + _spec.SetField(listing.FieldCategoryID, field.TypeUUID, value) + } + if value, ok := _u.mutation.PrimaryLocalityID(); ok { + _spec.SetField(listing.FieldPrimaryLocalityID, field.TypeUUID, value) + } + if value, ok := _u.mutation.Title(); ok { + _spec.SetField(listing.FieldTitle, field.TypeString, value) + } + if value, ok := _u.mutation.Description(); ok { + _spec.SetField(listing.FieldDescription, field.TypeString, value) + } + if value, ok := _u.mutation.PriceType(); ok { + _spec.SetField(listing.FieldPriceType, field.TypeEnum, value) + } + if value, ok := _u.mutation.PriceMinor(); ok { + _spec.SetField(listing.FieldPriceMinor, field.TypeInt, value) + } + if value, ok := _u.mutation.AddedPriceMinor(); ok { + _spec.AddField(listing.FieldPriceMinor, field.TypeInt, value) + } + if _u.mutation.PriceMinorCleared() { + _spec.ClearField(listing.FieldPriceMinor, field.TypeInt) + } + if value, ok := _u.mutation.TravelsToCustomer(); ok { + _spec.SetField(listing.FieldTravelsToCustomer, field.TypeBool, value) + } + if value, ok := _u.mutation.ReceivesCustomer(); ok { + _spec.SetField(listing.FieldReceivesCustomer, field.TypeBool, value) + } + if value, ok := _u.mutation.RemoteServices(); ok { + _spec.SetField(listing.FieldRemoteServices, field.TypeBool, value) + } + if value, ok := _u.mutation.State(); ok { + _spec.SetField(listing.FieldState, field.TypeEnum, value) + } + if value, ok := _u.mutation.Revision(); ok { + _spec.SetField(listing.FieldRevision, field.TypeInt, value) + } + if value, ok := _u.mutation.AddedRevision(); ok { + _spec.AddField(listing.FieldRevision, field.TypeInt, value) + } + if value, ok := _u.mutation.UpdatedAt(); ok { + _spec.SetField(listing.FieldUpdatedAt, field.TypeTime, value) + } + _node = &Listing{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{listing.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/backend/ent/listingevent.go b/backend/ent/listingevent.go new file mode 100644 index 0000000..6738057 --- /dev/null +++ b/backend/ent/listingevent.go @@ -0,0 +1,192 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "fmt" + "strings" + "time" + + "entgo.io/ent" + "entgo.io/ent/dialect/sql" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/listingevent" + "github.com/google/uuid" +) + +// ListingEvent is the model entity for the ListingEvent schema. +type ListingEvent struct { + config `json:"-"` + // ID of the ent. + ID uuid.UUID `json:"id,omitempty"` + // ListingID holds the value of the "listing_id" field. + ListingID uuid.UUID `json:"listing_id,omitempty"` + // ActorInternalUserID holds the value of the "actor_internal_user_id" field. + ActorInternalUserID uuid.UUID `json:"actor_internal_user_id,omitempty"` + // EventType holds the value of the "event_type" field. + EventType listingevent.EventType `json:"event_type,omitempty"` + // FromState holds the value of the "from_state" field. + FromState *string `json:"from_state,omitempty"` + // ToState holds the value of the "to_state" field. + ToState string `json:"to_state,omitempty"` + // Revision holds the value of the "revision" field. + Revision int `json:"revision,omitempty"` + // Reason holds the value of the "reason" field. + Reason *string `json:"reason,omitempty"` + // CreatedAt holds the value of the "created_at" field. + CreatedAt time.Time `json:"created_at,omitempty"` + selectValues sql.SelectValues +} + +// scanValues returns the types for scanning values from sql.Rows. +func (*ListingEvent) scanValues(columns []string) ([]any, error) { + values := make([]any, len(columns)) + for i := range columns { + switch columns[i] { + case listingevent.FieldRevision: + values[i] = new(sql.NullInt64) + case listingevent.FieldEventType, listingevent.FieldFromState, listingevent.FieldToState, listingevent.FieldReason: + values[i] = new(sql.NullString) + case listingevent.FieldCreatedAt: + values[i] = new(sql.NullTime) + case listingevent.FieldID, listingevent.FieldListingID, listingevent.FieldActorInternalUserID: + 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 ListingEvent fields. +func (_m *ListingEvent) 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 listingevent.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 listingevent.FieldListingID: + if value, ok := values[i].(*uuid.UUID); !ok { + return fmt.Errorf("unexpected type %T for field listing_id", values[i]) + } else if value != nil { + _m.ListingID = *value + } + case listingevent.FieldActorInternalUserID: + if value, ok := values[i].(*uuid.UUID); !ok { + return fmt.Errorf("unexpected type %T for field actor_internal_user_id", values[i]) + } else if value != nil { + _m.ActorInternalUserID = *value + } + case listingevent.FieldEventType: + if value, ok := values[i].(*sql.NullString); !ok { + return fmt.Errorf("unexpected type %T for field event_type", values[i]) + } else if value.Valid { + _m.EventType = listingevent.EventType(value.String) + } + case listingevent.FieldFromState: + if value, ok := values[i].(*sql.NullString); !ok { + return fmt.Errorf("unexpected type %T for field from_state", values[i]) + } else if value.Valid { + _m.FromState = new(string) + *_m.FromState = value.String + } + case listingevent.FieldToState: + if value, ok := values[i].(*sql.NullString); !ok { + return fmt.Errorf("unexpected type %T for field to_state", values[i]) + } else if value.Valid { + _m.ToState = value.String + } + case listingevent.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) + } + case listingevent.FieldReason: + if value, ok := values[i].(*sql.NullString); !ok { + return fmt.Errorf("unexpected type %T for field reason", values[i]) + } else if value.Valid { + _m.Reason = new(string) + *_m.Reason = value.String + } + case listingevent.FieldCreatedAt: + if value, ok := values[i].(*sql.NullTime); !ok { + return fmt.Errorf("unexpected type %T for field created_at", values[i]) + } else if value.Valid { + _m.CreatedAt = value.Time + } + default: + _m.selectValues.Set(columns[i], values[i]) + } + } + return nil +} + +// Value returns the ent.Value that was dynamically selected and assigned to the ListingEvent. +// This includes values selected through modifiers, order, etc. +func (_m *ListingEvent) Value(name string) (ent.Value, error) { + return _m.selectValues.Get(name) +} + +// Update returns a builder for updating this ListingEvent. +// Note that you need to call ListingEvent.Unwrap() before calling this method if this ListingEvent +// was returned from a transaction, and the transaction was committed or rolled back. +func (_m *ListingEvent) Update() *ListingEventUpdateOne { + return NewListingEventClient(_m.config).UpdateOne(_m) +} + +// Unwrap unwraps the ListingEvent 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 *ListingEvent) Unwrap() *ListingEvent { + _tx, ok := _m.config.driver.(*txDriver) + if !ok { + panic("ent: ListingEvent is not a transactional entity") + } + _m.config.driver = _tx.drv + return _m +} + +// String implements the fmt.Stringer. +func (_m *ListingEvent) String() string { + var builder strings.Builder + builder.WriteString("ListingEvent(") + builder.WriteString(fmt.Sprintf("id=%v, ", _m.ID)) + builder.WriteString("listing_id=") + builder.WriteString(fmt.Sprintf("%v", _m.ListingID)) + builder.WriteString(", ") + builder.WriteString("actor_internal_user_id=") + builder.WriteString(fmt.Sprintf("%v", _m.ActorInternalUserID)) + builder.WriteString(", ") + builder.WriteString("event_type=") + builder.WriteString(fmt.Sprintf("%v", _m.EventType)) + builder.WriteString(", ") + if v := _m.FromState; v != nil { + builder.WriteString("from_state=") + builder.WriteString(*v) + } + builder.WriteString(", ") + builder.WriteString("to_state=") + builder.WriteString(_m.ToState) + builder.WriteString(", ") + builder.WriteString("revision=") + builder.WriteString(fmt.Sprintf("%v", _m.Revision)) + builder.WriteString(", ") + if v := _m.Reason; v != nil { + builder.WriteString("reason=") + builder.WriteString(*v) + } + builder.WriteString(", ") + builder.WriteString("created_at=") + builder.WriteString(_m.CreatedAt.Format(time.ANSIC)) + builder.WriteByte(')') + return builder.String() +} + +// ListingEvents is a parsable slice of ListingEvent. +type ListingEvents []*ListingEvent diff --git a/backend/ent/listingevent/listingevent.go b/backend/ent/listingevent/listingevent.go new file mode 100644 index 0000000..a1232db --- /dev/null +++ b/backend/ent/listingevent/listingevent.go @@ -0,0 +1,150 @@ +// Code generated by ent, DO NOT EDIT. + +package listingevent + +import ( + "fmt" + "time" + + "entgo.io/ent/dialect/sql" + "github.com/google/uuid" +) + +const ( + // Label holds the string label denoting the listingevent type in the database. + Label = "listing_event" + // FieldID holds the string denoting the id field in the database. + FieldID = "id" + // FieldListingID holds the string denoting the listing_id field in the database. + FieldListingID = "listing_id" + // FieldActorInternalUserID holds the string denoting the actor_internal_user_id field in the database. + FieldActorInternalUserID = "actor_internal_user_id" + // FieldEventType holds the string denoting the event_type field in the database. + FieldEventType = "event_type" + // FieldFromState holds the string denoting the from_state field in the database. + FieldFromState = "from_state" + // FieldToState holds the string denoting the to_state field in the database. + FieldToState = "to_state" + // FieldRevision holds the string denoting the revision field in the database. + FieldRevision = "revision" + // FieldReason holds the string denoting the reason field in the database. + FieldReason = "reason" + // FieldCreatedAt holds the string denoting the created_at field in the database. + FieldCreatedAt = "created_at" + // Table holds the table name of the listingevent in the database. + Table = "listing_events" +) + +// Columns holds all SQL columns for listingevent fields. +var Columns = []string{ + FieldID, + FieldListingID, + FieldActorInternalUserID, + FieldEventType, + FieldFromState, + FieldToState, + FieldRevision, + FieldReason, + FieldCreatedAt, +} + +// 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 ( + // FromStateValidator is a validator for the "from_state" field. It is called by the builders before save. + FromStateValidator func(string) error + // ToStateValidator is a validator for the "to_state" field. It is called by the builders before save. + ToStateValidator func(string) error + // RevisionValidator is a validator for the "revision" field. It is called by the builders before save. + RevisionValidator func(int) error + // ReasonValidator is a validator for the "reason" field. It is called by the builders before save. + ReasonValidator func(string) error + // DefaultCreatedAt holds the default value on creation for the "created_at" field. + DefaultCreatedAt func() time.Time + // DefaultID holds the default value on creation for the "id" field. + DefaultID func() uuid.UUID +) + +// EventType defines the type for the "event_type" enum field. +type EventType string + +// EventType values. +const ( + EventTypeCreated EventType = "created" + EventTypeUpdated EventType = "updated" + EventTypeSubmitted EventType = "submitted" + EventTypeApproved EventType = "approved" + EventTypeRejected EventType = "rejected" + EventTypePaused EventType = "paused" + EventTypeArchived EventType = "archived" +) + +func (et EventType) String() string { + return string(et) +} + +// EventTypeValidator is a validator for the "event_type" field enum values. It is called by the builders before save. +func EventTypeValidator(et EventType) error { + switch et { + case EventTypeCreated, EventTypeUpdated, EventTypeSubmitted, EventTypeApproved, EventTypeRejected, EventTypePaused, EventTypeArchived: + return nil + default: + return fmt.Errorf("listingevent: invalid enum value for event_type field: %q", et) + } +} + +// OrderOption defines the ordering options for the ListingEvent 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() +} + +// ByListingID orders the results by the listing_id field. +func ByListingID(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldListingID, opts...).ToFunc() +} + +// ByActorInternalUserID orders the results by the actor_internal_user_id field. +func ByActorInternalUserID(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldActorInternalUserID, opts...).ToFunc() +} + +// ByEventType orders the results by the event_type field. +func ByEventType(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldEventType, opts...).ToFunc() +} + +// ByFromState orders the results by the from_state field. +func ByFromState(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldFromState, opts...).ToFunc() +} + +// ByToState orders the results by the to_state field. +func ByToState(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldToState, opts...).ToFunc() +} + +// ByRevision orders the results by the revision field. +func ByRevision(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldRevision, opts...).ToFunc() +} + +// ByReason orders the results by the reason field. +func ByReason(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldReason, opts...).ToFunc() +} + +// ByCreatedAt orders the results by the created_at field. +func ByCreatedAt(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldCreatedAt, opts...).ToFunc() +} diff --git a/backend/ent/listingevent/where.go b/backend/ent/listingevent/where.go new file mode 100644 index 0000000..457fdd8 --- /dev/null +++ b/backend/ent/listingevent/where.go @@ -0,0 +1,501 @@ +// Code generated by ent, DO NOT EDIT. + +package listingevent + +import ( + "time" + + "entgo.io/ent/dialect/sql" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/predicate" + "github.com/google/uuid" +) + +// ID filters vertices based on their ID field. +func ID(id uuid.UUID) predicate.ListingEvent { + return predicate.ListingEvent(sql.FieldEQ(FieldID, id)) +} + +// IDEQ applies the EQ predicate on the ID field. +func IDEQ(id uuid.UUID) predicate.ListingEvent { + return predicate.ListingEvent(sql.FieldEQ(FieldID, id)) +} + +// IDNEQ applies the NEQ predicate on the ID field. +func IDNEQ(id uuid.UUID) predicate.ListingEvent { + return predicate.ListingEvent(sql.FieldNEQ(FieldID, id)) +} + +// IDIn applies the In predicate on the ID field. +func IDIn(ids ...uuid.UUID) predicate.ListingEvent { + return predicate.ListingEvent(sql.FieldIn(FieldID, ids...)) +} + +// IDNotIn applies the NotIn predicate on the ID field. +func IDNotIn(ids ...uuid.UUID) predicate.ListingEvent { + return predicate.ListingEvent(sql.FieldNotIn(FieldID, ids...)) +} + +// IDGT applies the GT predicate on the ID field. +func IDGT(id uuid.UUID) predicate.ListingEvent { + return predicate.ListingEvent(sql.FieldGT(FieldID, id)) +} + +// IDGTE applies the GTE predicate on the ID field. +func IDGTE(id uuid.UUID) predicate.ListingEvent { + return predicate.ListingEvent(sql.FieldGTE(FieldID, id)) +} + +// IDLT applies the LT predicate on the ID field. +func IDLT(id uuid.UUID) predicate.ListingEvent { + return predicate.ListingEvent(sql.FieldLT(FieldID, id)) +} + +// IDLTE applies the LTE predicate on the ID field. +func IDLTE(id uuid.UUID) predicate.ListingEvent { + return predicate.ListingEvent(sql.FieldLTE(FieldID, id)) +} + +// ListingID applies equality check predicate on the "listing_id" field. It's identical to ListingIDEQ. +func ListingID(v uuid.UUID) predicate.ListingEvent { + return predicate.ListingEvent(sql.FieldEQ(FieldListingID, v)) +} + +// ActorInternalUserID applies equality check predicate on the "actor_internal_user_id" field. It's identical to ActorInternalUserIDEQ. +func ActorInternalUserID(v uuid.UUID) predicate.ListingEvent { + return predicate.ListingEvent(sql.FieldEQ(FieldActorInternalUserID, v)) +} + +// FromState applies equality check predicate on the "from_state" field. It's identical to FromStateEQ. +func FromState(v string) predicate.ListingEvent { + return predicate.ListingEvent(sql.FieldEQ(FieldFromState, v)) +} + +// ToState applies equality check predicate on the "to_state" field. It's identical to ToStateEQ. +func ToState(v string) predicate.ListingEvent { + return predicate.ListingEvent(sql.FieldEQ(FieldToState, v)) +} + +// Revision applies equality check predicate on the "revision" field. It's identical to RevisionEQ. +func Revision(v int) predicate.ListingEvent { + return predicate.ListingEvent(sql.FieldEQ(FieldRevision, v)) +} + +// Reason applies equality check predicate on the "reason" field. It's identical to ReasonEQ. +func Reason(v string) predicate.ListingEvent { + return predicate.ListingEvent(sql.FieldEQ(FieldReason, v)) +} + +// CreatedAt applies equality check predicate on the "created_at" field. It's identical to CreatedAtEQ. +func CreatedAt(v time.Time) predicate.ListingEvent { + return predicate.ListingEvent(sql.FieldEQ(FieldCreatedAt, v)) +} + +// ListingIDEQ applies the EQ predicate on the "listing_id" field. +func ListingIDEQ(v uuid.UUID) predicate.ListingEvent { + return predicate.ListingEvent(sql.FieldEQ(FieldListingID, v)) +} + +// ListingIDNEQ applies the NEQ predicate on the "listing_id" field. +func ListingIDNEQ(v uuid.UUID) predicate.ListingEvent { + return predicate.ListingEvent(sql.FieldNEQ(FieldListingID, v)) +} + +// ListingIDIn applies the In predicate on the "listing_id" field. +func ListingIDIn(vs ...uuid.UUID) predicate.ListingEvent { + return predicate.ListingEvent(sql.FieldIn(FieldListingID, vs...)) +} + +// ListingIDNotIn applies the NotIn predicate on the "listing_id" field. +func ListingIDNotIn(vs ...uuid.UUID) predicate.ListingEvent { + return predicate.ListingEvent(sql.FieldNotIn(FieldListingID, vs...)) +} + +// ListingIDGT applies the GT predicate on the "listing_id" field. +func ListingIDGT(v uuid.UUID) predicate.ListingEvent { + return predicate.ListingEvent(sql.FieldGT(FieldListingID, v)) +} + +// ListingIDGTE applies the GTE predicate on the "listing_id" field. +func ListingIDGTE(v uuid.UUID) predicate.ListingEvent { + return predicate.ListingEvent(sql.FieldGTE(FieldListingID, v)) +} + +// ListingIDLT applies the LT predicate on the "listing_id" field. +func ListingIDLT(v uuid.UUID) predicate.ListingEvent { + return predicate.ListingEvent(sql.FieldLT(FieldListingID, v)) +} + +// ListingIDLTE applies the LTE predicate on the "listing_id" field. +func ListingIDLTE(v uuid.UUID) predicate.ListingEvent { + return predicate.ListingEvent(sql.FieldLTE(FieldListingID, v)) +} + +// ActorInternalUserIDEQ applies the EQ predicate on the "actor_internal_user_id" field. +func ActorInternalUserIDEQ(v uuid.UUID) predicate.ListingEvent { + return predicate.ListingEvent(sql.FieldEQ(FieldActorInternalUserID, v)) +} + +// ActorInternalUserIDNEQ applies the NEQ predicate on the "actor_internal_user_id" field. +func ActorInternalUserIDNEQ(v uuid.UUID) predicate.ListingEvent { + return predicate.ListingEvent(sql.FieldNEQ(FieldActorInternalUserID, v)) +} + +// ActorInternalUserIDIn applies the In predicate on the "actor_internal_user_id" field. +func ActorInternalUserIDIn(vs ...uuid.UUID) predicate.ListingEvent { + return predicate.ListingEvent(sql.FieldIn(FieldActorInternalUserID, vs...)) +} + +// ActorInternalUserIDNotIn applies the NotIn predicate on the "actor_internal_user_id" field. +func ActorInternalUserIDNotIn(vs ...uuid.UUID) predicate.ListingEvent { + return predicate.ListingEvent(sql.FieldNotIn(FieldActorInternalUserID, vs...)) +} + +// ActorInternalUserIDGT applies the GT predicate on the "actor_internal_user_id" field. +func ActorInternalUserIDGT(v uuid.UUID) predicate.ListingEvent { + return predicate.ListingEvent(sql.FieldGT(FieldActorInternalUserID, v)) +} + +// ActorInternalUserIDGTE applies the GTE predicate on the "actor_internal_user_id" field. +func ActorInternalUserIDGTE(v uuid.UUID) predicate.ListingEvent { + return predicate.ListingEvent(sql.FieldGTE(FieldActorInternalUserID, v)) +} + +// ActorInternalUserIDLT applies the LT predicate on the "actor_internal_user_id" field. +func ActorInternalUserIDLT(v uuid.UUID) predicate.ListingEvent { + return predicate.ListingEvent(sql.FieldLT(FieldActorInternalUserID, v)) +} + +// ActorInternalUserIDLTE applies the LTE predicate on the "actor_internal_user_id" field. +func ActorInternalUserIDLTE(v uuid.UUID) predicate.ListingEvent { + return predicate.ListingEvent(sql.FieldLTE(FieldActorInternalUserID, v)) +} + +// EventTypeEQ applies the EQ predicate on the "event_type" field. +func EventTypeEQ(v EventType) predicate.ListingEvent { + return predicate.ListingEvent(sql.FieldEQ(FieldEventType, v)) +} + +// EventTypeNEQ applies the NEQ predicate on the "event_type" field. +func EventTypeNEQ(v EventType) predicate.ListingEvent { + return predicate.ListingEvent(sql.FieldNEQ(FieldEventType, v)) +} + +// EventTypeIn applies the In predicate on the "event_type" field. +func EventTypeIn(vs ...EventType) predicate.ListingEvent { + return predicate.ListingEvent(sql.FieldIn(FieldEventType, vs...)) +} + +// EventTypeNotIn applies the NotIn predicate on the "event_type" field. +func EventTypeNotIn(vs ...EventType) predicate.ListingEvent { + return predicate.ListingEvent(sql.FieldNotIn(FieldEventType, vs...)) +} + +// FromStateEQ applies the EQ predicate on the "from_state" field. +func FromStateEQ(v string) predicate.ListingEvent { + return predicate.ListingEvent(sql.FieldEQ(FieldFromState, v)) +} + +// FromStateNEQ applies the NEQ predicate on the "from_state" field. +func FromStateNEQ(v string) predicate.ListingEvent { + return predicate.ListingEvent(sql.FieldNEQ(FieldFromState, v)) +} + +// FromStateIn applies the In predicate on the "from_state" field. +func FromStateIn(vs ...string) predicate.ListingEvent { + return predicate.ListingEvent(sql.FieldIn(FieldFromState, vs...)) +} + +// FromStateNotIn applies the NotIn predicate on the "from_state" field. +func FromStateNotIn(vs ...string) predicate.ListingEvent { + return predicate.ListingEvent(sql.FieldNotIn(FieldFromState, vs...)) +} + +// FromStateGT applies the GT predicate on the "from_state" field. +func FromStateGT(v string) predicate.ListingEvent { + return predicate.ListingEvent(sql.FieldGT(FieldFromState, v)) +} + +// FromStateGTE applies the GTE predicate on the "from_state" field. +func FromStateGTE(v string) predicate.ListingEvent { + return predicate.ListingEvent(sql.FieldGTE(FieldFromState, v)) +} + +// FromStateLT applies the LT predicate on the "from_state" field. +func FromStateLT(v string) predicate.ListingEvent { + return predicate.ListingEvent(sql.FieldLT(FieldFromState, v)) +} + +// FromStateLTE applies the LTE predicate on the "from_state" field. +func FromStateLTE(v string) predicate.ListingEvent { + return predicate.ListingEvent(sql.FieldLTE(FieldFromState, v)) +} + +// FromStateContains applies the Contains predicate on the "from_state" field. +func FromStateContains(v string) predicate.ListingEvent { + return predicate.ListingEvent(sql.FieldContains(FieldFromState, v)) +} + +// FromStateHasPrefix applies the HasPrefix predicate on the "from_state" field. +func FromStateHasPrefix(v string) predicate.ListingEvent { + return predicate.ListingEvent(sql.FieldHasPrefix(FieldFromState, v)) +} + +// FromStateHasSuffix applies the HasSuffix predicate on the "from_state" field. +func FromStateHasSuffix(v string) predicate.ListingEvent { + return predicate.ListingEvent(sql.FieldHasSuffix(FieldFromState, v)) +} + +// FromStateIsNil applies the IsNil predicate on the "from_state" field. +func FromStateIsNil() predicate.ListingEvent { + return predicate.ListingEvent(sql.FieldIsNull(FieldFromState)) +} + +// FromStateNotNil applies the NotNil predicate on the "from_state" field. +func FromStateNotNil() predicate.ListingEvent { + return predicate.ListingEvent(sql.FieldNotNull(FieldFromState)) +} + +// FromStateEqualFold applies the EqualFold predicate on the "from_state" field. +func FromStateEqualFold(v string) predicate.ListingEvent { + return predicate.ListingEvent(sql.FieldEqualFold(FieldFromState, v)) +} + +// FromStateContainsFold applies the ContainsFold predicate on the "from_state" field. +func FromStateContainsFold(v string) predicate.ListingEvent { + return predicate.ListingEvent(sql.FieldContainsFold(FieldFromState, v)) +} + +// ToStateEQ applies the EQ predicate on the "to_state" field. +func ToStateEQ(v string) predicate.ListingEvent { + return predicate.ListingEvent(sql.FieldEQ(FieldToState, v)) +} + +// ToStateNEQ applies the NEQ predicate on the "to_state" field. +func ToStateNEQ(v string) predicate.ListingEvent { + return predicate.ListingEvent(sql.FieldNEQ(FieldToState, v)) +} + +// ToStateIn applies the In predicate on the "to_state" field. +func ToStateIn(vs ...string) predicate.ListingEvent { + return predicate.ListingEvent(sql.FieldIn(FieldToState, vs...)) +} + +// ToStateNotIn applies the NotIn predicate on the "to_state" field. +func ToStateNotIn(vs ...string) predicate.ListingEvent { + return predicate.ListingEvent(sql.FieldNotIn(FieldToState, vs...)) +} + +// ToStateGT applies the GT predicate on the "to_state" field. +func ToStateGT(v string) predicate.ListingEvent { + return predicate.ListingEvent(sql.FieldGT(FieldToState, v)) +} + +// ToStateGTE applies the GTE predicate on the "to_state" field. +func ToStateGTE(v string) predicate.ListingEvent { + return predicate.ListingEvent(sql.FieldGTE(FieldToState, v)) +} + +// ToStateLT applies the LT predicate on the "to_state" field. +func ToStateLT(v string) predicate.ListingEvent { + return predicate.ListingEvent(sql.FieldLT(FieldToState, v)) +} + +// ToStateLTE applies the LTE predicate on the "to_state" field. +func ToStateLTE(v string) predicate.ListingEvent { + return predicate.ListingEvent(sql.FieldLTE(FieldToState, v)) +} + +// ToStateContains applies the Contains predicate on the "to_state" field. +func ToStateContains(v string) predicate.ListingEvent { + return predicate.ListingEvent(sql.FieldContains(FieldToState, v)) +} + +// ToStateHasPrefix applies the HasPrefix predicate on the "to_state" field. +func ToStateHasPrefix(v string) predicate.ListingEvent { + return predicate.ListingEvent(sql.FieldHasPrefix(FieldToState, v)) +} + +// ToStateHasSuffix applies the HasSuffix predicate on the "to_state" field. +func ToStateHasSuffix(v string) predicate.ListingEvent { + return predicate.ListingEvent(sql.FieldHasSuffix(FieldToState, v)) +} + +// ToStateEqualFold applies the EqualFold predicate on the "to_state" field. +func ToStateEqualFold(v string) predicate.ListingEvent { + return predicate.ListingEvent(sql.FieldEqualFold(FieldToState, v)) +} + +// ToStateContainsFold applies the ContainsFold predicate on the "to_state" field. +func ToStateContainsFold(v string) predicate.ListingEvent { + return predicate.ListingEvent(sql.FieldContainsFold(FieldToState, v)) +} + +// RevisionEQ applies the EQ predicate on the "revision" field. +func RevisionEQ(v int) predicate.ListingEvent { + return predicate.ListingEvent(sql.FieldEQ(FieldRevision, v)) +} + +// RevisionNEQ applies the NEQ predicate on the "revision" field. +func RevisionNEQ(v int) predicate.ListingEvent { + return predicate.ListingEvent(sql.FieldNEQ(FieldRevision, v)) +} + +// RevisionIn applies the In predicate on the "revision" field. +func RevisionIn(vs ...int) predicate.ListingEvent { + return predicate.ListingEvent(sql.FieldIn(FieldRevision, vs...)) +} + +// RevisionNotIn applies the NotIn predicate on the "revision" field. +func RevisionNotIn(vs ...int) predicate.ListingEvent { + return predicate.ListingEvent(sql.FieldNotIn(FieldRevision, vs...)) +} + +// RevisionGT applies the GT predicate on the "revision" field. +func RevisionGT(v int) predicate.ListingEvent { + return predicate.ListingEvent(sql.FieldGT(FieldRevision, v)) +} + +// RevisionGTE applies the GTE predicate on the "revision" field. +func RevisionGTE(v int) predicate.ListingEvent { + return predicate.ListingEvent(sql.FieldGTE(FieldRevision, v)) +} + +// RevisionLT applies the LT predicate on the "revision" field. +func RevisionLT(v int) predicate.ListingEvent { + return predicate.ListingEvent(sql.FieldLT(FieldRevision, v)) +} + +// RevisionLTE applies the LTE predicate on the "revision" field. +func RevisionLTE(v int) predicate.ListingEvent { + return predicate.ListingEvent(sql.FieldLTE(FieldRevision, v)) +} + +// ReasonEQ applies the EQ predicate on the "reason" field. +func ReasonEQ(v string) predicate.ListingEvent { + return predicate.ListingEvent(sql.FieldEQ(FieldReason, v)) +} + +// ReasonNEQ applies the NEQ predicate on the "reason" field. +func ReasonNEQ(v string) predicate.ListingEvent { + return predicate.ListingEvent(sql.FieldNEQ(FieldReason, v)) +} + +// ReasonIn applies the In predicate on the "reason" field. +func ReasonIn(vs ...string) predicate.ListingEvent { + return predicate.ListingEvent(sql.FieldIn(FieldReason, vs...)) +} + +// ReasonNotIn applies the NotIn predicate on the "reason" field. +func ReasonNotIn(vs ...string) predicate.ListingEvent { + return predicate.ListingEvent(sql.FieldNotIn(FieldReason, vs...)) +} + +// ReasonGT applies the GT predicate on the "reason" field. +func ReasonGT(v string) predicate.ListingEvent { + return predicate.ListingEvent(sql.FieldGT(FieldReason, v)) +} + +// ReasonGTE applies the GTE predicate on the "reason" field. +func ReasonGTE(v string) predicate.ListingEvent { + return predicate.ListingEvent(sql.FieldGTE(FieldReason, v)) +} + +// ReasonLT applies the LT predicate on the "reason" field. +func ReasonLT(v string) predicate.ListingEvent { + return predicate.ListingEvent(sql.FieldLT(FieldReason, v)) +} + +// ReasonLTE applies the LTE predicate on the "reason" field. +func ReasonLTE(v string) predicate.ListingEvent { + return predicate.ListingEvent(sql.FieldLTE(FieldReason, v)) +} + +// ReasonContains applies the Contains predicate on the "reason" field. +func ReasonContains(v string) predicate.ListingEvent { + return predicate.ListingEvent(sql.FieldContains(FieldReason, v)) +} + +// ReasonHasPrefix applies the HasPrefix predicate on the "reason" field. +func ReasonHasPrefix(v string) predicate.ListingEvent { + return predicate.ListingEvent(sql.FieldHasPrefix(FieldReason, v)) +} + +// ReasonHasSuffix applies the HasSuffix predicate on the "reason" field. +func ReasonHasSuffix(v string) predicate.ListingEvent { + return predicate.ListingEvent(sql.FieldHasSuffix(FieldReason, v)) +} + +// ReasonIsNil applies the IsNil predicate on the "reason" field. +func ReasonIsNil() predicate.ListingEvent { + return predicate.ListingEvent(sql.FieldIsNull(FieldReason)) +} + +// ReasonNotNil applies the NotNil predicate on the "reason" field. +func ReasonNotNil() predicate.ListingEvent { + return predicate.ListingEvent(sql.FieldNotNull(FieldReason)) +} + +// ReasonEqualFold applies the EqualFold predicate on the "reason" field. +func ReasonEqualFold(v string) predicate.ListingEvent { + return predicate.ListingEvent(sql.FieldEqualFold(FieldReason, v)) +} + +// ReasonContainsFold applies the ContainsFold predicate on the "reason" field. +func ReasonContainsFold(v string) predicate.ListingEvent { + return predicate.ListingEvent(sql.FieldContainsFold(FieldReason, v)) +} + +// CreatedAtEQ applies the EQ predicate on the "created_at" field. +func CreatedAtEQ(v time.Time) predicate.ListingEvent { + return predicate.ListingEvent(sql.FieldEQ(FieldCreatedAt, v)) +} + +// CreatedAtNEQ applies the NEQ predicate on the "created_at" field. +func CreatedAtNEQ(v time.Time) predicate.ListingEvent { + return predicate.ListingEvent(sql.FieldNEQ(FieldCreatedAt, v)) +} + +// CreatedAtIn applies the In predicate on the "created_at" field. +func CreatedAtIn(vs ...time.Time) predicate.ListingEvent { + return predicate.ListingEvent(sql.FieldIn(FieldCreatedAt, vs...)) +} + +// CreatedAtNotIn applies the NotIn predicate on the "created_at" field. +func CreatedAtNotIn(vs ...time.Time) predicate.ListingEvent { + return predicate.ListingEvent(sql.FieldNotIn(FieldCreatedAt, vs...)) +} + +// CreatedAtGT applies the GT predicate on the "created_at" field. +func CreatedAtGT(v time.Time) predicate.ListingEvent { + return predicate.ListingEvent(sql.FieldGT(FieldCreatedAt, v)) +} + +// CreatedAtGTE applies the GTE predicate on the "created_at" field. +func CreatedAtGTE(v time.Time) predicate.ListingEvent { + return predicate.ListingEvent(sql.FieldGTE(FieldCreatedAt, v)) +} + +// CreatedAtLT applies the LT predicate on the "created_at" field. +func CreatedAtLT(v time.Time) predicate.ListingEvent { + return predicate.ListingEvent(sql.FieldLT(FieldCreatedAt, v)) +} + +// CreatedAtLTE applies the LTE predicate on the "created_at" field. +func CreatedAtLTE(v time.Time) predicate.ListingEvent { + return predicate.ListingEvent(sql.FieldLTE(FieldCreatedAt, v)) +} + +// And groups predicates with the AND operator between them. +func And(predicates ...predicate.ListingEvent) predicate.ListingEvent { + return predicate.ListingEvent(sql.AndPredicates(predicates...)) +} + +// Or groups predicates with the OR operator between them. +func Or(predicates ...predicate.ListingEvent) predicate.ListingEvent { + return predicate.ListingEvent(sql.OrPredicates(predicates...)) +} + +// Not applies the not operator on the given predicate. +func Not(p predicate.ListingEvent) predicate.ListingEvent { + return predicate.ListingEvent(sql.NotPredicates(p)) +} diff --git a/backend/ent/listingevent_create.go b/backend/ent/listingevent_create.go new file mode 100644 index 0000000..0c00159 --- /dev/null +++ b/backend/ent/listingevent_create.go @@ -0,0 +1,352 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "context" + "errors" + "fmt" + "time" + + "entgo.io/ent/dialect/sql/sqlgraph" + "entgo.io/ent/schema/field" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/listingevent" + "github.com/google/uuid" +) + +// ListingEventCreate is the builder for creating a ListingEvent entity. +type ListingEventCreate struct { + config + mutation *ListingEventMutation + hooks []Hook +} + +// SetListingID sets the "listing_id" field. +func (_c *ListingEventCreate) SetListingID(v uuid.UUID) *ListingEventCreate { + _c.mutation.SetListingID(v) + return _c +} + +// SetActorInternalUserID sets the "actor_internal_user_id" field. +func (_c *ListingEventCreate) SetActorInternalUserID(v uuid.UUID) *ListingEventCreate { + _c.mutation.SetActorInternalUserID(v) + return _c +} + +// SetEventType sets the "event_type" field. +func (_c *ListingEventCreate) SetEventType(v listingevent.EventType) *ListingEventCreate { + _c.mutation.SetEventType(v) + return _c +} + +// SetFromState sets the "from_state" field. +func (_c *ListingEventCreate) SetFromState(v string) *ListingEventCreate { + _c.mutation.SetFromState(v) + return _c +} + +// SetNillableFromState sets the "from_state" field if the given value is not nil. +func (_c *ListingEventCreate) SetNillableFromState(v *string) *ListingEventCreate { + if v != nil { + _c.SetFromState(*v) + } + return _c +} + +// SetToState sets the "to_state" field. +func (_c *ListingEventCreate) SetToState(v string) *ListingEventCreate { + _c.mutation.SetToState(v) + return _c +} + +// SetRevision sets the "revision" field. +func (_c *ListingEventCreate) SetRevision(v int) *ListingEventCreate { + _c.mutation.SetRevision(v) + return _c +} + +// SetReason sets the "reason" field. +func (_c *ListingEventCreate) SetReason(v string) *ListingEventCreate { + _c.mutation.SetReason(v) + return _c +} + +// SetNillableReason sets the "reason" field if the given value is not nil. +func (_c *ListingEventCreate) SetNillableReason(v *string) *ListingEventCreate { + if v != nil { + _c.SetReason(*v) + } + return _c +} + +// SetCreatedAt sets the "created_at" field. +func (_c *ListingEventCreate) SetCreatedAt(v time.Time) *ListingEventCreate { + _c.mutation.SetCreatedAt(v) + return _c +} + +// SetNillableCreatedAt sets the "created_at" field if the given value is not nil. +func (_c *ListingEventCreate) SetNillableCreatedAt(v *time.Time) *ListingEventCreate { + if v != nil { + _c.SetCreatedAt(*v) + } + return _c +} + +// SetID sets the "id" field. +func (_c *ListingEventCreate) SetID(v uuid.UUID) *ListingEventCreate { + _c.mutation.SetID(v) + return _c +} + +// SetNillableID sets the "id" field if the given value is not nil. +func (_c *ListingEventCreate) SetNillableID(v *uuid.UUID) *ListingEventCreate { + if v != nil { + _c.SetID(*v) + } + return _c +} + +// Mutation returns the ListingEventMutation object of the builder. +func (_c *ListingEventCreate) Mutation() *ListingEventMutation { + return _c.mutation +} + +// Save creates the ListingEvent in the database. +func (_c *ListingEventCreate) Save(ctx context.Context) (*ListingEvent, error) { + _c.defaults() + return withHooks(ctx, _c.sqlSave, _c.mutation, _c.hooks) +} + +// SaveX calls Save and panics if Save returns an error. +func (_c *ListingEventCreate) SaveX(ctx context.Context) *ListingEvent { + v, err := _c.Save(ctx) + if err != nil { + panic(err) + } + return v +} + +// Exec executes the query. +func (_c *ListingEventCreate) Exec(ctx context.Context) error { + _, err := _c.Save(ctx) + return err +} + +// ExecX is like Exec, but panics if an error occurs. +func (_c *ListingEventCreate) 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 *ListingEventCreate) defaults() { + if _, ok := _c.mutation.CreatedAt(); !ok { + v := listingevent.DefaultCreatedAt() + _c.mutation.SetCreatedAt(v) + } + if _, ok := _c.mutation.ID(); !ok { + v := listingevent.DefaultID() + _c.mutation.SetID(v) + } +} + +// check runs all checks and user-defined validators on the builder. +func (_c *ListingEventCreate) check() error { + if _, ok := _c.mutation.ListingID(); !ok { + return &ValidationError{Name: "listing_id", err: errors.New(`ent: missing required field "ListingEvent.listing_id"`)} + } + if _, ok := _c.mutation.ActorInternalUserID(); !ok { + return &ValidationError{Name: "actor_internal_user_id", err: errors.New(`ent: missing required field "ListingEvent.actor_internal_user_id"`)} + } + if _, ok := _c.mutation.EventType(); !ok { + return &ValidationError{Name: "event_type", err: errors.New(`ent: missing required field "ListingEvent.event_type"`)} + } + if v, ok := _c.mutation.EventType(); ok { + if err := listingevent.EventTypeValidator(v); err != nil { + return &ValidationError{Name: "event_type", err: fmt.Errorf(`ent: validator failed for field "ListingEvent.event_type": %w`, err)} + } + } + if v, ok := _c.mutation.FromState(); ok { + if err := listingevent.FromStateValidator(v); err != nil { + return &ValidationError{Name: "from_state", err: fmt.Errorf(`ent: validator failed for field "ListingEvent.from_state": %w`, err)} + } + } + if _, ok := _c.mutation.ToState(); !ok { + return &ValidationError{Name: "to_state", err: errors.New(`ent: missing required field "ListingEvent.to_state"`)} + } + if v, ok := _c.mutation.ToState(); ok { + if err := listingevent.ToStateValidator(v); err != nil { + return &ValidationError{Name: "to_state", err: fmt.Errorf(`ent: validator failed for field "ListingEvent.to_state": %w`, err)} + } + } + if _, ok := _c.mutation.Revision(); !ok { + return &ValidationError{Name: "revision", err: errors.New(`ent: missing required field "ListingEvent.revision"`)} + } + if v, ok := _c.mutation.Revision(); ok { + if err := listingevent.RevisionValidator(v); err != nil { + return &ValidationError{Name: "revision", err: fmt.Errorf(`ent: validator failed for field "ListingEvent.revision": %w`, err)} + } + } + if v, ok := _c.mutation.Reason(); ok { + if err := listingevent.ReasonValidator(v); err != nil { + return &ValidationError{Name: "reason", err: fmt.Errorf(`ent: validator failed for field "ListingEvent.reason": %w`, err)} + } + } + if _, ok := _c.mutation.CreatedAt(); !ok { + return &ValidationError{Name: "created_at", err: errors.New(`ent: missing required field "ListingEvent.created_at"`)} + } + return nil +} + +func (_c *ListingEventCreate) sqlSave(ctx context.Context) (*ListingEvent, 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 *ListingEventCreate) createSpec() (*ListingEvent, *sqlgraph.CreateSpec) { + var ( + _node = &ListingEvent{config: _c.config} + _spec = sqlgraph.NewCreateSpec(listingevent.Table, sqlgraph.NewFieldSpec(listingevent.FieldID, field.TypeUUID)) + ) + if id, ok := _c.mutation.ID(); ok { + _node.ID = id + _spec.ID.Value = &id + } + if value, ok := _c.mutation.ListingID(); ok { + _spec.SetField(listingevent.FieldListingID, field.TypeUUID, value) + _node.ListingID = value + } + if value, ok := _c.mutation.ActorInternalUserID(); ok { + _spec.SetField(listingevent.FieldActorInternalUserID, field.TypeUUID, value) + _node.ActorInternalUserID = value + } + if value, ok := _c.mutation.EventType(); ok { + _spec.SetField(listingevent.FieldEventType, field.TypeEnum, value) + _node.EventType = value + } + if value, ok := _c.mutation.FromState(); ok { + _spec.SetField(listingevent.FieldFromState, field.TypeString, value) + _node.FromState = &value + } + if value, ok := _c.mutation.ToState(); ok { + _spec.SetField(listingevent.FieldToState, field.TypeString, value) + _node.ToState = value + } + if value, ok := _c.mutation.Revision(); ok { + _spec.SetField(listingevent.FieldRevision, field.TypeInt, value) + _node.Revision = value + } + if value, ok := _c.mutation.Reason(); ok { + _spec.SetField(listingevent.FieldReason, field.TypeString, value) + _node.Reason = &value + } + if value, ok := _c.mutation.CreatedAt(); ok { + _spec.SetField(listingevent.FieldCreatedAt, field.TypeTime, value) + _node.CreatedAt = value + } + return _node, _spec +} + +// ListingEventCreateBulk is the builder for creating many ListingEvent entities in bulk. +type ListingEventCreateBulk struct { + config + err error + builders []*ListingEventCreate +} + +// Save creates the ListingEvent entities in the database. +func (_c *ListingEventCreateBulk) Save(ctx context.Context) ([]*ListingEvent, error) { + if _c.err != nil { + return nil, _c.err + } + specs := make([]*sqlgraph.CreateSpec, len(_c.builders)) + nodes := make([]*ListingEvent, 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.(*ListingEventMutation) + 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 *ListingEventCreateBulk) SaveX(ctx context.Context) []*ListingEvent { + v, err := _c.Save(ctx) + if err != nil { + panic(err) + } + return v +} + +// Exec executes the query. +func (_c *ListingEventCreateBulk) Exec(ctx context.Context) error { + _, err := _c.Save(ctx) + return err +} + +// ExecX is like Exec, but panics if an error occurs. +func (_c *ListingEventCreateBulk) ExecX(ctx context.Context) { + if err := _c.Exec(ctx); err != nil { + panic(err) + } +} diff --git a/backend/ent/listingevent_delete.go b/backend/ent/listingevent_delete.go new file mode 100644 index 0000000..f952472 --- /dev/null +++ b/backend/ent/listingevent_delete.go @@ -0,0 +1,88 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "context" + + "entgo.io/ent/dialect/sql" + "entgo.io/ent/dialect/sql/sqlgraph" + "entgo.io/ent/schema/field" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/listingevent" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/predicate" +) + +// ListingEventDelete is the builder for deleting a ListingEvent entity. +type ListingEventDelete struct { + config + hooks []Hook + mutation *ListingEventMutation +} + +// Where appends a list predicates to the ListingEventDelete builder. +func (_d *ListingEventDelete) Where(ps ...predicate.ListingEvent) *ListingEventDelete { + _d.mutation.Where(ps...) + return _d +} + +// Exec executes the deletion query and returns how many vertices were deleted. +func (_d *ListingEventDelete) 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 *ListingEventDelete) ExecX(ctx context.Context) int { + n, err := _d.Exec(ctx) + if err != nil { + panic(err) + } + return n +} + +func (_d *ListingEventDelete) sqlExec(ctx context.Context) (int, error) { + _spec := sqlgraph.NewDeleteSpec(listingevent.Table, sqlgraph.NewFieldSpec(listingevent.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 +} + +// ListingEventDeleteOne is the builder for deleting a single ListingEvent entity. +type ListingEventDeleteOne struct { + _d *ListingEventDelete +} + +// Where appends a list predicates to the ListingEventDelete builder. +func (_d *ListingEventDeleteOne) Where(ps ...predicate.ListingEvent) *ListingEventDeleteOne { + _d._d.mutation.Where(ps...) + return _d +} + +// Exec executes the deletion query. +func (_d *ListingEventDeleteOne) Exec(ctx context.Context) error { + n, err := _d._d.Exec(ctx) + switch { + case err != nil: + return err + case n == 0: + return &NotFoundError{listingevent.Label} + default: + return nil + } +} + +// ExecX is like Exec, but panics if an error occurs. +func (_d *ListingEventDeleteOne) ExecX(ctx context.Context) { + if err := _d.Exec(ctx); err != nil { + panic(err) + } +} diff --git a/backend/ent/listingevent_query.go b/backend/ent/listingevent_query.go new file mode 100644 index 0000000..8309602 --- /dev/null +++ b/backend/ent/listingevent_query.go @@ -0,0 +1,528 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "context" + "fmt" + "math" + + "entgo.io/ent" + "entgo.io/ent/dialect/sql" + "entgo.io/ent/dialect/sql/sqlgraph" + "entgo.io/ent/schema/field" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/listingevent" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/predicate" + "github.com/google/uuid" +) + +// ListingEventQuery is the builder for querying ListingEvent entities. +type ListingEventQuery struct { + config + ctx *QueryContext + order []listingevent.OrderOption + inters []Interceptor + predicates []predicate.ListingEvent + // intermediate query (i.e. traversal path). + sql *sql.Selector + path func(context.Context) (*sql.Selector, error) +} + +// Where adds a new predicate for the ListingEventQuery builder. +func (_q *ListingEventQuery) Where(ps ...predicate.ListingEvent) *ListingEventQuery { + _q.predicates = append(_q.predicates, ps...) + return _q +} + +// Limit the number of records to be returned by this query. +func (_q *ListingEventQuery) Limit(limit int) *ListingEventQuery { + _q.ctx.Limit = &limit + return _q +} + +// Offset to start from. +func (_q *ListingEventQuery) Offset(offset int) *ListingEventQuery { + _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 *ListingEventQuery) Unique(unique bool) *ListingEventQuery { + _q.ctx.Unique = &unique + return _q +} + +// Order specifies how the records should be ordered. +func (_q *ListingEventQuery) Order(o ...listingevent.OrderOption) *ListingEventQuery { + _q.order = append(_q.order, o...) + return _q +} + +// First returns the first ListingEvent entity from the query. +// Returns a *NotFoundError when no ListingEvent was found. +func (_q *ListingEventQuery) First(ctx context.Context) (*ListingEvent, 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{listingevent.Label} + } + return nodes[0], nil +} + +// FirstX is like First, but panics if an error occurs. +func (_q *ListingEventQuery) FirstX(ctx context.Context) *ListingEvent { + node, err := _q.First(ctx) + if err != nil && !IsNotFound(err) { + panic(err) + } + return node +} + +// FirstID returns the first ListingEvent ID from the query. +// Returns a *NotFoundError when no ListingEvent ID was found. +func (_q *ListingEventQuery) 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{listingevent.Label} + return + } + return ids[0], nil +} + +// FirstIDX is like FirstID, but panics if an error occurs. +func (_q *ListingEventQuery) FirstIDX(ctx context.Context) uuid.UUID { + id, err := _q.FirstID(ctx) + if err != nil && !IsNotFound(err) { + panic(err) + } + return id +} + +// Only returns a single ListingEvent entity found by the query, ensuring it only returns one. +// Returns a *NotSingularError when more than one ListingEvent entity is found. +// Returns a *NotFoundError when no ListingEvent entities are found. +func (_q *ListingEventQuery) Only(ctx context.Context) (*ListingEvent, 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{listingevent.Label} + default: + return nil, &NotSingularError{listingevent.Label} + } +} + +// OnlyX is like Only, but panics if an error occurs. +func (_q *ListingEventQuery) OnlyX(ctx context.Context) *ListingEvent { + node, err := _q.Only(ctx) + if err != nil { + panic(err) + } + return node +} + +// OnlyID is like Only, but returns the only ListingEvent ID in the query. +// Returns a *NotSingularError when more than one ListingEvent ID is found. +// Returns a *NotFoundError when no entities are found. +func (_q *ListingEventQuery) 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{listingevent.Label} + default: + err = &NotSingularError{listingevent.Label} + } + return +} + +// OnlyIDX is like OnlyID, but panics if an error occurs. +func (_q *ListingEventQuery) 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 ListingEvents. +func (_q *ListingEventQuery) All(ctx context.Context) ([]*ListingEvent, error) { + ctx = setContextOp(ctx, _q.ctx, ent.OpQueryAll) + if err := _q.prepareQuery(ctx); err != nil { + return nil, err + } + qr := querierAll[[]*ListingEvent, *ListingEventQuery]() + return withInterceptors[[]*ListingEvent](ctx, _q, qr, _q.inters) +} + +// AllX is like All, but panics if an error occurs. +func (_q *ListingEventQuery) AllX(ctx context.Context) []*ListingEvent { + nodes, err := _q.All(ctx) + if err != nil { + panic(err) + } + return nodes +} + +// IDs executes the query and returns a list of ListingEvent IDs. +func (_q *ListingEventQuery) 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(listingevent.FieldID).Scan(ctx, &ids); err != nil { + return nil, err + } + return ids, nil +} + +// IDsX is like IDs, but panics if an error occurs. +func (_q *ListingEventQuery) 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 *ListingEventQuery) 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[*ListingEventQuery](), _q.inters) +} + +// CountX is like Count, but panics if an error occurs. +func (_q *ListingEventQuery) 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 *ListingEventQuery) 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 *ListingEventQuery) ExistX(ctx context.Context) bool { + exist, err := _q.Exist(ctx) + if err != nil { + panic(err) + } + return exist +} + +// Clone returns a duplicate of the ListingEventQuery 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 *ListingEventQuery) Clone() *ListingEventQuery { + if _q == nil { + return nil + } + return &ListingEventQuery{ + config: _q.config, + ctx: _q.ctx.Clone(), + order: append([]listingevent.OrderOption{}, _q.order...), + inters: append([]Interceptor{}, _q.inters...), + predicates: append([]predicate.ListingEvent{}, _q.predicates...), + // clone intermediate query. + sql: _q.sql.Clone(), + path: _q.path, + } +} + +// GroupBy is used to group vertices by one or more fields/columns. +// It is often used with aggregate functions, like: count, max, mean, min, sum. +// +// Example: +// +// var v []struct { +// ListingID uuid.UUID `json:"listing_id,omitempty"` +// Count int `json:"count,omitempty"` +// } +// +// client.ListingEvent.Query(). +// GroupBy(listingevent.FieldListingID). +// Aggregate(ent.Count()). +// Scan(ctx, &v) +func (_q *ListingEventQuery) GroupBy(field string, fields ...string) *ListingEventGroupBy { + _q.ctx.Fields = append([]string{field}, fields...) + grbuild := &ListingEventGroupBy{build: _q} + grbuild.flds = &_q.ctx.Fields + grbuild.label = listingevent.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 { +// ListingID uuid.UUID `json:"listing_id,omitempty"` +// } +// +// client.ListingEvent.Query(). +// Select(listingevent.FieldListingID). +// Scan(ctx, &v) +func (_q *ListingEventQuery) Select(fields ...string) *ListingEventSelect { + _q.ctx.Fields = append(_q.ctx.Fields, fields...) + sbuild := &ListingEventSelect{ListingEventQuery: _q} + sbuild.label = listingevent.Label + sbuild.flds, sbuild.scan = &_q.ctx.Fields, sbuild.Scan + return sbuild +} + +// Aggregate returns a ListingEventSelect configured with the given aggregations. +func (_q *ListingEventQuery) Aggregate(fns ...AggregateFunc) *ListingEventSelect { + return _q.Select().Aggregate(fns...) +} + +func (_q *ListingEventQuery) 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 !listingevent.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 *ListingEventQuery) sqlAll(ctx context.Context, hooks ...queryHook) ([]*ListingEvent, error) { + var ( + nodes = []*ListingEvent{} + _spec = _q.querySpec() + ) + _spec.ScanValues = func(columns []string) ([]any, error) { + return (*ListingEvent).scanValues(nil, columns) + } + _spec.Assign = func(columns []string, values []any) error { + node := &ListingEvent{config: _q.config} + nodes = append(nodes, node) + return node.assignValues(columns, values) + } + for i := range hooks { + hooks[i](ctx, _spec) + } + if err := sqlgraph.QueryNodes(ctx, _q.driver, _spec); err != nil { + return nil, err + } + if len(nodes) == 0 { + return nodes, nil + } + return nodes, nil +} + +func (_q *ListingEventQuery) sqlCount(ctx context.Context) (int, error) { + _spec := _q.querySpec() + _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 *ListingEventQuery) querySpec() *sqlgraph.QuerySpec { + _spec := sqlgraph.NewQuerySpec(listingevent.Table, listingevent.Columns, sqlgraph.NewFieldSpec(listingevent.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, listingevent.FieldID) + for i := range fields { + if fields[i] != listingevent.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 *ListingEventQuery) sqlQuery(ctx context.Context) *sql.Selector { + builder := sql.Dialect(_q.driver.Dialect()) + t1 := builder.Table(listingevent.Table) + columns := _q.ctx.Fields + if len(columns) == 0 { + columns = listingevent.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 _, 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 +} + +// ListingEventGroupBy is the group-by builder for ListingEvent entities. +type ListingEventGroupBy struct { + selector + build *ListingEventQuery +} + +// Aggregate adds the given aggregation functions to the group-by query. +func (_g *ListingEventGroupBy) Aggregate(fns ...AggregateFunc) *ListingEventGroupBy { + _g.fns = append(_g.fns, fns...) + return _g +} + +// Scan applies the selector query and scans the result into the given value. +func (_g *ListingEventGroupBy) 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[*ListingEventQuery, *ListingEventGroupBy](ctx, _g.build, _g, _g.build.inters, v) +} + +func (_g *ListingEventGroupBy) sqlScan(ctx context.Context, root *ListingEventQuery, 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) +} + +// ListingEventSelect is the builder for selecting fields of ListingEvent entities. +type ListingEventSelect struct { + *ListingEventQuery + selector +} + +// Aggregate adds the given aggregation functions to the selector query. +func (_s *ListingEventSelect) Aggregate(fns ...AggregateFunc) *ListingEventSelect { + _s.fns = append(_s.fns, fns...) + return _s +} + +// Scan applies the selector query and scans the result into the given value. +func (_s *ListingEventSelect) 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[*ListingEventQuery, *ListingEventSelect](ctx, _s.ListingEventQuery, _s, _s.inters, v) +} + +func (_s *ListingEventSelect) sqlScan(ctx context.Context, root *ListingEventQuery, 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/backend/ent/listingevent_update.go b/backend/ent/listingevent_update.go new file mode 100644 index 0000000..e330088 --- /dev/null +++ b/backend/ent/listingevent_update.go @@ -0,0 +1,187 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "context" + "errors" + "fmt" + + "entgo.io/ent/dialect/sql" + "entgo.io/ent/dialect/sql/sqlgraph" + "entgo.io/ent/schema/field" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/listingevent" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/predicate" +) + +// ListingEventUpdate is the builder for updating ListingEvent entities. +type ListingEventUpdate struct { + config + hooks []Hook + mutation *ListingEventMutation +} + +// Where appends a list predicates to the ListingEventUpdate builder. +func (_u *ListingEventUpdate) Where(ps ...predicate.ListingEvent) *ListingEventUpdate { + _u.mutation.Where(ps...) + return _u +} + +// Mutation returns the ListingEventMutation object of the builder. +func (_u *ListingEventUpdate) Mutation() *ListingEventMutation { + return _u.mutation +} + +// Save executes the query and returns the number of nodes affected by the update operation. +func (_u *ListingEventUpdate) 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 *ListingEventUpdate) SaveX(ctx context.Context) int { + affected, err := _u.Save(ctx) + if err != nil { + panic(err) + } + return affected +} + +// Exec executes the query. +func (_u *ListingEventUpdate) Exec(ctx context.Context) error { + _, err := _u.Save(ctx) + return err +} + +// ExecX is like Exec, but panics if an error occurs. +func (_u *ListingEventUpdate) ExecX(ctx context.Context) { + if err := _u.Exec(ctx); err != nil { + panic(err) + } +} + +func (_u *ListingEventUpdate) sqlSave(ctx context.Context) (_node int, err error) { + _spec := sqlgraph.NewUpdateSpec(listingevent.Table, listingevent.Columns, sqlgraph.NewFieldSpec(listingevent.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.FromStateCleared() { + _spec.ClearField(listingevent.FieldFromState, field.TypeString) + } + if _u.mutation.ReasonCleared() { + _spec.ClearField(listingevent.FieldReason, field.TypeString) + } + if _node, err = sqlgraph.UpdateNodes(ctx, _u.driver, _spec); err != nil { + if _, ok := err.(*sqlgraph.NotFoundError); ok { + err = &NotFoundError{listingevent.Label} + } else if sqlgraph.IsConstraintError(err) { + err = &ConstraintError{msg: err.Error(), wrap: err} + } + return 0, err + } + _u.mutation.done = true + return _node, nil +} + +// ListingEventUpdateOne is the builder for updating a single ListingEvent entity. +type ListingEventUpdateOne struct { + config + fields []string + hooks []Hook + mutation *ListingEventMutation +} + +// Mutation returns the ListingEventMutation object of the builder. +func (_u *ListingEventUpdateOne) Mutation() *ListingEventMutation { + return _u.mutation +} + +// Where appends a list predicates to the ListingEventUpdate builder. +func (_u *ListingEventUpdateOne) Where(ps ...predicate.ListingEvent) *ListingEventUpdateOne { + _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 *ListingEventUpdateOne) Select(field string, fields ...string) *ListingEventUpdateOne { + _u.fields = append([]string{field}, fields...) + return _u +} + +// Save executes the query and returns the updated ListingEvent entity. +func (_u *ListingEventUpdateOne) Save(ctx context.Context) (*ListingEvent, error) { + return withHooks(ctx, _u.sqlSave, _u.mutation, _u.hooks) +} + +// SaveX is like Save, but panics if an error occurs. +func (_u *ListingEventUpdateOne) SaveX(ctx context.Context) *ListingEvent { + node, err := _u.Save(ctx) + if err != nil { + panic(err) + } + return node +} + +// Exec executes the query on the entity. +func (_u *ListingEventUpdateOne) Exec(ctx context.Context) error { + _, err := _u.Save(ctx) + return err +} + +// ExecX is like Exec, but panics if an error occurs. +func (_u *ListingEventUpdateOne) ExecX(ctx context.Context) { + if err := _u.Exec(ctx); err != nil { + panic(err) + } +} + +func (_u *ListingEventUpdateOne) sqlSave(ctx context.Context) (_node *ListingEvent, err error) { + _spec := sqlgraph.NewUpdateSpec(listingevent.Table, listingevent.Columns, sqlgraph.NewFieldSpec(listingevent.FieldID, field.TypeUUID)) + id, ok := _u.mutation.ID() + if !ok { + return nil, &ValidationError{Name: "id", err: errors.New(`ent: missing "ListingEvent.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, listingevent.FieldID) + for _, f := range fields { + if !listingevent.ValidColumn(f) { + return nil, &ValidationError{Name: f, err: fmt.Errorf("ent: invalid field %q for query", f)} + } + if f != listingevent.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.FromStateCleared() { + _spec.ClearField(listingevent.FieldFromState, field.TypeString) + } + if _u.mutation.ReasonCleared() { + _spec.ClearField(listingevent.FieldReason, field.TypeString) + } + _node = &ListingEvent{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{listingevent.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/backend/ent/listingmedia.go b/backend/ent/listingmedia.go new file mode 100644 index 0000000..4b643e4 --- /dev/null +++ b/backend/ent/listingmedia.go @@ -0,0 +1,197 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "fmt" + "strings" + "time" + + "entgo.io/ent" + "entgo.io/ent/dialect/sql" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/listingmedia" + "github.com/google/uuid" +) + +// ListingMedia is the model entity for the ListingMedia schema. +type ListingMedia struct { + config `json:"-"` + // ID of the ent. + ID uuid.UUID `json:"id,omitempty"` + // ListingID holds the value of the "listing_id" field. + ListingID uuid.UUID `json:"listing_id,omitempty"` + // Ordinal holds the value of the "ordinal" field. + Ordinal int `json:"ordinal,omitempty"` + // ContentType holds the value of the "content_type" field. + ContentType string `json:"content_type,omitempty"` + // ByteSize holds the value of the "byte_size" field. + ByteSize int64 `json:"byte_size,omitempty"` + // ChecksumSha256 holds the value of the "checksum_sha256" field. + ChecksumSha256 string `json:"checksum_sha256,omitempty"` + // ObjectReference holds the value of the "object_reference" field. + ObjectReference string `json:"object_reference,omitempty"` + // State holds the value of the "state" field. + State listingmedia.State `json:"state,omitempty"` + // CreatedAt holds the value of the "created_at" field. + CreatedAt time.Time `json:"created_at,omitempty"` + // UpdatedAt holds the value of the "updated_at" field. + UpdatedAt time.Time `json:"updated_at,omitempty"` + selectValues sql.SelectValues +} + +// scanValues returns the types for scanning values from sql.Rows. +func (*ListingMedia) scanValues(columns []string) ([]any, error) { + values := make([]any, len(columns)) + for i := range columns { + switch columns[i] { + case listingmedia.FieldOrdinal, listingmedia.FieldByteSize: + values[i] = new(sql.NullInt64) + case listingmedia.FieldContentType, listingmedia.FieldChecksumSha256, listingmedia.FieldObjectReference, listingmedia.FieldState: + values[i] = new(sql.NullString) + case listingmedia.FieldCreatedAt, listingmedia.FieldUpdatedAt: + values[i] = new(sql.NullTime) + case listingmedia.FieldID, listingmedia.FieldListingID: + 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 ListingMedia fields. +func (_m *ListingMedia) 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 listingmedia.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 listingmedia.FieldListingID: + if value, ok := values[i].(*uuid.UUID); !ok { + return fmt.Errorf("unexpected type %T for field listing_id", values[i]) + } else if value != nil { + _m.ListingID = *value + } + case listingmedia.FieldOrdinal: + if value, ok := values[i].(*sql.NullInt64); !ok { + return fmt.Errorf("unexpected type %T for field ordinal", values[i]) + } else if value.Valid { + _m.Ordinal = int(value.Int64) + } + case listingmedia.FieldContentType: + if value, ok := values[i].(*sql.NullString); !ok { + return fmt.Errorf("unexpected type %T for field content_type", values[i]) + } else if value.Valid { + _m.ContentType = value.String + } + case listingmedia.FieldByteSize: + if value, ok := values[i].(*sql.NullInt64); !ok { + return fmt.Errorf("unexpected type %T for field byte_size", values[i]) + } else if value.Valid { + _m.ByteSize = value.Int64 + } + case listingmedia.FieldChecksumSha256: + if value, ok := values[i].(*sql.NullString); !ok { + return fmt.Errorf("unexpected type %T for field checksum_sha256", values[i]) + } else if value.Valid { + _m.ChecksumSha256 = value.String + } + case listingmedia.FieldObjectReference: + if value, ok := values[i].(*sql.NullString); !ok { + return fmt.Errorf("unexpected type %T for field object_reference", values[i]) + } else if value.Valid { + _m.ObjectReference = value.String + } + case listingmedia.FieldState: + if value, ok := values[i].(*sql.NullString); !ok { + return fmt.Errorf("unexpected type %T for field state", values[i]) + } else if value.Valid { + _m.State = listingmedia.State(value.String) + } + case listingmedia.FieldCreatedAt: + if value, ok := values[i].(*sql.NullTime); !ok { + return fmt.Errorf("unexpected type %T for field created_at", values[i]) + } else if value.Valid { + _m.CreatedAt = value.Time + } + case listingmedia.FieldUpdatedAt: + if value, ok := values[i].(*sql.NullTime); !ok { + return fmt.Errorf("unexpected type %T for field updated_at", values[i]) + } else if value.Valid { + _m.UpdatedAt = value.Time + } + default: + _m.selectValues.Set(columns[i], values[i]) + } + } + return nil +} + +// Value returns the ent.Value that was dynamically selected and assigned to the ListingMedia. +// This includes values selected through modifiers, order, etc. +func (_m *ListingMedia) Value(name string) (ent.Value, error) { + return _m.selectValues.Get(name) +} + +// Update returns a builder for updating this ListingMedia. +// Note that you need to call ListingMedia.Unwrap() before calling this method if this ListingMedia +// was returned from a transaction, and the transaction was committed or rolled back. +func (_m *ListingMedia) Update() *ListingMediaUpdateOne { + return NewListingMediaClient(_m.config).UpdateOne(_m) +} + +// Unwrap unwraps the ListingMedia 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 *ListingMedia) Unwrap() *ListingMedia { + _tx, ok := _m.config.driver.(*txDriver) + if !ok { + panic("ent: ListingMedia is not a transactional entity") + } + _m.config.driver = _tx.drv + return _m +} + +// String implements the fmt.Stringer. +func (_m *ListingMedia) String() string { + var builder strings.Builder + builder.WriteString("ListingMedia(") + builder.WriteString(fmt.Sprintf("id=%v, ", _m.ID)) + builder.WriteString("listing_id=") + builder.WriteString(fmt.Sprintf("%v", _m.ListingID)) + builder.WriteString(", ") + builder.WriteString("ordinal=") + builder.WriteString(fmt.Sprintf("%v", _m.Ordinal)) + builder.WriteString(", ") + builder.WriteString("content_type=") + builder.WriteString(_m.ContentType) + builder.WriteString(", ") + builder.WriteString("byte_size=") + builder.WriteString(fmt.Sprintf("%v", _m.ByteSize)) + builder.WriteString(", ") + builder.WriteString("checksum_sha256=") + builder.WriteString(_m.ChecksumSha256) + builder.WriteString(", ") + builder.WriteString("object_reference=") + builder.WriteString(_m.ObjectReference) + builder.WriteString(", ") + builder.WriteString("state=") + builder.WriteString(fmt.Sprintf("%v", _m.State)) + builder.WriteString(", ") + builder.WriteString("created_at=") + builder.WriteString(_m.CreatedAt.Format(time.ANSIC)) + builder.WriteString(", ") + builder.WriteString("updated_at=") + builder.WriteString(_m.UpdatedAt.Format(time.ANSIC)) + builder.WriteByte(')') + return builder.String() +} + +// ListingMediaSlice is a parsable slice of ListingMedia. +type ListingMediaSlice []*ListingMedia diff --git a/backend/ent/listingmedia/listingmedia.go b/backend/ent/listingmedia/listingmedia.go new file mode 100644 index 0000000..139cbd1 --- /dev/null +++ b/backend/ent/listingmedia/listingmedia.go @@ -0,0 +1,163 @@ +// Code generated by ent, DO NOT EDIT. + +package listingmedia + +import ( + "fmt" + "time" + + "entgo.io/ent/dialect/sql" + "github.com/google/uuid" +) + +const ( + // Label holds the string label denoting the listingmedia type in the database. + Label = "listing_media" + // FieldID holds the string denoting the id field in the database. + FieldID = "id" + // FieldListingID holds the string denoting the listing_id field in the database. + FieldListingID = "listing_id" + // FieldOrdinal holds the string denoting the ordinal field in the database. + FieldOrdinal = "ordinal" + // FieldContentType holds the string denoting the content_type field in the database. + FieldContentType = "content_type" + // FieldByteSize holds the string denoting the byte_size field in the database. + FieldByteSize = "byte_size" + // FieldChecksumSha256 holds the string denoting the checksum_sha256 field in the database. + FieldChecksumSha256 = "checksum_sha256" + // FieldObjectReference holds the string denoting the object_reference field in the database. + FieldObjectReference = "object_reference" + // FieldState holds the string denoting the state field in the database. + FieldState = "state" + // FieldCreatedAt holds the string denoting the created_at field in the database. + FieldCreatedAt = "created_at" + // FieldUpdatedAt holds the string denoting the updated_at field in the database. + FieldUpdatedAt = "updated_at" + // Table holds the table name of the listingmedia in the database. + Table = "listing_media" +) + +// Columns holds all SQL columns for listingmedia fields. +var Columns = []string{ + FieldID, + FieldListingID, + FieldOrdinal, + FieldContentType, + FieldByteSize, + FieldChecksumSha256, + FieldObjectReference, + FieldState, + FieldCreatedAt, + FieldUpdatedAt, +} + +// 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 ( + // OrdinalValidator is a validator for the "ordinal" field. It is called by the builders before save. + OrdinalValidator func(int) error + // ContentTypeValidator is a validator for the "content_type" field. It is called by the builders before save. + ContentTypeValidator func(string) error + // ByteSizeValidator is a validator for the "byte_size" field. It is called by the builders before save. + ByteSizeValidator func(int64) error + // ChecksumSha256Validator is a validator for the "checksum_sha256" field. It is called by the builders before save. + ChecksumSha256Validator func(string) error + // ObjectReferenceValidator is a validator for the "object_reference" field. It is called by the builders before save. + ObjectReferenceValidator func(string) error + // DefaultCreatedAt holds the default value on creation for the "created_at" field. + DefaultCreatedAt func() time.Time + // DefaultUpdatedAt holds the default value on creation for the "updated_at" field. + DefaultUpdatedAt func() time.Time + // UpdateDefaultUpdatedAt holds the default value on update for the "updated_at" field. + UpdateDefaultUpdatedAt func() time.Time + // DefaultID holds the default value on creation for the "id" field. + DefaultID func() uuid.UUID +) + +// State defines the type for the "state" enum field. +type State string + +// StatePendingUpload is the default value of the State enum. +const DefaultState = StatePendingUpload + +// State values. +const ( + StatePendingUpload State = "pending_upload" + StateReady State = "ready" + StateDeleted State = "deleted" +) + +func (s State) String() string { + return string(s) +} + +// StateValidator is a validator for the "state" field enum values. It is called by the builders before save. +func StateValidator(s State) error { + switch s { + case StatePendingUpload, StateReady, StateDeleted: + return nil + default: + return fmt.Errorf("listingmedia: invalid enum value for state field: %q", s) + } +} + +// OrderOption defines the ordering options for the ListingMedia 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() +} + +// ByListingID orders the results by the listing_id field. +func ByListingID(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldListingID, opts...).ToFunc() +} + +// ByOrdinal orders the results by the ordinal field. +func ByOrdinal(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldOrdinal, opts...).ToFunc() +} + +// ByContentType orders the results by the content_type field. +func ByContentType(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldContentType, opts...).ToFunc() +} + +// ByByteSize orders the results by the byte_size field. +func ByByteSize(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldByteSize, opts...).ToFunc() +} + +// ByChecksumSha256 orders the results by the checksum_sha256 field. +func ByChecksumSha256(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldChecksumSha256, opts...).ToFunc() +} + +// ByObjectReference orders the results by the object_reference field. +func ByObjectReference(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldObjectReference, opts...).ToFunc() +} + +// ByState orders the results by the state field. +func ByState(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldState, opts...).ToFunc() +} + +// ByCreatedAt orders the results by the created_at field. +func ByCreatedAt(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldCreatedAt, opts...).ToFunc() +} + +// ByUpdatedAt orders the results by the updated_at field. +func ByUpdatedAt(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldUpdatedAt, opts...).ToFunc() +} diff --git a/backend/ent/listingmedia/where.go b/backend/ent/listingmedia/where.go new file mode 100644 index 0000000..88418f4 --- /dev/null +++ b/backend/ent/listingmedia/where.go @@ -0,0 +1,526 @@ +// Code generated by ent, DO NOT EDIT. + +package listingmedia + +import ( + "time" + + "entgo.io/ent/dialect/sql" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/predicate" + "github.com/google/uuid" +) + +// ID filters vertices based on their ID field. +func ID(id uuid.UUID) predicate.ListingMedia { + return predicate.ListingMedia(sql.FieldEQ(FieldID, id)) +} + +// IDEQ applies the EQ predicate on the ID field. +func IDEQ(id uuid.UUID) predicate.ListingMedia { + return predicate.ListingMedia(sql.FieldEQ(FieldID, id)) +} + +// IDNEQ applies the NEQ predicate on the ID field. +func IDNEQ(id uuid.UUID) predicate.ListingMedia { + return predicate.ListingMedia(sql.FieldNEQ(FieldID, id)) +} + +// IDIn applies the In predicate on the ID field. +func IDIn(ids ...uuid.UUID) predicate.ListingMedia { + return predicate.ListingMedia(sql.FieldIn(FieldID, ids...)) +} + +// IDNotIn applies the NotIn predicate on the ID field. +func IDNotIn(ids ...uuid.UUID) predicate.ListingMedia { + return predicate.ListingMedia(sql.FieldNotIn(FieldID, ids...)) +} + +// IDGT applies the GT predicate on the ID field. +func IDGT(id uuid.UUID) predicate.ListingMedia { + return predicate.ListingMedia(sql.FieldGT(FieldID, id)) +} + +// IDGTE applies the GTE predicate on the ID field. +func IDGTE(id uuid.UUID) predicate.ListingMedia { + return predicate.ListingMedia(sql.FieldGTE(FieldID, id)) +} + +// IDLT applies the LT predicate on the ID field. +func IDLT(id uuid.UUID) predicate.ListingMedia { + return predicate.ListingMedia(sql.FieldLT(FieldID, id)) +} + +// IDLTE applies the LTE predicate on the ID field. +func IDLTE(id uuid.UUID) predicate.ListingMedia { + return predicate.ListingMedia(sql.FieldLTE(FieldID, id)) +} + +// ListingID applies equality check predicate on the "listing_id" field. It's identical to ListingIDEQ. +func ListingID(v uuid.UUID) predicate.ListingMedia { + return predicate.ListingMedia(sql.FieldEQ(FieldListingID, v)) +} + +// Ordinal applies equality check predicate on the "ordinal" field. It's identical to OrdinalEQ. +func Ordinal(v int) predicate.ListingMedia { + return predicate.ListingMedia(sql.FieldEQ(FieldOrdinal, v)) +} + +// ContentType applies equality check predicate on the "content_type" field. It's identical to ContentTypeEQ. +func ContentType(v string) predicate.ListingMedia { + return predicate.ListingMedia(sql.FieldEQ(FieldContentType, v)) +} + +// ByteSize applies equality check predicate on the "byte_size" field. It's identical to ByteSizeEQ. +func ByteSize(v int64) predicate.ListingMedia { + return predicate.ListingMedia(sql.FieldEQ(FieldByteSize, v)) +} + +// ChecksumSha256 applies equality check predicate on the "checksum_sha256" field. It's identical to ChecksumSha256EQ. +func ChecksumSha256(v string) predicate.ListingMedia { + return predicate.ListingMedia(sql.FieldEQ(FieldChecksumSha256, v)) +} + +// ObjectReference applies equality check predicate on the "object_reference" field. It's identical to ObjectReferenceEQ. +func ObjectReference(v string) predicate.ListingMedia { + return predicate.ListingMedia(sql.FieldEQ(FieldObjectReference, v)) +} + +// CreatedAt applies equality check predicate on the "created_at" field. It's identical to CreatedAtEQ. +func CreatedAt(v time.Time) predicate.ListingMedia { + return predicate.ListingMedia(sql.FieldEQ(FieldCreatedAt, v)) +} + +// UpdatedAt applies equality check predicate on the "updated_at" field. It's identical to UpdatedAtEQ. +func UpdatedAt(v time.Time) predicate.ListingMedia { + return predicate.ListingMedia(sql.FieldEQ(FieldUpdatedAt, v)) +} + +// ListingIDEQ applies the EQ predicate on the "listing_id" field. +func ListingIDEQ(v uuid.UUID) predicate.ListingMedia { + return predicate.ListingMedia(sql.FieldEQ(FieldListingID, v)) +} + +// ListingIDNEQ applies the NEQ predicate on the "listing_id" field. +func ListingIDNEQ(v uuid.UUID) predicate.ListingMedia { + return predicate.ListingMedia(sql.FieldNEQ(FieldListingID, v)) +} + +// ListingIDIn applies the In predicate on the "listing_id" field. +func ListingIDIn(vs ...uuid.UUID) predicate.ListingMedia { + return predicate.ListingMedia(sql.FieldIn(FieldListingID, vs...)) +} + +// ListingIDNotIn applies the NotIn predicate on the "listing_id" field. +func ListingIDNotIn(vs ...uuid.UUID) predicate.ListingMedia { + return predicate.ListingMedia(sql.FieldNotIn(FieldListingID, vs...)) +} + +// ListingIDGT applies the GT predicate on the "listing_id" field. +func ListingIDGT(v uuid.UUID) predicate.ListingMedia { + return predicate.ListingMedia(sql.FieldGT(FieldListingID, v)) +} + +// ListingIDGTE applies the GTE predicate on the "listing_id" field. +func ListingIDGTE(v uuid.UUID) predicate.ListingMedia { + return predicate.ListingMedia(sql.FieldGTE(FieldListingID, v)) +} + +// ListingIDLT applies the LT predicate on the "listing_id" field. +func ListingIDLT(v uuid.UUID) predicate.ListingMedia { + return predicate.ListingMedia(sql.FieldLT(FieldListingID, v)) +} + +// ListingIDLTE applies the LTE predicate on the "listing_id" field. +func ListingIDLTE(v uuid.UUID) predicate.ListingMedia { + return predicate.ListingMedia(sql.FieldLTE(FieldListingID, v)) +} + +// OrdinalEQ applies the EQ predicate on the "ordinal" field. +func OrdinalEQ(v int) predicate.ListingMedia { + return predicate.ListingMedia(sql.FieldEQ(FieldOrdinal, v)) +} + +// OrdinalNEQ applies the NEQ predicate on the "ordinal" field. +func OrdinalNEQ(v int) predicate.ListingMedia { + return predicate.ListingMedia(sql.FieldNEQ(FieldOrdinal, v)) +} + +// OrdinalIn applies the In predicate on the "ordinal" field. +func OrdinalIn(vs ...int) predicate.ListingMedia { + return predicate.ListingMedia(sql.FieldIn(FieldOrdinal, vs...)) +} + +// OrdinalNotIn applies the NotIn predicate on the "ordinal" field. +func OrdinalNotIn(vs ...int) predicate.ListingMedia { + return predicate.ListingMedia(sql.FieldNotIn(FieldOrdinal, vs...)) +} + +// OrdinalGT applies the GT predicate on the "ordinal" field. +func OrdinalGT(v int) predicate.ListingMedia { + return predicate.ListingMedia(sql.FieldGT(FieldOrdinal, v)) +} + +// OrdinalGTE applies the GTE predicate on the "ordinal" field. +func OrdinalGTE(v int) predicate.ListingMedia { + return predicate.ListingMedia(sql.FieldGTE(FieldOrdinal, v)) +} + +// OrdinalLT applies the LT predicate on the "ordinal" field. +func OrdinalLT(v int) predicate.ListingMedia { + return predicate.ListingMedia(sql.FieldLT(FieldOrdinal, v)) +} + +// OrdinalLTE applies the LTE predicate on the "ordinal" field. +func OrdinalLTE(v int) predicate.ListingMedia { + return predicate.ListingMedia(sql.FieldLTE(FieldOrdinal, v)) +} + +// ContentTypeEQ applies the EQ predicate on the "content_type" field. +func ContentTypeEQ(v string) predicate.ListingMedia { + return predicate.ListingMedia(sql.FieldEQ(FieldContentType, v)) +} + +// ContentTypeNEQ applies the NEQ predicate on the "content_type" field. +func ContentTypeNEQ(v string) predicate.ListingMedia { + return predicate.ListingMedia(sql.FieldNEQ(FieldContentType, v)) +} + +// ContentTypeIn applies the In predicate on the "content_type" field. +func ContentTypeIn(vs ...string) predicate.ListingMedia { + return predicate.ListingMedia(sql.FieldIn(FieldContentType, vs...)) +} + +// ContentTypeNotIn applies the NotIn predicate on the "content_type" field. +func ContentTypeNotIn(vs ...string) predicate.ListingMedia { + return predicate.ListingMedia(sql.FieldNotIn(FieldContentType, vs...)) +} + +// ContentTypeGT applies the GT predicate on the "content_type" field. +func ContentTypeGT(v string) predicate.ListingMedia { + return predicate.ListingMedia(sql.FieldGT(FieldContentType, v)) +} + +// ContentTypeGTE applies the GTE predicate on the "content_type" field. +func ContentTypeGTE(v string) predicate.ListingMedia { + return predicate.ListingMedia(sql.FieldGTE(FieldContentType, v)) +} + +// ContentTypeLT applies the LT predicate on the "content_type" field. +func ContentTypeLT(v string) predicate.ListingMedia { + return predicate.ListingMedia(sql.FieldLT(FieldContentType, v)) +} + +// ContentTypeLTE applies the LTE predicate on the "content_type" field. +func ContentTypeLTE(v string) predicate.ListingMedia { + return predicate.ListingMedia(sql.FieldLTE(FieldContentType, v)) +} + +// ContentTypeContains applies the Contains predicate on the "content_type" field. +func ContentTypeContains(v string) predicate.ListingMedia { + return predicate.ListingMedia(sql.FieldContains(FieldContentType, v)) +} + +// ContentTypeHasPrefix applies the HasPrefix predicate on the "content_type" field. +func ContentTypeHasPrefix(v string) predicate.ListingMedia { + return predicate.ListingMedia(sql.FieldHasPrefix(FieldContentType, v)) +} + +// ContentTypeHasSuffix applies the HasSuffix predicate on the "content_type" field. +func ContentTypeHasSuffix(v string) predicate.ListingMedia { + return predicate.ListingMedia(sql.FieldHasSuffix(FieldContentType, v)) +} + +// ContentTypeEqualFold applies the EqualFold predicate on the "content_type" field. +func ContentTypeEqualFold(v string) predicate.ListingMedia { + return predicate.ListingMedia(sql.FieldEqualFold(FieldContentType, v)) +} + +// ContentTypeContainsFold applies the ContainsFold predicate on the "content_type" field. +func ContentTypeContainsFold(v string) predicate.ListingMedia { + return predicate.ListingMedia(sql.FieldContainsFold(FieldContentType, v)) +} + +// ByteSizeEQ applies the EQ predicate on the "byte_size" field. +func ByteSizeEQ(v int64) predicate.ListingMedia { + return predicate.ListingMedia(sql.FieldEQ(FieldByteSize, v)) +} + +// ByteSizeNEQ applies the NEQ predicate on the "byte_size" field. +func ByteSizeNEQ(v int64) predicate.ListingMedia { + return predicate.ListingMedia(sql.FieldNEQ(FieldByteSize, v)) +} + +// ByteSizeIn applies the In predicate on the "byte_size" field. +func ByteSizeIn(vs ...int64) predicate.ListingMedia { + return predicate.ListingMedia(sql.FieldIn(FieldByteSize, vs...)) +} + +// ByteSizeNotIn applies the NotIn predicate on the "byte_size" field. +func ByteSizeNotIn(vs ...int64) predicate.ListingMedia { + return predicate.ListingMedia(sql.FieldNotIn(FieldByteSize, vs...)) +} + +// ByteSizeGT applies the GT predicate on the "byte_size" field. +func ByteSizeGT(v int64) predicate.ListingMedia { + return predicate.ListingMedia(sql.FieldGT(FieldByteSize, v)) +} + +// ByteSizeGTE applies the GTE predicate on the "byte_size" field. +func ByteSizeGTE(v int64) predicate.ListingMedia { + return predicate.ListingMedia(sql.FieldGTE(FieldByteSize, v)) +} + +// ByteSizeLT applies the LT predicate on the "byte_size" field. +func ByteSizeLT(v int64) predicate.ListingMedia { + return predicate.ListingMedia(sql.FieldLT(FieldByteSize, v)) +} + +// ByteSizeLTE applies the LTE predicate on the "byte_size" field. +func ByteSizeLTE(v int64) predicate.ListingMedia { + return predicate.ListingMedia(sql.FieldLTE(FieldByteSize, v)) +} + +// ChecksumSha256EQ applies the EQ predicate on the "checksum_sha256" field. +func ChecksumSha256EQ(v string) predicate.ListingMedia { + return predicate.ListingMedia(sql.FieldEQ(FieldChecksumSha256, v)) +} + +// ChecksumSha256NEQ applies the NEQ predicate on the "checksum_sha256" field. +func ChecksumSha256NEQ(v string) predicate.ListingMedia { + return predicate.ListingMedia(sql.FieldNEQ(FieldChecksumSha256, v)) +} + +// ChecksumSha256In applies the In predicate on the "checksum_sha256" field. +func ChecksumSha256In(vs ...string) predicate.ListingMedia { + return predicate.ListingMedia(sql.FieldIn(FieldChecksumSha256, vs...)) +} + +// ChecksumSha256NotIn applies the NotIn predicate on the "checksum_sha256" field. +func ChecksumSha256NotIn(vs ...string) predicate.ListingMedia { + return predicate.ListingMedia(sql.FieldNotIn(FieldChecksumSha256, vs...)) +} + +// ChecksumSha256GT applies the GT predicate on the "checksum_sha256" field. +func ChecksumSha256GT(v string) predicate.ListingMedia { + return predicate.ListingMedia(sql.FieldGT(FieldChecksumSha256, v)) +} + +// ChecksumSha256GTE applies the GTE predicate on the "checksum_sha256" field. +func ChecksumSha256GTE(v string) predicate.ListingMedia { + return predicate.ListingMedia(sql.FieldGTE(FieldChecksumSha256, v)) +} + +// ChecksumSha256LT applies the LT predicate on the "checksum_sha256" field. +func ChecksumSha256LT(v string) predicate.ListingMedia { + return predicate.ListingMedia(sql.FieldLT(FieldChecksumSha256, v)) +} + +// ChecksumSha256LTE applies the LTE predicate on the "checksum_sha256" field. +func ChecksumSha256LTE(v string) predicate.ListingMedia { + return predicate.ListingMedia(sql.FieldLTE(FieldChecksumSha256, v)) +} + +// ChecksumSha256Contains applies the Contains predicate on the "checksum_sha256" field. +func ChecksumSha256Contains(v string) predicate.ListingMedia { + return predicate.ListingMedia(sql.FieldContains(FieldChecksumSha256, v)) +} + +// ChecksumSha256HasPrefix applies the HasPrefix predicate on the "checksum_sha256" field. +func ChecksumSha256HasPrefix(v string) predicate.ListingMedia { + return predicate.ListingMedia(sql.FieldHasPrefix(FieldChecksumSha256, v)) +} + +// ChecksumSha256HasSuffix applies the HasSuffix predicate on the "checksum_sha256" field. +func ChecksumSha256HasSuffix(v string) predicate.ListingMedia { + return predicate.ListingMedia(sql.FieldHasSuffix(FieldChecksumSha256, v)) +} + +// ChecksumSha256EqualFold applies the EqualFold predicate on the "checksum_sha256" field. +func ChecksumSha256EqualFold(v string) predicate.ListingMedia { + return predicate.ListingMedia(sql.FieldEqualFold(FieldChecksumSha256, v)) +} + +// ChecksumSha256ContainsFold applies the ContainsFold predicate on the "checksum_sha256" field. +func ChecksumSha256ContainsFold(v string) predicate.ListingMedia { + return predicate.ListingMedia(sql.FieldContainsFold(FieldChecksumSha256, v)) +} + +// ObjectReferenceEQ applies the EQ predicate on the "object_reference" field. +func ObjectReferenceEQ(v string) predicate.ListingMedia { + return predicate.ListingMedia(sql.FieldEQ(FieldObjectReference, v)) +} + +// ObjectReferenceNEQ applies the NEQ predicate on the "object_reference" field. +func ObjectReferenceNEQ(v string) predicate.ListingMedia { + return predicate.ListingMedia(sql.FieldNEQ(FieldObjectReference, v)) +} + +// ObjectReferenceIn applies the In predicate on the "object_reference" field. +func ObjectReferenceIn(vs ...string) predicate.ListingMedia { + return predicate.ListingMedia(sql.FieldIn(FieldObjectReference, vs...)) +} + +// ObjectReferenceNotIn applies the NotIn predicate on the "object_reference" field. +func ObjectReferenceNotIn(vs ...string) predicate.ListingMedia { + return predicate.ListingMedia(sql.FieldNotIn(FieldObjectReference, vs...)) +} + +// ObjectReferenceGT applies the GT predicate on the "object_reference" field. +func ObjectReferenceGT(v string) predicate.ListingMedia { + return predicate.ListingMedia(sql.FieldGT(FieldObjectReference, v)) +} + +// ObjectReferenceGTE applies the GTE predicate on the "object_reference" field. +func ObjectReferenceGTE(v string) predicate.ListingMedia { + return predicate.ListingMedia(sql.FieldGTE(FieldObjectReference, v)) +} + +// ObjectReferenceLT applies the LT predicate on the "object_reference" field. +func ObjectReferenceLT(v string) predicate.ListingMedia { + return predicate.ListingMedia(sql.FieldLT(FieldObjectReference, v)) +} + +// ObjectReferenceLTE applies the LTE predicate on the "object_reference" field. +func ObjectReferenceLTE(v string) predicate.ListingMedia { + return predicate.ListingMedia(sql.FieldLTE(FieldObjectReference, v)) +} + +// ObjectReferenceContains applies the Contains predicate on the "object_reference" field. +func ObjectReferenceContains(v string) predicate.ListingMedia { + return predicate.ListingMedia(sql.FieldContains(FieldObjectReference, v)) +} + +// ObjectReferenceHasPrefix applies the HasPrefix predicate on the "object_reference" field. +func ObjectReferenceHasPrefix(v string) predicate.ListingMedia { + return predicate.ListingMedia(sql.FieldHasPrefix(FieldObjectReference, v)) +} + +// ObjectReferenceHasSuffix applies the HasSuffix predicate on the "object_reference" field. +func ObjectReferenceHasSuffix(v string) predicate.ListingMedia { + return predicate.ListingMedia(sql.FieldHasSuffix(FieldObjectReference, v)) +} + +// ObjectReferenceEqualFold applies the EqualFold predicate on the "object_reference" field. +func ObjectReferenceEqualFold(v string) predicate.ListingMedia { + return predicate.ListingMedia(sql.FieldEqualFold(FieldObjectReference, v)) +} + +// ObjectReferenceContainsFold applies the ContainsFold predicate on the "object_reference" field. +func ObjectReferenceContainsFold(v string) predicate.ListingMedia { + return predicate.ListingMedia(sql.FieldContainsFold(FieldObjectReference, v)) +} + +// StateEQ applies the EQ predicate on the "state" field. +func StateEQ(v State) predicate.ListingMedia { + return predicate.ListingMedia(sql.FieldEQ(FieldState, v)) +} + +// StateNEQ applies the NEQ predicate on the "state" field. +func StateNEQ(v State) predicate.ListingMedia { + return predicate.ListingMedia(sql.FieldNEQ(FieldState, v)) +} + +// StateIn applies the In predicate on the "state" field. +func StateIn(vs ...State) predicate.ListingMedia { + return predicate.ListingMedia(sql.FieldIn(FieldState, vs...)) +} + +// StateNotIn applies the NotIn predicate on the "state" field. +func StateNotIn(vs ...State) predicate.ListingMedia { + return predicate.ListingMedia(sql.FieldNotIn(FieldState, vs...)) +} + +// CreatedAtEQ applies the EQ predicate on the "created_at" field. +func CreatedAtEQ(v time.Time) predicate.ListingMedia { + return predicate.ListingMedia(sql.FieldEQ(FieldCreatedAt, v)) +} + +// CreatedAtNEQ applies the NEQ predicate on the "created_at" field. +func CreatedAtNEQ(v time.Time) predicate.ListingMedia { + return predicate.ListingMedia(sql.FieldNEQ(FieldCreatedAt, v)) +} + +// CreatedAtIn applies the In predicate on the "created_at" field. +func CreatedAtIn(vs ...time.Time) predicate.ListingMedia { + return predicate.ListingMedia(sql.FieldIn(FieldCreatedAt, vs...)) +} + +// CreatedAtNotIn applies the NotIn predicate on the "created_at" field. +func CreatedAtNotIn(vs ...time.Time) predicate.ListingMedia { + return predicate.ListingMedia(sql.FieldNotIn(FieldCreatedAt, vs...)) +} + +// CreatedAtGT applies the GT predicate on the "created_at" field. +func CreatedAtGT(v time.Time) predicate.ListingMedia { + return predicate.ListingMedia(sql.FieldGT(FieldCreatedAt, v)) +} + +// CreatedAtGTE applies the GTE predicate on the "created_at" field. +func CreatedAtGTE(v time.Time) predicate.ListingMedia { + return predicate.ListingMedia(sql.FieldGTE(FieldCreatedAt, v)) +} + +// CreatedAtLT applies the LT predicate on the "created_at" field. +func CreatedAtLT(v time.Time) predicate.ListingMedia { + return predicate.ListingMedia(sql.FieldLT(FieldCreatedAt, v)) +} + +// CreatedAtLTE applies the LTE predicate on the "created_at" field. +func CreatedAtLTE(v time.Time) predicate.ListingMedia { + return predicate.ListingMedia(sql.FieldLTE(FieldCreatedAt, v)) +} + +// UpdatedAtEQ applies the EQ predicate on the "updated_at" field. +func UpdatedAtEQ(v time.Time) predicate.ListingMedia { + return predicate.ListingMedia(sql.FieldEQ(FieldUpdatedAt, v)) +} + +// UpdatedAtNEQ applies the NEQ predicate on the "updated_at" field. +func UpdatedAtNEQ(v time.Time) predicate.ListingMedia { + return predicate.ListingMedia(sql.FieldNEQ(FieldUpdatedAt, v)) +} + +// UpdatedAtIn applies the In predicate on the "updated_at" field. +func UpdatedAtIn(vs ...time.Time) predicate.ListingMedia { + return predicate.ListingMedia(sql.FieldIn(FieldUpdatedAt, vs...)) +} + +// UpdatedAtNotIn applies the NotIn predicate on the "updated_at" field. +func UpdatedAtNotIn(vs ...time.Time) predicate.ListingMedia { + return predicate.ListingMedia(sql.FieldNotIn(FieldUpdatedAt, vs...)) +} + +// UpdatedAtGT applies the GT predicate on the "updated_at" field. +func UpdatedAtGT(v time.Time) predicate.ListingMedia { + return predicate.ListingMedia(sql.FieldGT(FieldUpdatedAt, v)) +} + +// UpdatedAtGTE applies the GTE predicate on the "updated_at" field. +func UpdatedAtGTE(v time.Time) predicate.ListingMedia { + return predicate.ListingMedia(sql.FieldGTE(FieldUpdatedAt, v)) +} + +// UpdatedAtLT applies the LT predicate on the "updated_at" field. +func UpdatedAtLT(v time.Time) predicate.ListingMedia { + return predicate.ListingMedia(sql.FieldLT(FieldUpdatedAt, v)) +} + +// UpdatedAtLTE applies the LTE predicate on the "updated_at" field. +func UpdatedAtLTE(v time.Time) predicate.ListingMedia { + return predicate.ListingMedia(sql.FieldLTE(FieldUpdatedAt, v)) +} + +// And groups predicates with the AND operator between them. +func And(predicates ...predicate.ListingMedia) predicate.ListingMedia { + return predicate.ListingMedia(sql.AndPredicates(predicates...)) +} + +// Or groups predicates with the OR operator between them. +func Or(predicates ...predicate.ListingMedia) predicate.ListingMedia { + return predicate.ListingMedia(sql.OrPredicates(predicates...)) +} + +// Not applies the not operator on the given predicate. +func Not(p predicate.ListingMedia) predicate.ListingMedia { + return predicate.ListingMedia(sql.NotPredicates(p)) +} diff --git a/backend/ent/listingmedia_create.go b/backend/ent/listingmedia_create.go new file mode 100644 index 0000000..79713a5 --- /dev/null +++ b/backend/ent/listingmedia_create.go @@ -0,0 +1,384 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "context" + "errors" + "fmt" + "time" + + "entgo.io/ent/dialect/sql/sqlgraph" + "entgo.io/ent/schema/field" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/listingmedia" + "github.com/google/uuid" +) + +// ListingMediaCreate is the builder for creating a ListingMedia entity. +type ListingMediaCreate struct { + config + mutation *ListingMediaMutation + hooks []Hook +} + +// SetListingID sets the "listing_id" field. +func (_c *ListingMediaCreate) SetListingID(v uuid.UUID) *ListingMediaCreate { + _c.mutation.SetListingID(v) + return _c +} + +// SetOrdinal sets the "ordinal" field. +func (_c *ListingMediaCreate) SetOrdinal(v int) *ListingMediaCreate { + _c.mutation.SetOrdinal(v) + return _c +} + +// SetContentType sets the "content_type" field. +func (_c *ListingMediaCreate) SetContentType(v string) *ListingMediaCreate { + _c.mutation.SetContentType(v) + return _c +} + +// SetByteSize sets the "byte_size" field. +func (_c *ListingMediaCreate) SetByteSize(v int64) *ListingMediaCreate { + _c.mutation.SetByteSize(v) + return _c +} + +// SetChecksumSha256 sets the "checksum_sha256" field. +func (_c *ListingMediaCreate) SetChecksumSha256(v string) *ListingMediaCreate { + _c.mutation.SetChecksumSha256(v) + return _c +} + +// SetObjectReference sets the "object_reference" field. +func (_c *ListingMediaCreate) SetObjectReference(v string) *ListingMediaCreate { + _c.mutation.SetObjectReference(v) + return _c +} + +// SetState sets the "state" field. +func (_c *ListingMediaCreate) SetState(v listingmedia.State) *ListingMediaCreate { + _c.mutation.SetState(v) + return _c +} + +// SetNillableState sets the "state" field if the given value is not nil. +func (_c *ListingMediaCreate) SetNillableState(v *listingmedia.State) *ListingMediaCreate { + if v != nil { + _c.SetState(*v) + } + return _c +} + +// SetCreatedAt sets the "created_at" field. +func (_c *ListingMediaCreate) SetCreatedAt(v time.Time) *ListingMediaCreate { + _c.mutation.SetCreatedAt(v) + return _c +} + +// SetNillableCreatedAt sets the "created_at" field if the given value is not nil. +func (_c *ListingMediaCreate) SetNillableCreatedAt(v *time.Time) *ListingMediaCreate { + if v != nil { + _c.SetCreatedAt(*v) + } + return _c +} + +// SetUpdatedAt sets the "updated_at" field. +func (_c *ListingMediaCreate) SetUpdatedAt(v time.Time) *ListingMediaCreate { + _c.mutation.SetUpdatedAt(v) + return _c +} + +// SetNillableUpdatedAt sets the "updated_at" field if the given value is not nil. +func (_c *ListingMediaCreate) SetNillableUpdatedAt(v *time.Time) *ListingMediaCreate { + if v != nil { + _c.SetUpdatedAt(*v) + } + return _c +} + +// SetID sets the "id" field. +func (_c *ListingMediaCreate) SetID(v uuid.UUID) *ListingMediaCreate { + _c.mutation.SetID(v) + return _c +} + +// SetNillableID sets the "id" field if the given value is not nil. +func (_c *ListingMediaCreate) SetNillableID(v *uuid.UUID) *ListingMediaCreate { + if v != nil { + _c.SetID(*v) + } + return _c +} + +// Mutation returns the ListingMediaMutation object of the builder. +func (_c *ListingMediaCreate) Mutation() *ListingMediaMutation { + return _c.mutation +} + +// Save creates the ListingMedia in the database. +func (_c *ListingMediaCreate) Save(ctx context.Context) (*ListingMedia, error) { + _c.defaults() + return withHooks(ctx, _c.sqlSave, _c.mutation, _c.hooks) +} + +// SaveX calls Save and panics if Save returns an error. +func (_c *ListingMediaCreate) SaveX(ctx context.Context) *ListingMedia { + v, err := _c.Save(ctx) + if err != nil { + panic(err) + } + return v +} + +// Exec executes the query. +func (_c *ListingMediaCreate) Exec(ctx context.Context) error { + _, err := _c.Save(ctx) + return err +} + +// ExecX is like Exec, but panics if an error occurs. +func (_c *ListingMediaCreate) 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 *ListingMediaCreate) defaults() { + if _, ok := _c.mutation.State(); !ok { + v := listingmedia.DefaultState + _c.mutation.SetState(v) + } + if _, ok := _c.mutation.CreatedAt(); !ok { + v := listingmedia.DefaultCreatedAt() + _c.mutation.SetCreatedAt(v) + } + if _, ok := _c.mutation.UpdatedAt(); !ok { + v := listingmedia.DefaultUpdatedAt() + _c.mutation.SetUpdatedAt(v) + } + if _, ok := _c.mutation.ID(); !ok { + v := listingmedia.DefaultID() + _c.mutation.SetID(v) + } +} + +// check runs all checks and user-defined validators on the builder. +func (_c *ListingMediaCreate) check() error { + if _, ok := _c.mutation.ListingID(); !ok { + return &ValidationError{Name: "listing_id", err: errors.New(`ent: missing required field "ListingMedia.listing_id"`)} + } + if _, ok := _c.mutation.Ordinal(); !ok { + return &ValidationError{Name: "ordinal", err: errors.New(`ent: missing required field "ListingMedia.ordinal"`)} + } + if v, ok := _c.mutation.Ordinal(); ok { + if err := listingmedia.OrdinalValidator(v); err != nil { + return &ValidationError{Name: "ordinal", err: fmt.Errorf(`ent: validator failed for field "ListingMedia.ordinal": %w`, err)} + } + } + if _, ok := _c.mutation.ContentType(); !ok { + return &ValidationError{Name: "content_type", err: errors.New(`ent: missing required field "ListingMedia.content_type"`)} + } + if v, ok := _c.mutation.ContentType(); ok { + if err := listingmedia.ContentTypeValidator(v); err != nil { + return &ValidationError{Name: "content_type", err: fmt.Errorf(`ent: validator failed for field "ListingMedia.content_type": %w`, err)} + } + } + if _, ok := _c.mutation.ByteSize(); !ok { + return &ValidationError{Name: "byte_size", err: errors.New(`ent: missing required field "ListingMedia.byte_size"`)} + } + if v, ok := _c.mutation.ByteSize(); ok { + if err := listingmedia.ByteSizeValidator(v); err != nil { + return &ValidationError{Name: "byte_size", err: fmt.Errorf(`ent: validator failed for field "ListingMedia.byte_size": %w`, err)} + } + } + if _, ok := _c.mutation.ChecksumSha256(); !ok { + return &ValidationError{Name: "checksum_sha256", err: errors.New(`ent: missing required field "ListingMedia.checksum_sha256"`)} + } + if v, ok := _c.mutation.ChecksumSha256(); ok { + if err := listingmedia.ChecksumSha256Validator(v); err != nil { + return &ValidationError{Name: "checksum_sha256", err: fmt.Errorf(`ent: validator failed for field "ListingMedia.checksum_sha256": %w`, err)} + } + } + if _, ok := _c.mutation.ObjectReference(); !ok { + return &ValidationError{Name: "object_reference", err: errors.New(`ent: missing required field "ListingMedia.object_reference"`)} + } + if v, ok := _c.mutation.ObjectReference(); ok { + if err := listingmedia.ObjectReferenceValidator(v); err != nil { + return &ValidationError{Name: "object_reference", err: fmt.Errorf(`ent: validator failed for field "ListingMedia.object_reference": %w`, err)} + } + } + if _, ok := _c.mutation.State(); !ok { + return &ValidationError{Name: "state", err: errors.New(`ent: missing required field "ListingMedia.state"`)} + } + if v, ok := _c.mutation.State(); ok { + if err := listingmedia.StateValidator(v); err != nil { + return &ValidationError{Name: "state", err: fmt.Errorf(`ent: validator failed for field "ListingMedia.state": %w`, err)} + } + } + if _, ok := _c.mutation.CreatedAt(); !ok { + return &ValidationError{Name: "created_at", err: errors.New(`ent: missing required field "ListingMedia.created_at"`)} + } + if _, ok := _c.mutation.UpdatedAt(); !ok { + return &ValidationError{Name: "updated_at", err: errors.New(`ent: missing required field "ListingMedia.updated_at"`)} + } + return nil +} + +func (_c *ListingMediaCreate) sqlSave(ctx context.Context) (*ListingMedia, 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 *ListingMediaCreate) createSpec() (*ListingMedia, *sqlgraph.CreateSpec) { + var ( + _node = &ListingMedia{config: _c.config} + _spec = sqlgraph.NewCreateSpec(listingmedia.Table, sqlgraph.NewFieldSpec(listingmedia.FieldID, field.TypeUUID)) + ) + if id, ok := _c.mutation.ID(); ok { + _node.ID = id + _spec.ID.Value = &id + } + if value, ok := _c.mutation.ListingID(); ok { + _spec.SetField(listingmedia.FieldListingID, field.TypeUUID, value) + _node.ListingID = value + } + if value, ok := _c.mutation.Ordinal(); ok { + _spec.SetField(listingmedia.FieldOrdinal, field.TypeInt, value) + _node.Ordinal = value + } + if value, ok := _c.mutation.ContentType(); ok { + _spec.SetField(listingmedia.FieldContentType, field.TypeString, value) + _node.ContentType = value + } + if value, ok := _c.mutation.ByteSize(); ok { + _spec.SetField(listingmedia.FieldByteSize, field.TypeInt64, value) + _node.ByteSize = value + } + if value, ok := _c.mutation.ChecksumSha256(); ok { + _spec.SetField(listingmedia.FieldChecksumSha256, field.TypeString, value) + _node.ChecksumSha256 = value + } + if value, ok := _c.mutation.ObjectReference(); ok { + _spec.SetField(listingmedia.FieldObjectReference, field.TypeString, value) + _node.ObjectReference = value + } + if value, ok := _c.mutation.State(); ok { + _spec.SetField(listingmedia.FieldState, field.TypeEnum, value) + _node.State = value + } + if value, ok := _c.mutation.CreatedAt(); ok { + _spec.SetField(listingmedia.FieldCreatedAt, field.TypeTime, value) + _node.CreatedAt = value + } + if value, ok := _c.mutation.UpdatedAt(); ok { + _spec.SetField(listingmedia.FieldUpdatedAt, field.TypeTime, value) + _node.UpdatedAt = value + } + return _node, _spec +} + +// ListingMediaCreateBulk is the builder for creating many ListingMedia entities in bulk. +type ListingMediaCreateBulk struct { + config + err error + builders []*ListingMediaCreate +} + +// Save creates the ListingMedia entities in the database. +func (_c *ListingMediaCreateBulk) Save(ctx context.Context) ([]*ListingMedia, error) { + if _c.err != nil { + return nil, _c.err + } + specs := make([]*sqlgraph.CreateSpec, len(_c.builders)) + nodes := make([]*ListingMedia, 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.(*ListingMediaMutation) + 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 *ListingMediaCreateBulk) SaveX(ctx context.Context) []*ListingMedia { + v, err := _c.Save(ctx) + if err != nil { + panic(err) + } + return v +} + +// Exec executes the query. +func (_c *ListingMediaCreateBulk) Exec(ctx context.Context) error { + _, err := _c.Save(ctx) + return err +} + +// ExecX is like Exec, but panics if an error occurs. +func (_c *ListingMediaCreateBulk) ExecX(ctx context.Context) { + if err := _c.Exec(ctx); err != nil { + panic(err) + } +} diff --git a/backend/ent/listingmedia_delete.go b/backend/ent/listingmedia_delete.go new file mode 100644 index 0000000..be24b3f --- /dev/null +++ b/backend/ent/listingmedia_delete.go @@ -0,0 +1,88 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "context" + + "entgo.io/ent/dialect/sql" + "entgo.io/ent/dialect/sql/sqlgraph" + "entgo.io/ent/schema/field" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/listingmedia" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/predicate" +) + +// ListingMediaDelete is the builder for deleting a ListingMedia entity. +type ListingMediaDelete struct { + config + hooks []Hook + mutation *ListingMediaMutation +} + +// Where appends a list predicates to the ListingMediaDelete builder. +func (_d *ListingMediaDelete) Where(ps ...predicate.ListingMedia) *ListingMediaDelete { + _d.mutation.Where(ps...) + return _d +} + +// Exec executes the deletion query and returns how many vertices were deleted. +func (_d *ListingMediaDelete) 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 *ListingMediaDelete) ExecX(ctx context.Context) int { + n, err := _d.Exec(ctx) + if err != nil { + panic(err) + } + return n +} + +func (_d *ListingMediaDelete) sqlExec(ctx context.Context) (int, error) { + _spec := sqlgraph.NewDeleteSpec(listingmedia.Table, sqlgraph.NewFieldSpec(listingmedia.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 +} + +// ListingMediaDeleteOne is the builder for deleting a single ListingMedia entity. +type ListingMediaDeleteOne struct { + _d *ListingMediaDelete +} + +// Where appends a list predicates to the ListingMediaDelete builder. +func (_d *ListingMediaDeleteOne) Where(ps ...predicate.ListingMedia) *ListingMediaDeleteOne { + _d._d.mutation.Where(ps...) + return _d +} + +// Exec executes the deletion query. +func (_d *ListingMediaDeleteOne) Exec(ctx context.Context) error { + n, err := _d._d.Exec(ctx) + switch { + case err != nil: + return err + case n == 0: + return &NotFoundError{listingmedia.Label} + default: + return nil + } +} + +// ExecX is like Exec, but panics if an error occurs. +func (_d *ListingMediaDeleteOne) ExecX(ctx context.Context) { + if err := _d.Exec(ctx); err != nil { + panic(err) + } +} diff --git a/backend/ent/listingmedia_query.go b/backend/ent/listingmedia_query.go new file mode 100644 index 0000000..8eca17b --- /dev/null +++ b/backend/ent/listingmedia_query.go @@ -0,0 +1,528 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "context" + "fmt" + "math" + + "entgo.io/ent" + "entgo.io/ent/dialect/sql" + "entgo.io/ent/dialect/sql/sqlgraph" + "entgo.io/ent/schema/field" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/listingmedia" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/predicate" + "github.com/google/uuid" +) + +// ListingMediaQuery is the builder for querying ListingMedia entities. +type ListingMediaQuery struct { + config + ctx *QueryContext + order []listingmedia.OrderOption + inters []Interceptor + predicates []predicate.ListingMedia + // intermediate query (i.e. traversal path). + sql *sql.Selector + path func(context.Context) (*sql.Selector, error) +} + +// Where adds a new predicate for the ListingMediaQuery builder. +func (_q *ListingMediaQuery) Where(ps ...predicate.ListingMedia) *ListingMediaQuery { + _q.predicates = append(_q.predicates, ps...) + return _q +} + +// Limit the number of records to be returned by this query. +func (_q *ListingMediaQuery) Limit(limit int) *ListingMediaQuery { + _q.ctx.Limit = &limit + return _q +} + +// Offset to start from. +func (_q *ListingMediaQuery) Offset(offset int) *ListingMediaQuery { + _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 *ListingMediaQuery) Unique(unique bool) *ListingMediaQuery { + _q.ctx.Unique = &unique + return _q +} + +// Order specifies how the records should be ordered. +func (_q *ListingMediaQuery) Order(o ...listingmedia.OrderOption) *ListingMediaQuery { + _q.order = append(_q.order, o...) + return _q +} + +// First returns the first ListingMedia entity from the query. +// Returns a *NotFoundError when no ListingMedia was found. +func (_q *ListingMediaQuery) First(ctx context.Context) (*ListingMedia, 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{listingmedia.Label} + } + return nodes[0], nil +} + +// FirstX is like First, but panics if an error occurs. +func (_q *ListingMediaQuery) FirstX(ctx context.Context) *ListingMedia { + node, err := _q.First(ctx) + if err != nil && !IsNotFound(err) { + panic(err) + } + return node +} + +// FirstID returns the first ListingMedia ID from the query. +// Returns a *NotFoundError when no ListingMedia ID was found. +func (_q *ListingMediaQuery) 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{listingmedia.Label} + return + } + return ids[0], nil +} + +// FirstIDX is like FirstID, but panics if an error occurs. +func (_q *ListingMediaQuery) FirstIDX(ctx context.Context) uuid.UUID { + id, err := _q.FirstID(ctx) + if err != nil && !IsNotFound(err) { + panic(err) + } + return id +} + +// Only returns a single ListingMedia entity found by the query, ensuring it only returns one. +// Returns a *NotSingularError when more than one ListingMedia entity is found. +// Returns a *NotFoundError when no ListingMedia entities are found. +func (_q *ListingMediaQuery) Only(ctx context.Context) (*ListingMedia, 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{listingmedia.Label} + default: + return nil, &NotSingularError{listingmedia.Label} + } +} + +// OnlyX is like Only, but panics if an error occurs. +func (_q *ListingMediaQuery) OnlyX(ctx context.Context) *ListingMedia { + node, err := _q.Only(ctx) + if err != nil { + panic(err) + } + return node +} + +// OnlyID is like Only, but returns the only ListingMedia ID in the query. +// Returns a *NotSingularError when more than one ListingMedia ID is found. +// Returns a *NotFoundError when no entities are found. +func (_q *ListingMediaQuery) 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{listingmedia.Label} + default: + err = &NotSingularError{listingmedia.Label} + } + return +} + +// OnlyIDX is like OnlyID, but panics if an error occurs. +func (_q *ListingMediaQuery) 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 ListingMediaSlice. +func (_q *ListingMediaQuery) All(ctx context.Context) ([]*ListingMedia, error) { + ctx = setContextOp(ctx, _q.ctx, ent.OpQueryAll) + if err := _q.prepareQuery(ctx); err != nil { + return nil, err + } + qr := querierAll[[]*ListingMedia, *ListingMediaQuery]() + return withInterceptors[[]*ListingMedia](ctx, _q, qr, _q.inters) +} + +// AllX is like All, but panics if an error occurs. +func (_q *ListingMediaQuery) AllX(ctx context.Context) []*ListingMedia { + nodes, err := _q.All(ctx) + if err != nil { + panic(err) + } + return nodes +} + +// IDs executes the query and returns a list of ListingMedia IDs. +func (_q *ListingMediaQuery) 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(listingmedia.FieldID).Scan(ctx, &ids); err != nil { + return nil, err + } + return ids, nil +} + +// IDsX is like IDs, but panics if an error occurs. +func (_q *ListingMediaQuery) 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 *ListingMediaQuery) 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[*ListingMediaQuery](), _q.inters) +} + +// CountX is like Count, but panics if an error occurs. +func (_q *ListingMediaQuery) 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 *ListingMediaQuery) 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 *ListingMediaQuery) ExistX(ctx context.Context) bool { + exist, err := _q.Exist(ctx) + if err != nil { + panic(err) + } + return exist +} + +// Clone returns a duplicate of the ListingMediaQuery 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 *ListingMediaQuery) Clone() *ListingMediaQuery { + if _q == nil { + return nil + } + return &ListingMediaQuery{ + config: _q.config, + ctx: _q.ctx.Clone(), + order: append([]listingmedia.OrderOption{}, _q.order...), + inters: append([]Interceptor{}, _q.inters...), + predicates: append([]predicate.ListingMedia{}, _q.predicates...), + // clone intermediate query. + sql: _q.sql.Clone(), + path: _q.path, + } +} + +// GroupBy is used to group vertices by one or more fields/columns. +// It is often used with aggregate functions, like: count, max, mean, min, sum. +// +// Example: +// +// var v []struct { +// ListingID uuid.UUID `json:"listing_id,omitempty"` +// Count int `json:"count,omitempty"` +// } +// +// client.ListingMedia.Query(). +// GroupBy(listingmedia.FieldListingID). +// Aggregate(ent.Count()). +// Scan(ctx, &v) +func (_q *ListingMediaQuery) GroupBy(field string, fields ...string) *ListingMediaGroupBy { + _q.ctx.Fields = append([]string{field}, fields...) + grbuild := &ListingMediaGroupBy{build: _q} + grbuild.flds = &_q.ctx.Fields + grbuild.label = listingmedia.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 { +// ListingID uuid.UUID `json:"listing_id,omitempty"` +// } +// +// client.ListingMedia.Query(). +// Select(listingmedia.FieldListingID). +// Scan(ctx, &v) +func (_q *ListingMediaQuery) Select(fields ...string) *ListingMediaSelect { + _q.ctx.Fields = append(_q.ctx.Fields, fields...) + sbuild := &ListingMediaSelect{ListingMediaQuery: _q} + sbuild.label = listingmedia.Label + sbuild.flds, sbuild.scan = &_q.ctx.Fields, sbuild.Scan + return sbuild +} + +// Aggregate returns a ListingMediaSelect configured with the given aggregations. +func (_q *ListingMediaQuery) Aggregate(fns ...AggregateFunc) *ListingMediaSelect { + return _q.Select().Aggregate(fns...) +} + +func (_q *ListingMediaQuery) 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 !listingmedia.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 *ListingMediaQuery) sqlAll(ctx context.Context, hooks ...queryHook) ([]*ListingMedia, error) { + var ( + nodes = []*ListingMedia{} + _spec = _q.querySpec() + ) + _spec.ScanValues = func(columns []string) ([]any, error) { + return (*ListingMedia).scanValues(nil, columns) + } + _spec.Assign = func(columns []string, values []any) error { + node := &ListingMedia{config: _q.config} + nodes = append(nodes, node) + return node.assignValues(columns, values) + } + for i := range hooks { + hooks[i](ctx, _spec) + } + if err := sqlgraph.QueryNodes(ctx, _q.driver, _spec); err != nil { + return nil, err + } + if len(nodes) == 0 { + return nodes, nil + } + return nodes, nil +} + +func (_q *ListingMediaQuery) sqlCount(ctx context.Context) (int, error) { + _spec := _q.querySpec() + _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 *ListingMediaQuery) querySpec() *sqlgraph.QuerySpec { + _spec := sqlgraph.NewQuerySpec(listingmedia.Table, listingmedia.Columns, sqlgraph.NewFieldSpec(listingmedia.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, listingmedia.FieldID) + for i := range fields { + if fields[i] != listingmedia.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 *ListingMediaQuery) sqlQuery(ctx context.Context) *sql.Selector { + builder := sql.Dialect(_q.driver.Dialect()) + t1 := builder.Table(listingmedia.Table) + columns := _q.ctx.Fields + if len(columns) == 0 { + columns = listingmedia.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 _, 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 +} + +// ListingMediaGroupBy is the group-by builder for ListingMedia entities. +type ListingMediaGroupBy struct { + selector + build *ListingMediaQuery +} + +// Aggregate adds the given aggregation functions to the group-by query. +func (_g *ListingMediaGroupBy) Aggregate(fns ...AggregateFunc) *ListingMediaGroupBy { + _g.fns = append(_g.fns, fns...) + return _g +} + +// Scan applies the selector query and scans the result into the given value. +func (_g *ListingMediaGroupBy) 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[*ListingMediaQuery, *ListingMediaGroupBy](ctx, _g.build, _g, _g.build.inters, v) +} + +func (_g *ListingMediaGroupBy) sqlScan(ctx context.Context, root *ListingMediaQuery, 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) +} + +// ListingMediaSelect is the builder for selecting fields of ListingMedia entities. +type ListingMediaSelect struct { + *ListingMediaQuery + selector +} + +// Aggregate adds the given aggregation functions to the selector query. +func (_s *ListingMediaSelect) Aggregate(fns ...AggregateFunc) *ListingMediaSelect { + _s.fns = append(_s.fns, fns...) + return _s +} + +// Scan applies the selector query and scans the result into the given value. +func (_s *ListingMediaSelect) 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[*ListingMediaQuery, *ListingMediaSelect](ctx, _s.ListingMediaQuery, _s, _s.inters, v) +} + +func (_s *ListingMediaSelect) sqlScan(ctx context.Context, root *ListingMediaQuery, 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/backend/ent/listingmedia_update.go b/backend/ent/listingmedia_update.go new file mode 100644 index 0000000..8f404d9 --- /dev/null +++ b/backend/ent/listingmedia_update.go @@ -0,0 +1,444 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "context" + "errors" + "fmt" + "time" + + "entgo.io/ent/dialect/sql" + "entgo.io/ent/dialect/sql/sqlgraph" + "entgo.io/ent/schema/field" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/listingmedia" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/predicate" +) + +// ListingMediaUpdate is the builder for updating ListingMedia entities. +type ListingMediaUpdate struct { + config + hooks []Hook + mutation *ListingMediaMutation +} + +// Where appends a list predicates to the ListingMediaUpdate builder. +func (_u *ListingMediaUpdate) Where(ps ...predicate.ListingMedia) *ListingMediaUpdate { + _u.mutation.Where(ps...) + return _u +} + +// SetOrdinal sets the "ordinal" field. +func (_u *ListingMediaUpdate) SetOrdinal(v int) *ListingMediaUpdate { + _u.mutation.ResetOrdinal() + _u.mutation.SetOrdinal(v) + return _u +} + +// SetNillableOrdinal sets the "ordinal" field if the given value is not nil. +func (_u *ListingMediaUpdate) SetNillableOrdinal(v *int) *ListingMediaUpdate { + if v != nil { + _u.SetOrdinal(*v) + } + return _u +} + +// AddOrdinal adds value to the "ordinal" field. +func (_u *ListingMediaUpdate) AddOrdinal(v int) *ListingMediaUpdate { + _u.mutation.AddOrdinal(v) + return _u +} + +// SetContentType sets the "content_type" field. +func (_u *ListingMediaUpdate) SetContentType(v string) *ListingMediaUpdate { + _u.mutation.SetContentType(v) + return _u +} + +// SetNillableContentType sets the "content_type" field if the given value is not nil. +func (_u *ListingMediaUpdate) SetNillableContentType(v *string) *ListingMediaUpdate { + if v != nil { + _u.SetContentType(*v) + } + return _u +} + +// SetByteSize sets the "byte_size" field. +func (_u *ListingMediaUpdate) SetByteSize(v int64) *ListingMediaUpdate { + _u.mutation.ResetByteSize() + _u.mutation.SetByteSize(v) + return _u +} + +// SetNillableByteSize sets the "byte_size" field if the given value is not nil. +func (_u *ListingMediaUpdate) SetNillableByteSize(v *int64) *ListingMediaUpdate { + if v != nil { + _u.SetByteSize(*v) + } + return _u +} + +// AddByteSize adds value to the "byte_size" field. +func (_u *ListingMediaUpdate) AddByteSize(v int64) *ListingMediaUpdate { + _u.mutation.AddByteSize(v) + return _u +} + +// SetState sets the "state" field. +func (_u *ListingMediaUpdate) SetState(v listingmedia.State) *ListingMediaUpdate { + _u.mutation.SetState(v) + return _u +} + +// SetNillableState sets the "state" field if the given value is not nil. +func (_u *ListingMediaUpdate) SetNillableState(v *listingmedia.State) *ListingMediaUpdate { + if v != nil { + _u.SetState(*v) + } + return _u +} + +// SetUpdatedAt sets the "updated_at" field. +func (_u *ListingMediaUpdate) SetUpdatedAt(v time.Time) *ListingMediaUpdate { + _u.mutation.SetUpdatedAt(v) + return _u +} + +// Mutation returns the ListingMediaMutation object of the builder. +func (_u *ListingMediaUpdate) Mutation() *ListingMediaMutation { + return _u.mutation +} + +// Save executes the query and returns the number of nodes affected by the update operation. +func (_u *ListingMediaUpdate) Save(ctx context.Context) (int, error) { + _u.defaults() + return withHooks(ctx, _u.sqlSave, _u.mutation, _u.hooks) +} + +// SaveX is like Save, but panics if an error occurs. +func (_u *ListingMediaUpdate) SaveX(ctx context.Context) int { + affected, err := _u.Save(ctx) + if err != nil { + panic(err) + } + return affected +} + +// Exec executes the query. +func (_u *ListingMediaUpdate) Exec(ctx context.Context) error { + _, err := _u.Save(ctx) + return err +} + +// ExecX is like Exec, but panics if an error occurs. +func (_u *ListingMediaUpdate) ExecX(ctx context.Context) { + if err := _u.Exec(ctx); err != nil { + panic(err) + } +} + +// defaults sets the default values of the builder before save. +func (_u *ListingMediaUpdate) defaults() { + if _, ok := _u.mutation.UpdatedAt(); !ok { + v := listingmedia.UpdateDefaultUpdatedAt() + _u.mutation.SetUpdatedAt(v) + } +} + +// check runs all checks and user-defined validators on the builder. +func (_u *ListingMediaUpdate) check() error { + if v, ok := _u.mutation.Ordinal(); ok { + if err := listingmedia.OrdinalValidator(v); err != nil { + return &ValidationError{Name: "ordinal", err: fmt.Errorf(`ent: validator failed for field "ListingMedia.ordinal": %w`, err)} + } + } + if v, ok := _u.mutation.ContentType(); ok { + if err := listingmedia.ContentTypeValidator(v); err != nil { + return &ValidationError{Name: "content_type", err: fmt.Errorf(`ent: validator failed for field "ListingMedia.content_type": %w`, err)} + } + } + if v, ok := _u.mutation.ByteSize(); ok { + if err := listingmedia.ByteSizeValidator(v); err != nil { + return &ValidationError{Name: "byte_size", err: fmt.Errorf(`ent: validator failed for field "ListingMedia.byte_size": %w`, err)} + } + } + if v, ok := _u.mutation.State(); ok { + if err := listingmedia.StateValidator(v); err != nil { + return &ValidationError{Name: "state", err: fmt.Errorf(`ent: validator failed for field "ListingMedia.state": %w`, err)} + } + } + return nil +} + +func (_u *ListingMediaUpdate) sqlSave(ctx context.Context) (_node int, err error) { + if err := _u.check(); err != nil { + return _node, err + } + _spec := sqlgraph.NewUpdateSpec(listingmedia.Table, listingmedia.Columns, sqlgraph.NewFieldSpec(listingmedia.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.Ordinal(); ok { + _spec.SetField(listingmedia.FieldOrdinal, field.TypeInt, value) + } + if value, ok := _u.mutation.AddedOrdinal(); ok { + _spec.AddField(listingmedia.FieldOrdinal, field.TypeInt, value) + } + if value, ok := _u.mutation.ContentType(); ok { + _spec.SetField(listingmedia.FieldContentType, field.TypeString, value) + } + if value, ok := _u.mutation.ByteSize(); ok { + _spec.SetField(listingmedia.FieldByteSize, field.TypeInt64, value) + } + if value, ok := _u.mutation.AddedByteSize(); ok { + _spec.AddField(listingmedia.FieldByteSize, field.TypeInt64, value) + } + if value, ok := _u.mutation.State(); ok { + _spec.SetField(listingmedia.FieldState, field.TypeEnum, value) + } + if value, ok := _u.mutation.UpdatedAt(); ok { + _spec.SetField(listingmedia.FieldUpdatedAt, field.TypeTime, value) + } + if _node, err = sqlgraph.UpdateNodes(ctx, _u.driver, _spec); err != nil { + if _, ok := err.(*sqlgraph.NotFoundError); ok { + err = &NotFoundError{listingmedia.Label} + } else if sqlgraph.IsConstraintError(err) { + err = &ConstraintError{msg: err.Error(), wrap: err} + } + return 0, err + } + _u.mutation.done = true + return _node, nil +} + +// ListingMediaUpdateOne is the builder for updating a single ListingMedia entity. +type ListingMediaUpdateOne struct { + config + fields []string + hooks []Hook + mutation *ListingMediaMutation +} + +// SetOrdinal sets the "ordinal" field. +func (_u *ListingMediaUpdateOne) SetOrdinal(v int) *ListingMediaUpdateOne { + _u.mutation.ResetOrdinal() + _u.mutation.SetOrdinal(v) + return _u +} + +// SetNillableOrdinal sets the "ordinal" field if the given value is not nil. +func (_u *ListingMediaUpdateOne) SetNillableOrdinal(v *int) *ListingMediaUpdateOne { + if v != nil { + _u.SetOrdinal(*v) + } + return _u +} + +// AddOrdinal adds value to the "ordinal" field. +func (_u *ListingMediaUpdateOne) AddOrdinal(v int) *ListingMediaUpdateOne { + _u.mutation.AddOrdinal(v) + return _u +} + +// SetContentType sets the "content_type" field. +func (_u *ListingMediaUpdateOne) SetContentType(v string) *ListingMediaUpdateOne { + _u.mutation.SetContentType(v) + return _u +} + +// SetNillableContentType sets the "content_type" field if the given value is not nil. +func (_u *ListingMediaUpdateOne) SetNillableContentType(v *string) *ListingMediaUpdateOne { + if v != nil { + _u.SetContentType(*v) + } + return _u +} + +// SetByteSize sets the "byte_size" field. +func (_u *ListingMediaUpdateOne) SetByteSize(v int64) *ListingMediaUpdateOne { + _u.mutation.ResetByteSize() + _u.mutation.SetByteSize(v) + return _u +} + +// SetNillableByteSize sets the "byte_size" field if the given value is not nil. +func (_u *ListingMediaUpdateOne) SetNillableByteSize(v *int64) *ListingMediaUpdateOne { + if v != nil { + _u.SetByteSize(*v) + } + return _u +} + +// AddByteSize adds value to the "byte_size" field. +func (_u *ListingMediaUpdateOne) AddByteSize(v int64) *ListingMediaUpdateOne { + _u.mutation.AddByteSize(v) + return _u +} + +// SetState sets the "state" field. +func (_u *ListingMediaUpdateOne) SetState(v listingmedia.State) *ListingMediaUpdateOne { + _u.mutation.SetState(v) + return _u +} + +// SetNillableState sets the "state" field if the given value is not nil. +func (_u *ListingMediaUpdateOne) SetNillableState(v *listingmedia.State) *ListingMediaUpdateOne { + if v != nil { + _u.SetState(*v) + } + return _u +} + +// SetUpdatedAt sets the "updated_at" field. +func (_u *ListingMediaUpdateOne) SetUpdatedAt(v time.Time) *ListingMediaUpdateOne { + _u.mutation.SetUpdatedAt(v) + return _u +} + +// Mutation returns the ListingMediaMutation object of the builder. +func (_u *ListingMediaUpdateOne) Mutation() *ListingMediaMutation { + return _u.mutation +} + +// Where appends a list predicates to the ListingMediaUpdate builder. +func (_u *ListingMediaUpdateOne) Where(ps ...predicate.ListingMedia) *ListingMediaUpdateOne { + _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 *ListingMediaUpdateOne) Select(field string, fields ...string) *ListingMediaUpdateOne { + _u.fields = append([]string{field}, fields...) + return _u +} + +// Save executes the query and returns the updated ListingMedia entity. +func (_u *ListingMediaUpdateOne) Save(ctx context.Context) (*ListingMedia, error) { + _u.defaults() + return withHooks(ctx, _u.sqlSave, _u.mutation, _u.hooks) +} + +// SaveX is like Save, but panics if an error occurs. +func (_u *ListingMediaUpdateOne) SaveX(ctx context.Context) *ListingMedia { + node, err := _u.Save(ctx) + if err != nil { + panic(err) + } + return node +} + +// Exec executes the query on the entity. +func (_u *ListingMediaUpdateOne) Exec(ctx context.Context) error { + _, err := _u.Save(ctx) + return err +} + +// ExecX is like Exec, but panics if an error occurs. +func (_u *ListingMediaUpdateOne) ExecX(ctx context.Context) { + if err := _u.Exec(ctx); err != nil { + panic(err) + } +} + +// defaults sets the default values of the builder before save. +func (_u *ListingMediaUpdateOne) defaults() { + if _, ok := _u.mutation.UpdatedAt(); !ok { + v := listingmedia.UpdateDefaultUpdatedAt() + _u.mutation.SetUpdatedAt(v) + } +} + +// check runs all checks and user-defined validators on the builder. +func (_u *ListingMediaUpdateOne) check() error { + if v, ok := _u.mutation.Ordinal(); ok { + if err := listingmedia.OrdinalValidator(v); err != nil { + return &ValidationError{Name: "ordinal", err: fmt.Errorf(`ent: validator failed for field "ListingMedia.ordinal": %w`, err)} + } + } + if v, ok := _u.mutation.ContentType(); ok { + if err := listingmedia.ContentTypeValidator(v); err != nil { + return &ValidationError{Name: "content_type", err: fmt.Errorf(`ent: validator failed for field "ListingMedia.content_type": %w`, err)} + } + } + if v, ok := _u.mutation.ByteSize(); ok { + if err := listingmedia.ByteSizeValidator(v); err != nil { + return &ValidationError{Name: "byte_size", err: fmt.Errorf(`ent: validator failed for field "ListingMedia.byte_size": %w`, err)} + } + } + if v, ok := _u.mutation.State(); ok { + if err := listingmedia.StateValidator(v); err != nil { + return &ValidationError{Name: "state", err: fmt.Errorf(`ent: validator failed for field "ListingMedia.state": %w`, err)} + } + } + return nil +} + +func (_u *ListingMediaUpdateOne) sqlSave(ctx context.Context) (_node *ListingMedia, err error) { + if err := _u.check(); err != nil { + return _node, err + } + _spec := sqlgraph.NewUpdateSpec(listingmedia.Table, listingmedia.Columns, sqlgraph.NewFieldSpec(listingmedia.FieldID, field.TypeUUID)) + id, ok := _u.mutation.ID() + if !ok { + return nil, &ValidationError{Name: "id", err: errors.New(`ent: missing "ListingMedia.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, listingmedia.FieldID) + for _, f := range fields { + if !listingmedia.ValidColumn(f) { + return nil, &ValidationError{Name: f, err: fmt.Errorf("ent: invalid field %q for query", f)} + } + if f != listingmedia.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.Ordinal(); ok { + _spec.SetField(listingmedia.FieldOrdinal, field.TypeInt, value) + } + if value, ok := _u.mutation.AddedOrdinal(); ok { + _spec.AddField(listingmedia.FieldOrdinal, field.TypeInt, value) + } + if value, ok := _u.mutation.ContentType(); ok { + _spec.SetField(listingmedia.FieldContentType, field.TypeString, value) + } + if value, ok := _u.mutation.ByteSize(); ok { + _spec.SetField(listingmedia.FieldByteSize, field.TypeInt64, value) + } + if value, ok := _u.mutation.AddedByteSize(); ok { + _spec.AddField(listingmedia.FieldByteSize, field.TypeInt64, value) + } + if value, ok := _u.mutation.State(); ok { + _spec.SetField(listingmedia.FieldState, field.TypeEnum, value) + } + if value, ok := _u.mutation.UpdatedAt(); ok { + _spec.SetField(listingmedia.FieldUpdatedAt, field.TypeTime, value) + } + _node = &ListingMedia{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{listingmedia.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/backend/ent/locality.go b/backend/ent/locality.go new file mode 100644 index 0000000..7806339 --- /dev/null +++ b/backend/ent/locality.go @@ -0,0 +1,277 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "fmt" + "strings" + "time" + + "entgo.io/ent" + "entgo.io/ent/dialect/sql" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/administrativearea" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/locality" + "github.com/google/uuid" +) + +// Locality is the model entity for the Locality schema. +type Locality struct { + config `json:"-"` + // ID of the ent. + ID uuid.UUID `json:"id,omitempty"` + // Slug holds the value of the "slug" field. + Slug string `json:"slug,omitempty"` + // Name holds the value of the "name" field. + Name string `json:"name,omitempty"` + // ParentParishID holds the value of the "parent_parish_id" field. + ParentParishID uuid.UUID `json:"parent_parish_id,omitempty"` + // Source holds the value of the "source" field. + Source string `json:"source,omitempty"` + // SourceElementID holds the value of the "source_element_id" field. + SourceElementID string `json:"source_element_id,omitempty"` + // SourceVersion holds the value of the "source_version" field. + SourceVersion string `json:"source_version,omitempty"` + // SourceRetrievedAt holds the value of the "source_retrieved_at" field. + SourceRetrievedAt time.Time `json:"source_retrieved_at,omitempty"` + // Latitude holds the value of the "latitude" field. + Latitude float64 `json:"latitude,omitempty"` + // Longitude holds the value of the "longitude" field. + Longitude float64 `json:"longitude,omitempty"` + // Active holds the value of the "active" field. + Active bool `json:"active,omitempty"` + // CreatedAt holds the value of the "created_at" field. + CreatedAt time.Time `json:"created_at,omitempty"` + // UpdatedAt holds the value of the "updated_at" field. + UpdatedAt time.Time `json:"updated_at,omitempty"` + // Edges holds the relations/edges for other nodes in the graph. + // The values are being populated by the LocalityQuery when eager-loading is set. + Edges LocalityEdges `json:"edges"` + selectValues sql.SelectValues +} + +// LocalityEdges holds the relations/edges for other nodes in the graph. +type LocalityEdges struct { + // ParentParish holds the value of the parent_parish edge. + ParentParish *AdministrativeArea `json:"parent_parish,omitempty"` + // ProviderProfiles holds the value of the provider_profiles edge. + ProviderProfiles []*ProviderProfile `json:"provider_profiles,omitempty"` + // loadedTypes holds the information for reporting if a + // type was loaded (or requested) in eager-loading or not. + loadedTypes [2]bool +} + +// ParentParishOrErr returns the ParentParish value or an error if the edge +// was not loaded in eager-loading, or loaded but was not found. +func (e LocalityEdges) ParentParishOrErr() (*AdministrativeArea, error) { + if e.ParentParish != nil { + return e.ParentParish, nil + } else if e.loadedTypes[0] { + return nil, &NotFoundError{label: administrativearea.Label} + } + return nil, &NotLoadedError{edge: "parent_parish"} +} + +// ProviderProfilesOrErr returns the ProviderProfiles value or an error if the edge +// was not loaded in eager-loading. +func (e LocalityEdges) ProviderProfilesOrErr() ([]*ProviderProfile, error) { + if e.loadedTypes[1] { + return e.ProviderProfiles, nil + } + return nil, &NotLoadedError{edge: "provider_profiles"} +} + +// scanValues returns the types for scanning values from sql.Rows. +func (*Locality) scanValues(columns []string) ([]any, error) { + values := make([]any, len(columns)) + for i := range columns { + switch columns[i] { + case locality.FieldActive: + values[i] = new(sql.NullBool) + case locality.FieldLatitude, locality.FieldLongitude: + values[i] = new(sql.NullFloat64) + case locality.FieldSlug, locality.FieldName, locality.FieldSource, locality.FieldSourceElementID, locality.FieldSourceVersion: + values[i] = new(sql.NullString) + case locality.FieldSourceRetrievedAt, locality.FieldCreatedAt, locality.FieldUpdatedAt: + values[i] = new(sql.NullTime) + case locality.FieldID, locality.FieldParentParishID: + 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 Locality fields. +func (_m *Locality) 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 locality.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 locality.FieldSlug: + if value, ok := values[i].(*sql.NullString); !ok { + return fmt.Errorf("unexpected type %T for field slug", values[i]) + } else if value.Valid { + _m.Slug = value.String + } + case locality.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 locality.FieldParentParishID: + if value, ok := values[i].(*uuid.UUID); !ok { + return fmt.Errorf("unexpected type %T for field parent_parish_id", values[i]) + } else if value != nil { + _m.ParentParishID = *value + } + case locality.FieldSource: + if value, ok := values[i].(*sql.NullString); !ok { + return fmt.Errorf("unexpected type %T for field source", values[i]) + } else if value.Valid { + _m.Source = value.String + } + case locality.FieldSourceElementID: + if value, ok := values[i].(*sql.NullString); !ok { + return fmt.Errorf("unexpected type %T for field source_element_id", values[i]) + } else if value.Valid { + _m.SourceElementID = value.String + } + case locality.FieldSourceVersion: + if value, ok := values[i].(*sql.NullString); !ok { + return fmt.Errorf("unexpected type %T for field source_version", values[i]) + } else if value.Valid { + _m.SourceVersion = value.String + } + case locality.FieldSourceRetrievedAt: + if value, ok := values[i].(*sql.NullTime); !ok { + return fmt.Errorf("unexpected type %T for field source_retrieved_at", values[i]) + } else if value.Valid { + _m.SourceRetrievedAt = value.Time + } + case locality.FieldLatitude: + if value, ok := values[i].(*sql.NullFloat64); !ok { + return fmt.Errorf("unexpected type %T for field latitude", values[i]) + } else if value.Valid { + _m.Latitude = value.Float64 + } + case locality.FieldLongitude: + if value, ok := values[i].(*sql.NullFloat64); !ok { + return fmt.Errorf("unexpected type %T for field longitude", values[i]) + } else if value.Valid { + _m.Longitude = value.Float64 + } + case locality.FieldActive: + if value, ok := values[i].(*sql.NullBool); !ok { + return fmt.Errorf("unexpected type %T for field active", values[i]) + } else if value.Valid { + _m.Active = value.Bool + } + case locality.FieldCreatedAt: + if value, ok := values[i].(*sql.NullTime); !ok { + return fmt.Errorf("unexpected type %T for field created_at", values[i]) + } else if value.Valid { + _m.CreatedAt = value.Time + } + case locality.FieldUpdatedAt: + if value, ok := values[i].(*sql.NullTime); !ok { + return fmt.Errorf("unexpected type %T for field updated_at", values[i]) + } else if value.Valid { + _m.UpdatedAt = value.Time + } + default: + _m.selectValues.Set(columns[i], values[i]) + } + } + return nil +} + +// Value returns the ent.Value that was dynamically selected and assigned to the Locality. +// This includes values selected through modifiers, order, etc. +func (_m *Locality) Value(name string) (ent.Value, error) { + return _m.selectValues.Get(name) +} + +// QueryParentParish queries the "parent_parish" edge of the Locality entity. +func (_m *Locality) QueryParentParish() *AdministrativeAreaQuery { + return NewLocalityClient(_m.config).QueryParentParish(_m) +} + +// QueryProviderProfiles queries the "provider_profiles" edge of the Locality entity. +func (_m *Locality) QueryProviderProfiles() *ProviderProfileQuery { + return NewLocalityClient(_m.config).QueryProviderProfiles(_m) +} + +// Update returns a builder for updating this Locality. +// Note that you need to call Locality.Unwrap() before calling this method if this Locality +// was returned from a transaction, and the transaction was committed or rolled back. +func (_m *Locality) Update() *LocalityUpdateOne { + return NewLocalityClient(_m.config).UpdateOne(_m) +} + +// Unwrap unwraps the Locality 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 *Locality) Unwrap() *Locality { + _tx, ok := _m.config.driver.(*txDriver) + if !ok { + panic("ent: Locality is not a transactional entity") + } + _m.config.driver = _tx.drv + return _m +} + +// String implements the fmt.Stringer. +func (_m *Locality) String() string { + var builder strings.Builder + builder.WriteString("Locality(") + builder.WriteString(fmt.Sprintf("id=%v, ", _m.ID)) + builder.WriteString("slug=") + builder.WriteString(_m.Slug) + builder.WriteString(", ") + builder.WriteString("name=") + builder.WriteString(_m.Name) + builder.WriteString(", ") + builder.WriteString("parent_parish_id=") + builder.WriteString(fmt.Sprintf("%v", _m.ParentParishID)) + builder.WriteString(", ") + builder.WriteString("source=") + builder.WriteString(_m.Source) + builder.WriteString(", ") + builder.WriteString("source_element_id=") + builder.WriteString(_m.SourceElementID) + builder.WriteString(", ") + builder.WriteString("source_version=") + builder.WriteString(_m.SourceVersion) + builder.WriteString(", ") + builder.WriteString("source_retrieved_at=") + builder.WriteString(_m.SourceRetrievedAt.Format(time.ANSIC)) + builder.WriteString(", ") + builder.WriteString("latitude=") + builder.WriteString(fmt.Sprintf("%v", _m.Latitude)) + builder.WriteString(", ") + builder.WriteString("longitude=") + builder.WriteString(fmt.Sprintf("%v", _m.Longitude)) + builder.WriteString(", ") + builder.WriteString("active=") + builder.WriteString(fmt.Sprintf("%v", _m.Active)) + builder.WriteString(", ") + builder.WriteString("created_at=") + builder.WriteString(_m.CreatedAt.Format(time.ANSIC)) + builder.WriteString(", ") + builder.WriteString("updated_at=") + builder.WriteString(_m.UpdatedAt.Format(time.ANSIC)) + builder.WriteByte(')') + return builder.String() +} + +// Localities is a parsable slice of Locality. +type Localities []*Locality diff --git a/backend/ent/locality/locality.go b/backend/ent/locality/locality.go new file mode 100644 index 0000000..58ac867 --- /dev/null +++ b/backend/ent/locality/locality.go @@ -0,0 +1,225 @@ +// Code generated by ent, DO NOT EDIT. + +package locality + +import ( + "time" + + "entgo.io/ent/dialect/sql" + "entgo.io/ent/dialect/sql/sqlgraph" + "github.com/google/uuid" +) + +const ( + // Label holds the string label denoting the locality type in the database. + Label = "locality" + // FieldID holds the string denoting the id field in the database. + FieldID = "id" + // FieldSlug holds the string denoting the slug field in the database. + FieldSlug = "slug" + // FieldName holds the string denoting the name field in the database. + FieldName = "name" + // FieldParentParishID holds the string denoting the parent_parish_id field in the database. + FieldParentParishID = "parent_parish_id" + // FieldSource holds the string denoting the source field in the database. + FieldSource = "source" + // FieldSourceElementID holds the string denoting the source_element_id field in the database. + FieldSourceElementID = "source_element_id" + // FieldSourceVersion holds the string denoting the source_version field in the database. + FieldSourceVersion = "source_version" + // FieldSourceRetrievedAt holds the string denoting the source_retrieved_at field in the database. + FieldSourceRetrievedAt = "source_retrieved_at" + // FieldLatitude holds the string denoting the latitude field in the database. + FieldLatitude = "latitude" + // FieldLongitude holds the string denoting the longitude field in the database. + FieldLongitude = "longitude" + // FieldActive holds the string denoting the active field in the database. + FieldActive = "active" + // FieldCreatedAt holds the string denoting the created_at field in the database. + FieldCreatedAt = "created_at" + // FieldUpdatedAt holds the string denoting the updated_at field in the database. + FieldUpdatedAt = "updated_at" + // EdgeParentParish holds the string denoting the parent_parish edge name in mutations. + EdgeParentParish = "parent_parish" + // EdgeProviderProfiles holds the string denoting the provider_profiles edge name in mutations. + EdgeProviderProfiles = "provider_profiles" + // ProviderProfileFieldID holds the string denoting the ID field of the ProviderProfile. + ProviderProfileFieldID = "internal_user_id" + // Table holds the table name of the locality in the database. + Table = "localities" + // ParentParishTable is the table that holds the parent_parish relation/edge. + ParentParishTable = "localities" + // ParentParishInverseTable is the table name for the AdministrativeArea entity. + // It exists in this package in order to avoid circular dependency with the "administrativearea" package. + ParentParishInverseTable = "administrative_areas" + // ParentParishColumn is the table column denoting the parent_parish relation/edge. + ParentParishColumn = "parent_parish_id" + // ProviderProfilesTable is the table that holds the provider_profiles relation/edge. The primary key declared below. + ProviderProfilesTable = "provider_service_localities" + // ProviderProfilesInverseTable is the table name for the ProviderProfile entity. + // It exists in this package in order to avoid circular dependency with the "providerprofile" package. + ProviderProfilesInverseTable = "provider_profiles" +) + +// Columns holds all SQL columns for locality fields. +var Columns = []string{ + FieldID, + FieldSlug, + FieldName, + FieldParentParishID, + FieldSource, + FieldSourceElementID, + FieldSourceVersion, + FieldSourceRetrievedAt, + FieldLatitude, + FieldLongitude, + FieldActive, + FieldCreatedAt, + FieldUpdatedAt, +} + +var ( + // ProviderProfilesPrimaryKey and ProviderProfilesColumn2 are the table columns denoting the + // primary key for the provider_profiles relation (M2M). + ProviderProfilesPrimaryKey = []string{"internal_user_id", "locality_id"} +) + +// 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 ( + // SlugValidator is a validator for the "slug" field. It is called by the builders before save. + SlugValidator func(string) error + // NameValidator is a validator for the "name" field. It is called by the builders before save. + NameValidator func(string) error + // SourceValidator is a validator for the "source" field. It is called by the builders before save. + SourceValidator func(string) error + // SourceElementIDValidator is a validator for the "source_element_id" field. It is called by the builders before save. + SourceElementIDValidator func(string) error + // SourceVersionValidator is a validator for the "source_version" field. It is called by the builders before save. + SourceVersionValidator func(string) error + // LatitudeValidator is a validator for the "latitude" field. It is called by the builders before save. + LatitudeValidator func(float64) error + // LongitudeValidator is a validator for the "longitude" field. It is called by the builders before save. + LongitudeValidator func(float64) error + // DefaultActive holds the default value on creation for the "active" field. + DefaultActive bool + // DefaultCreatedAt holds the default value on creation for the "created_at" field. + DefaultCreatedAt func() time.Time + // DefaultUpdatedAt holds the default value on creation for the "updated_at" field. + DefaultUpdatedAt func() time.Time + // UpdateDefaultUpdatedAt holds the default value on update for the "updated_at" field. + UpdateDefaultUpdatedAt func() time.Time + // DefaultID holds the default value on creation for the "id" field. + DefaultID func() uuid.UUID +) + +// OrderOption defines the ordering options for the Locality 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() +} + +// BySlug orders the results by the slug field. +func BySlug(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldSlug, opts...).ToFunc() +} + +// ByName orders the results by the name field. +func ByName(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldName, opts...).ToFunc() +} + +// ByParentParishID orders the results by the parent_parish_id field. +func ByParentParishID(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldParentParishID, opts...).ToFunc() +} + +// BySource orders the results by the source field. +func BySource(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldSource, opts...).ToFunc() +} + +// BySourceElementID orders the results by the source_element_id field. +func BySourceElementID(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldSourceElementID, opts...).ToFunc() +} + +// BySourceVersion orders the results by the source_version field. +func BySourceVersion(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldSourceVersion, opts...).ToFunc() +} + +// BySourceRetrievedAt orders the results by the source_retrieved_at field. +func BySourceRetrievedAt(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldSourceRetrievedAt, opts...).ToFunc() +} + +// ByLatitude orders the results by the latitude field. +func ByLatitude(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldLatitude, opts...).ToFunc() +} + +// ByLongitude orders the results by the longitude field. +func ByLongitude(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldLongitude, opts...).ToFunc() +} + +// ByActive orders the results by the active field. +func ByActive(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldActive, opts...).ToFunc() +} + +// ByCreatedAt orders the results by the created_at field. +func ByCreatedAt(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldCreatedAt, opts...).ToFunc() +} + +// ByUpdatedAt orders the results by the updated_at field. +func ByUpdatedAt(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldUpdatedAt, opts...).ToFunc() +} + +// ByParentParishField orders the results by parent_parish field. +func ByParentParishField(field string, opts ...sql.OrderTermOption) OrderOption { + return func(s *sql.Selector) { + sqlgraph.OrderByNeighborTerms(s, newParentParishStep(), sql.OrderByField(field, opts...)) + } +} + +// ByProviderProfilesCount orders the results by provider_profiles count. +func ByProviderProfilesCount(opts ...sql.OrderTermOption) OrderOption { + return func(s *sql.Selector) { + sqlgraph.OrderByNeighborsCount(s, newProviderProfilesStep(), opts...) + } +} + +// ByProviderProfiles orders the results by provider_profiles terms. +func ByProviderProfiles(term sql.OrderTerm, terms ...sql.OrderTerm) OrderOption { + return func(s *sql.Selector) { + sqlgraph.OrderByNeighborTerms(s, newProviderProfilesStep(), append([]sql.OrderTerm{term}, terms...)...) + } +} +func newParentParishStep() *sqlgraph.Step { + return sqlgraph.NewStep( + sqlgraph.From(Table, FieldID), + sqlgraph.To(ParentParishInverseTable, FieldID), + sqlgraph.Edge(sqlgraph.M2O, true, ParentParishTable, ParentParishColumn), + ) +} +func newProviderProfilesStep() *sqlgraph.Step { + return sqlgraph.NewStep( + sqlgraph.From(Table, FieldID), + sqlgraph.To(ProviderProfilesInverseTable, ProviderProfileFieldID), + sqlgraph.Edge(sqlgraph.M2M, true, ProviderProfilesTable, ProviderProfilesPrimaryKey...), + ) +} diff --git a/backend/ent/locality/where.go b/backend/ent/locality/where.go new file mode 100644 index 0000000..51bb4d4 --- /dev/null +++ b/backend/ent/locality/where.go @@ -0,0 +1,733 @@ +// Code generated by ent, DO NOT EDIT. + +package locality + +import ( + "time" + + "entgo.io/ent/dialect/sql" + "entgo.io/ent/dialect/sql/sqlgraph" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/predicate" + "github.com/google/uuid" +) + +// ID filters vertices based on their ID field. +func ID(id uuid.UUID) predicate.Locality { + return predicate.Locality(sql.FieldEQ(FieldID, id)) +} + +// IDEQ applies the EQ predicate on the ID field. +func IDEQ(id uuid.UUID) predicate.Locality { + return predicate.Locality(sql.FieldEQ(FieldID, id)) +} + +// IDNEQ applies the NEQ predicate on the ID field. +func IDNEQ(id uuid.UUID) predicate.Locality { + return predicate.Locality(sql.FieldNEQ(FieldID, id)) +} + +// IDIn applies the In predicate on the ID field. +func IDIn(ids ...uuid.UUID) predicate.Locality { + return predicate.Locality(sql.FieldIn(FieldID, ids...)) +} + +// IDNotIn applies the NotIn predicate on the ID field. +func IDNotIn(ids ...uuid.UUID) predicate.Locality { + return predicate.Locality(sql.FieldNotIn(FieldID, ids...)) +} + +// IDGT applies the GT predicate on the ID field. +func IDGT(id uuid.UUID) predicate.Locality { + return predicate.Locality(sql.FieldGT(FieldID, id)) +} + +// IDGTE applies the GTE predicate on the ID field. +func IDGTE(id uuid.UUID) predicate.Locality { + return predicate.Locality(sql.FieldGTE(FieldID, id)) +} + +// IDLT applies the LT predicate on the ID field. +func IDLT(id uuid.UUID) predicate.Locality { + return predicate.Locality(sql.FieldLT(FieldID, id)) +} + +// IDLTE applies the LTE predicate on the ID field. +func IDLTE(id uuid.UUID) predicate.Locality { + return predicate.Locality(sql.FieldLTE(FieldID, id)) +} + +// Slug applies equality check predicate on the "slug" field. It's identical to SlugEQ. +func Slug(v string) predicate.Locality { + return predicate.Locality(sql.FieldEQ(FieldSlug, v)) +} + +// Name applies equality check predicate on the "name" field. It's identical to NameEQ. +func Name(v string) predicate.Locality { + return predicate.Locality(sql.FieldEQ(FieldName, v)) +} + +// ParentParishID applies equality check predicate on the "parent_parish_id" field. It's identical to ParentParishIDEQ. +func ParentParishID(v uuid.UUID) predicate.Locality { + return predicate.Locality(sql.FieldEQ(FieldParentParishID, v)) +} + +// Source applies equality check predicate on the "source" field. It's identical to SourceEQ. +func Source(v string) predicate.Locality { + return predicate.Locality(sql.FieldEQ(FieldSource, v)) +} + +// SourceElementID applies equality check predicate on the "source_element_id" field. It's identical to SourceElementIDEQ. +func SourceElementID(v string) predicate.Locality { + return predicate.Locality(sql.FieldEQ(FieldSourceElementID, v)) +} + +// SourceVersion applies equality check predicate on the "source_version" field. It's identical to SourceVersionEQ. +func SourceVersion(v string) predicate.Locality { + return predicate.Locality(sql.FieldEQ(FieldSourceVersion, v)) +} + +// SourceRetrievedAt applies equality check predicate on the "source_retrieved_at" field. It's identical to SourceRetrievedAtEQ. +func SourceRetrievedAt(v time.Time) predicate.Locality { + return predicate.Locality(sql.FieldEQ(FieldSourceRetrievedAt, v)) +} + +// Latitude applies equality check predicate on the "latitude" field. It's identical to LatitudeEQ. +func Latitude(v float64) predicate.Locality { + return predicate.Locality(sql.FieldEQ(FieldLatitude, v)) +} + +// Longitude applies equality check predicate on the "longitude" field. It's identical to LongitudeEQ. +func Longitude(v float64) predicate.Locality { + return predicate.Locality(sql.FieldEQ(FieldLongitude, v)) +} + +// Active applies equality check predicate on the "active" field. It's identical to ActiveEQ. +func Active(v bool) predicate.Locality { + return predicate.Locality(sql.FieldEQ(FieldActive, v)) +} + +// CreatedAt applies equality check predicate on the "created_at" field. It's identical to CreatedAtEQ. +func CreatedAt(v time.Time) predicate.Locality { + return predicate.Locality(sql.FieldEQ(FieldCreatedAt, v)) +} + +// UpdatedAt applies equality check predicate on the "updated_at" field. It's identical to UpdatedAtEQ. +func UpdatedAt(v time.Time) predicate.Locality { + return predicate.Locality(sql.FieldEQ(FieldUpdatedAt, v)) +} + +// SlugEQ applies the EQ predicate on the "slug" field. +func SlugEQ(v string) predicate.Locality { + return predicate.Locality(sql.FieldEQ(FieldSlug, v)) +} + +// SlugNEQ applies the NEQ predicate on the "slug" field. +func SlugNEQ(v string) predicate.Locality { + return predicate.Locality(sql.FieldNEQ(FieldSlug, v)) +} + +// SlugIn applies the In predicate on the "slug" field. +func SlugIn(vs ...string) predicate.Locality { + return predicate.Locality(sql.FieldIn(FieldSlug, vs...)) +} + +// SlugNotIn applies the NotIn predicate on the "slug" field. +func SlugNotIn(vs ...string) predicate.Locality { + return predicate.Locality(sql.FieldNotIn(FieldSlug, vs...)) +} + +// SlugGT applies the GT predicate on the "slug" field. +func SlugGT(v string) predicate.Locality { + return predicate.Locality(sql.FieldGT(FieldSlug, v)) +} + +// SlugGTE applies the GTE predicate on the "slug" field. +func SlugGTE(v string) predicate.Locality { + return predicate.Locality(sql.FieldGTE(FieldSlug, v)) +} + +// SlugLT applies the LT predicate on the "slug" field. +func SlugLT(v string) predicate.Locality { + return predicate.Locality(sql.FieldLT(FieldSlug, v)) +} + +// SlugLTE applies the LTE predicate on the "slug" field. +func SlugLTE(v string) predicate.Locality { + return predicate.Locality(sql.FieldLTE(FieldSlug, v)) +} + +// SlugContains applies the Contains predicate on the "slug" field. +func SlugContains(v string) predicate.Locality { + return predicate.Locality(sql.FieldContains(FieldSlug, v)) +} + +// SlugHasPrefix applies the HasPrefix predicate on the "slug" field. +func SlugHasPrefix(v string) predicate.Locality { + return predicate.Locality(sql.FieldHasPrefix(FieldSlug, v)) +} + +// SlugHasSuffix applies the HasSuffix predicate on the "slug" field. +func SlugHasSuffix(v string) predicate.Locality { + return predicate.Locality(sql.FieldHasSuffix(FieldSlug, v)) +} + +// SlugEqualFold applies the EqualFold predicate on the "slug" field. +func SlugEqualFold(v string) predicate.Locality { + return predicate.Locality(sql.FieldEqualFold(FieldSlug, v)) +} + +// SlugContainsFold applies the ContainsFold predicate on the "slug" field. +func SlugContainsFold(v string) predicate.Locality { + return predicate.Locality(sql.FieldContainsFold(FieldSlug, v)) +} + +// NameEQ applies the EQ predicate on the "name" field. +func NameEQ(v string) predicate.Locality { + return predicate.Locality(sql.FieldEQ(FieldName, v)) +} + +// NameNEQ applies the NEQ predicate on the "name" field. +func NameNEQ(v string) predicate.Locality { + return predicate.Locality(sql.FieldNEQ(FieldName, v)) +} + +// NameIn applies the In predicate on the "name" field. +func NameIn(vs ...string) predicate.Locality { + return predicate.Locality(sql.FieldIn(FieldName, vs...)) +} + +// NameNotIn applies the NotIn predicate on the "name" field. +func NameNotIn(vs ...string) predicate.Locality { + return predicate.Locality(sql.FieldNotIn(FieldName, vs...)) +} + +// NameGT applies the GT predicate on the "name" field. +func NameGT(v string) predicate.Locality { + return predicate.Locality(sql.FieldGT(FieldName, v)) +} + +// NameGTE applies the GTE predicate on the "name" field. +func NameGTE(v string) predicate.Locality { + return predicate.Locality(sql.FieldGTE(FieldName, v)) +} + +// NameLT applies the LT predicate on the "name" field. +func NameLT(v string) predicate.Locality { + return predicate.Locality(sql.FieldLT(FieldName, v)) +} + +// NameLTE applies the LTE predicate on the "name" field. +func NameLTE(v string) predicate.Locality { + return predicate.Locality(sql.FieldLTE(FieldName, v)) +} + +// NameContains applies the Contains predicate on the "name" field. +func NameContains(v string) predicate.Locality { + return predicate.Locality(sql.FieldContains(FieldName, v)) +} + +// NameHasPrefix applies the HasPrefix predicate on the "name" field. +func NameHasPrefix(v string) predicate.Locality { + return predicate.Locality(sql.FieldHasPrefix(FieldName, v)) +} + +// NameHasSuffix applies the HasSuffix predicate on the "name" field. +func NameHasSuffix(v string) predicate.Locality { + return predicate.Locality(sql.FieldHasSuffix(FieldName, v)) +} + +// NameEqualFold applies the EqualFold predicate on the "name" field. +func NameEqualFold(v string) predicate.Locality { + return predicate.Locality(sql.FieldEqualFold(FieldName, v)) +} + +// NameContainsFold applies the ContainsFold predicate on the "name" field. +func NameContainsFold(v string) predicate.Locality { + return predicate.Locality(sql.FieldContainsFold(FieldName, v)) +} + +// ParentParishIDEQ applies the EQ predicate on the "parent_parish_id" field. +func ParentParishIDEQ(v uuid.UUID) predicate.Locality { + return predicate.Locality(sql.FieldEQ(FieldParentParishID, v)) +} + +// ParentParishIDNEQ applies the NEQ predicate on the "parent_parish_id" field. +func ParentParishIDNEQ(v uuid.UUID) predicate.Locality { + return predicate.Locality(sql.FieldNEQ(FieldParentParishID, v)) +} + +// ParentParishIDIn applies the In predicate on the "parent_parish_id" field. +func ParentParishIDIn(vs ...uuid.UUID) predicate.Locality { + return predicate.Locality(sql.FieldIn(FieldParentParishID, vs...)) +} + +// ParentParishIDNotIn applies the NotIn predicate on the "parent_parish_id" field. +func ParentParishIDNotIn(vs ...uuid.UUID) predicate.Locality { + return predicate.Locality(sql.FieldNotIn(FieldParentParishID, vs...)) +} + +// SourceEQ applies the EQ predicate on the "source" field. +func SourceEQ(v string) predicate.Locality { + return predicate.Locality(sql.FieldEQ(FieldSource, v)) +} + +// SourceNEQ applies the NEQ predicate on the "source" field. +func SourceNEQ(v string) predicate.Locality { + return predicate.Locality(sql.FieldNEQ(FieldSource, v)) +} + +// SourceIn applies the In predicate on the "source" field. +func SourceIn(vs ...string) predicate.Locality { + return predicate.Locality(sql.FieldIn(FieldSource, vs...)) +} + +// SourceNotIn applies the NotIn predicate on the "source" field. +func SourceNotIn(vs ...string) predicate.Locality { + return predicate.Locality(sql.FieldNotIn(FieldSource, vs...)) +} + +// SourceGT applies the GT predicate on the "source" field. +func SourceGT(v string) predicate.Locality { + return predicate.Locality(sql.FieldGT(FieldSource, v)) +} + +// SourceGTE applies the GTE predicate on the "source" field. +func SourceGTE(v string) predicate.Locality { + return predicate.Locality(sql.FieldGTE(FieldSource, v)) +} + +// SourceLT applies the LT predicate on the "source" field. +func SourceLT(v string) predicate.Locality { + return predicate.Locality(sql.FieldLT(FieldSource, v)) +} + +// SourceLTE applies the LTE predicate on the "source" field. +func SourceLTE(v string) predicate.Locality { + return predicate.Locality(sql.FieldLTE(FieldSource, v)) +} + +// SourceContains applies the Contains predicate on the "source" field. +func SourceContains(v string) predicate.Locality { + return predicate.Locality(sql.FieldContains(FieldSource, v)) +} + +// SourceHasPrefix applies the HasPrefix predicate on the "source" field. +func SourceHasPrefix(v string) predicate.Locality { + return predicate.Locality(sql.FieldHasPrefix(FieldSource, v)) +} + +// SourceHasSuffix applies the HasSuffix predicate on the "source" field. +func SourceHasSuffix(v string) predicate.Locality { + return predicate.Locality(sql.FieldHasSuffix(FieldSource, v)) +} + +// SourceEqualFold applies the EqualFold predicate on the "source" field. +func SourceEqualFold(v string) predicate.Locality { + return predicate.Locality(sql.FieldEqualFold(FieldSource, v)) +} + +// SourceContainsFold applies the ContainsFold predicate on the "source" field. +func SourceContainsFold(v string) predicate.Locality { + return predicate.Locality(sql.FieldContainsFold(FieldSource, v)) +} + +// SourceElementIDEQ applies the EQ predicate on the "source_element_id" field. +func SourceElementIDEQ(v string) predicate.Locality { + return predicate.Locality(sql.FieldEQ(FieldSourceElementID, v)) +} + +// SourceElementIDNEQ applies the NEQ predicate on the "source_element_id" field. +func SourceElementIDNEQ(v string) predicate.Locality { + return predicate.Locality(sql.FieldNEQ(FieldSourceElementID, v)) +} + +// SourceElementIDIn applies the In predicate on the "source_element_id" field. +func SourceElementIDIn(vs ...string) predicate.Locality { + return predicate.Locality(sql.FieldIn(FieldSourceElementID, vs...)) +} + +// SourceElementIDNotIn applies the NotIn predicate on the "source_element_id" field. +func SourceElementIDNotIn(vs ...string) predicate.Locality { + return predicate.Locality(sql.FieldNotIn(FieldSourceElementID, vs...)) +} + +// SourceElementIDGT applies the GT predicate on the "source_element_id" field. +func SourceElementIDGT(v string) predicate.Locality { + return predicate.Locality(sql.FieldGT(FieldSourceElementID, v)) +} + +// SourceElementIDGTE applies the GTE predicate on the "source_element_id" field. +func SourceElementIDGTE(v string) predicate.Locality { + return predicate.Locality(sql.FieldGTE(FieldSourceElementID, v)) +} + +// SourceElementIDLT applies the LT predicate on the "source_element_id" field. +func SourceElementIDLT(v string) predicate.Locality { + return predicate.Locality(sql.FieldLT(FieldSourceElementID, v)) +} + +// SourceElementIDLTE applies the LTE predicate on the "source_element_id" field. +func SourceElementIDLTE(v string) predicate.Locality { + return predicate.Locality(sql.FieldLTE(FieldSourceElementID, v)) +} + +// SourceElementIDContains applies the Contains predicate on the "source_element_id" field. +func SourceElementIDContains(v string) predicate.Locality { + return predicate.Locality(sql.FieldContains(FieldSourceElementID, v)) +} + +// SourceElementIDHasPrefix applies the HasPrefix predicate on the "source_element_id" field. +func SourceElementIDHasPrefix(v string) predicate.Locality { + return predicate.Locality(sql.FieldHasPrefix(FieldSourceElementID, v)) +} + +// SourceElementIDHasSuffix applies the HasSuffix predicate on the "source_element_id" field. +func SourceElementIDHasSuffix(v string) predicate.Locality { + return predicate.Locality(sql.FieldHasSuffix(FieldSourceElementID, v)) +} + +// SourceElementIDEqualFold applies the EqualFold predicate on the "source_element_id" field. +func SourceElementIDEqualFold(v string) predicate.Locality { + return predicate.Locality(sql.FieldEqualFold(FieldSourceElementID, v)) +} + +// SourceElementIDContainsFold applies the ContainsFold predicate on the "source_element_id" field. +func SourceElementIDContainsFold(v string) predicate.Locality { + return predicate.Locality(sql.FieldContainsFold(FieldSourceElementID, v)) +} + +// SourceVersionEQ applies the EQ predicate on the "source_version" field. +func SourceVersionEQ(v string) predicate.Locality { + return predicate.Locality(sql.FieldEQ(FieldSourceVersion, v)) +} + +// SourceVersionNEQ applies the NEQ predicate on the "source_version" field. +func SourceVersionNEQ(v string) predicate.Locality { + return predicate.Locality(sql.FieldNEQ(FieldSourceVersion, v)) +} + +// SourceVersionIn applies the In predicate on the "source_version" field. +func SourceVersionIn(vs ...string) predicate.Locality { + return predicate.Locality(sql.FieldIn(FieldSourceVersion, vs...)) +} + +// SourceVersionNotIn applies the NotIn predicate on the "source_version" field. +func SourceVersionNotIn(vs ...string) predicate.Locality { + return predicate.Locality(sql.FieldNotIn(FieldSourceVersion, vs...)) +} + +// SourceVersionGT applies the GT predicate on the "source_version" field. +func SourceVersionGT(v string) predicate.Locality { + return predicate.Locality(sql.FieldGT(FieldSourceVersion, v)) +} + +// SourceVersionGTE applies the GTE predicate on the "source_version" field. +func SourceVersionGTE(v string) predicate.Locality { + return predicate.Locality(sql.FieldGTE(FieldSourceVersion, v)) +} + +// SourceVersionLT applies the LT predicate on the "source_version" field. +func SourceVersionLT(v string) predicate.Locality { + return predicate.Locality(sql.FieldLT(FieldSourceVersion, v)) +} + +// SourceVersionLTE applies the LTE predicate on the "source_version" field. +func SourceVersionLTE(v string) predicate.Locality { + return predicate.Locality(sql.FieldLTE(FieldSourceVersion, v)) +} + +// SourceVersionContains applies the Contains predicate on the "source_version" field. +func SourceVersionContains(v string) predicate.Locality { + return predicate.Locality(sql.FieldContains(FieldSourceVersion, v)) +} + +// SourceVersionHasPrefix applies the HasPrefix predicate on the "source_version" field. +func SourceVersionHasPrefix(v string) predicate.Locality { + return predicate.Locality(sql.FieldHasPrefix(FieldSourceVersion, v)) +} + +// SourceVersionHasSuffix applies the HasSuffix predicate on the "source_version" field. +func SourceVersionHasSuffix(v string) predicate.Locality { + return predicate.Locality(sql.FieldHasSuffix(FieldSourceVersion, v)) +} + +// SourceVersionEqualFold applies the EqualFold predicate on the "source_version" field. +func SourceVersionEqualFold(v string) predicate.Locality { + return predicate.Locality(sql.FieldEqualFold(FieldSourceVersion, v)) +} + +// SourceVersionContainsFold applies the ContainsFold predicate on the "source_version" field. +func SourceVersionContainsFold(v string) predicate.Locality { + return predicate.Locality(sql.FieldContainsFold(FieldSourceVersion, v)) +} + +// SourceRetrievedAtEQ applies the EQ predicate on the "source_retrieved_at" field. +func SourceRetrievedAtEQ(v time.Time) predicate.Locality { + return predicate.Locality(sql.FieldEQ(FieldSourceRetrievedAt, v)) +} + +// SourceRetrievedAtNEQ applies the NEQ predicate on the "source_retrieved_at" field. +func SourceRetrievedAtNEQ(v time.Time) predicate.Locality { + return predicate.Locality(sql.FieldNEQ(FieldSourceRetrievedAt, v)) +} + +// SourceRetrievedAtIn applies the In predicate on the "source_retrieved_at" field. +func SourceRetrievedAtIn(vs ...time.Time) predicate.Locality { + return predicate.Locality(sql.FieldIn(FieldSourceRetrievedAt, vs...)) +} + +// SourceRetrievedAtNotIn applies the NotIn predicate on the "source_retrieved_at" field. +func SourceRetrievedAtNotIn(vs ...time.Time) predicate.Locality { + return predicate.Locality(sql.FieldNotIn(FieldSourceRetrievedAt, vs...)) +} + +// SourceRetrievedAtGT applies the GT predicate on the "source_retrieved_at" field. +func SourceRetrievedAtGT(v time.Time) predicate.Locality { + return predicate.Locality(sql.FieldGT(FieldSourceRetrievedAt, v)) +} + +// SourceRetrievedAtGTE applies the GTE predicate on the "source_retrieved_at" field. +func SourceRetrievedAtGTE(v time.Time) predicate.Locality { + return predicate.Locality(sql.FieldGTE(FieldSourceRetrievedAt, v)) +} + +// SourceRetrievedAtLT applies the LT predicate on the "source_retrieved_at" field. +func SourceRetrievedAtLT(v time.Time) predicate.Locality { + return predicate.Locality(sql.FieldLT(FieldSourceRetrievedAt, v)) +} + +// SourceRetrievedAtLTE applies the LTE predicate on the "source_retrieved_at" field. +func SourceRetrievedAtLTE(v time.Time) predicate.Locality { + return predicate.Locality(sql.FieldLTE(FieldSourceRetrievedAt, v)) +} + +// LatitudeEQ applies the EQ predicate on the "latitude" field. +func LatitudeEQ(v float64) predicate.Locality { + return predicate.Locality(sql.FieldEQ(FieldLatitude, v)) +} + +// LatitudeNEQ applies the NEQ predicate on the "latitude" field. +func LatitudeNEQ(v float64) predicate.Locality { + return predicate.Locality(sql.FieldNEQ(FieldLatitude, v)) +} + +// LatitudeIn applies the In predicate on the "latitude" field. +func LatitudeIn(vs ...float64) predicate.Locality { + return predicate.Locality(sql.FieldIn(FieldLatitude, vs...)) +} + +// LatitudeNotIn applies the NotIn predicate on the "latitude" field. +func LatitudeNotIn(vs ...float64) predicate.Locality { + return predicate.Locality(sql.FieldNotIn(FieldLatitude, vs...)) +} + +// LatitudeGT applies the GT predicate on the "latitude" field. +func LatitudeGT(v float64) predicate.Locality { + return predicate.Locality(sql.FieldGT(FieldLatitude, v)) +} + +// LatitudeGTE applies the GTE predicate on the "latitude" field. +func LatitudeGTE(v float64) predicate.Locality { + return predicate.Locality(sql.FieldGTE(FieldLatitude, v)) +} + +// LatitudeLT applies the LT predicate on the "latitude" field. +func LatitudeLT(v float64) predicate.Locality { + return predicate.Locality(sql.FieldLT(FieldLatitude, v)) +} + +// LatitudeLTE applies the LTE predicate on the "latitude" field. +func LatitudeLTE(v float64) predicate.Locality { + return predicate.Locality(sql.FieldLTE(FieldLatitude, v)) +} + +// LongitudeEQ applies the EQ predicate on the "longitude" field. +func LongitudeEQ(v float64) predicate.Locality { + return predicate.Locality(sql.FieldEQ(FieldLongitude, v)) +} + +// LongitudeNEQ applies the NEQ predicate on the "longitude" field. +func LongitudeNEQ(v float64) predicate.Locality { + return predicate.Locality(sql.FieldNEQ(FieldLongitude, v)) +} + +// LongitudeIn applies the In predicate on the "longitude" field. +func LongitudeIn(vs ...float64) predicate.Locality { + return predicate.Locality(sql.FieldIn(FieldLongitude, vs...)) +} + +// LongitudeNotIn applies the NotIn predicate on the "longitude" field. +func LongitudeNotIn(vs ...float64) predicate.Locality { + return predicate.Locality(sql.FieldNotIn(FieldLongitude, vs...)) +} + +// LongitudeGT applies the GT predicate on the "longitude" field. +func LongitudeGT(v float64) predicate.Locality { + return predicate.Locality(sql.FieldGT(FieldLongitude, v)) +} + +// LongitudeGTE applies the GTE predicate on the "longitude" field. +func LongitudeGTE(v float64) predicate.Locality { + return predicate.Locality(sql.FieldGTE(FieldLongitude, v)) +} + +// LongitudeLT applies the LT predicate on the "longitude" field. +func LongitudeLT(v float64) predicate.Locality { + return predicate.Locality(sql.FieldLT(FieldLongitude, v)) +} + +// LongitudeLTE applies the LTE predicate on the "longitude" field. +func LongitudeLTE(v float64) predicate.Locality { + return predicate.Locality(sql.FieldLTE(FieldLongitude, v)) +} + +// ActiveEQ applies the EQ predicate on the "active" field. +func ActiveEQ(v bool) predicate.Locality { + return predicate.Locality(sql.FieldEQ(FieldActive, v)) +} + +// ActiveNEQ applies the NEQ predicate on the "active" field. +func ActiveNEQ(v bool) predicate.Locality { + return predicate.Locality(sql.FieldNEQ(FieldActive, v)) +} + +// CreatedAtEQ applies the EQ predicate on the "created_at" field. +func CreatedAtEQ(v time.Time) predicate.Locality { + return predicate.Locality(sql.FieldEQ(FieldCreatedAt, v)) +} + +// CreatedAtNEQ applies the NEQ predicate on the "created_at" field. +func CreatedAtNEQ(v time.Time) predicate.Locality { + return predicate.Locality(sql.FieldNEQ(FieldCreatedAt, v)) +} + +// CreatedAtIn applies the In predicate on the "created_at" field. +func CreatedAtIn(vs ...time.Time) predicate.Locality { + return predicate.Locality(sql.FieldIn(FieldCreatedAt, vs...)) +} + +// CreatedAtNotIn applies the NotIn predicate on the "created_at" field. +func CreatedAtNotIn(vs ...time.Time) predicate.Locality { + return predicate.Locality(sql.FieldNotIn(FieldCreatedAt, vs...)) +} + +// CreatedAtGT applies the GT predicate on the "created_at" field. +func CreatedAtGT(v time.Time) predicate.Locality { + return predicate.Locality(sql.FieldGT(FieldCreatedAt, v)) +} + +// CreatedAtGTE applies the GTE predicate on the "created_at" field. +func CreatedAtGTE(v time.Time) predicate.Locality { + return predicate.Locality(sql.FieldGTE(FieldCreatedAt, v)) +} + +// CreatedAtLT applies the LT predicate on the "created_at" field. +func CreatedAtLT(v time.Time) predicate.Locality { + return predicate.Locality(sql.FieldLT(FieldCreatedAt, v)) +} + +// CreatedAtLTE applies the LTE predicate on the "created_at" field. +func CreatedAtLTE(v time.Time) predicate.Locality { + return predicate.Locality(sql.FieldLTE(FieldCreatedAt, v)) +} + +// UpdatedAtEQ applies the EQ predicate on the "updated_at" field. +func UpdatedAtEQ(v time.Time) predicate.Locality { + return predicate.Locality(sql.FieldEQ(FieldUpdatedAt, v)) +} + +// UpdatedAtNEQ applies the NEQ predicate on the "updated_at" field. +func UpdatedAtNEQ(v time.Time) predicate.Locality { + return predicate.Locality(sql.FieldNEQ(FieldUpdatedAt, v)) +} + +// UpdatedAtIn applies the In predicate on the "updated_at" field. +func UpdatedAtIn(vs ...time.Time) predicate.Locality { + return predicate.Locality(sql.FieldIn(FieldUpdatedAt, vs...)) +} + +// UpdatedAtNotIn applies the NotIn predicate on the "updated_at" field. +func UpdatedAtNotIn(vs ...time.Time) predicate.Locality { + return predicate.Locality(sql.FieldNotIn(FieldUpdatedAt, vs...)) +} + +// UpdatedAtGT applies the GT predicate on the "updated_at" field. +func UpdatedAtGT(v time.Time) predicate.Locality { + return predicate.Locality(sql.FieldGT(FieldUpdatedAt, v)) +} + +// UpdatedAtGTE applies the GTE predicate on the "updated_at" field. +func UpdatedAtGTE(v time.Time) predicate.Locality { + return predicate.Locality(sql.FieldGTE(FieldUpdatedAt, v)) +} + +// UpdatedAtLT applies the LT predicate on the "updated_at" field. +func UpdatedAtLT(v time.Time) predicate.Locality { + return predicate.Locality(sql.FieldLT(FieldUpdatedAt, v)) +} + +// UpdatedAtLTE applies the LTE predicate on the "updated_at" field. +func UpdatedAtLTE(v time.Time) predicate.Locality { + return predicate.Locality(sql.FieldLTE(FieldUpdatedAt, v)) +} + +// HasParentParish applies the HasEdge predicate on the "parent_parish" edge. +func HasParentParish() predicate.Locality { + return predicate.Locality(func(s *sql.Selector) { + step := sqlgraph.NewStep( + sqlgraph.From(Table, FieldID), + sqlgraph.Edge(sqlgraph.M2O, true, ParentParishTable, ParentParishColumn), + ) + sqlgraph.HasNeighbors(s, step) + }) +} + +// HasParentParishWith applies the HasEdge predicate on the "parent_parish" edge with a given conditions (other predicates). +func HasParentParishWith(preds ...predicate.AdministrativeArea) predicate.Locality { + return predicate.Locality(func(s *sql.Selector) { + step := newParentParishStep() + sqlgraph.HasNeighborsWith(s, step, func(s *sql.Selector) { + for _, p := range preds { + p(s) + } + }) + }) +} + +// HasProviderProfiles applies the HasEdge predicate on the "provider_profiles" edge. +func HasProviderProfiles() predicate.Locality { + return predicate.Locality(func(s *sql.Selector) { + step := sqlgraph.NewStep( + sqlgraph.From(Table, FieldID), + sqlgraph.Edge(sqlgraph.M2M, true, ProviderProfilesTable, ProviderProfilesPrimaryKey...), + ) + sqlgraph.HasNeighbors(s, step) + }) +} + +// HasProviderProfilesWith applies the HasEdge predicate on the "provider_profiles" edge with a given conditions (other predicates). +func HasProviderProfilesWith(preds ...predicate.ProviderProfile) predicate.Locality { + return predicate.Locality(func(s *sql.Selector) { + step := newProviderProfilesStep() + 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.Locality) predicate.Locality { + return predicate.Locality(sql.AndPredicates(predicates...)) +} + +// Or groups predicates with the OR operator between them. +func Or(predicates ...predicate.Locality) predicate.Locality { + return predicate.Locality(sql.OrPredicates(predicates...)) +} + +// Not applies the not operator on the given predicate. +func Not(p predicate.Locality) predicate.Locality { + return predicate.Locality(sql.NotPredicates(p)) +} diff --git a/backend/ent/locality_create.go b/backend/ent/locality_create.go new file mode 100644 index 0000000..b14fe6c --- /dev/null +++ b/backend/ent/locality_create.go @@ -0,0 +1,482 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "context" + "errors" + "fmt" + "time" + + "entgo.io/ent/dialect/sql/sqlgraph" + "entgo.io/ent/schema/field" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/administrativearea" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/locality" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/providerprofile" + "github.com/google/uuid" +) + +// LocalityCreate is the builder for creating a Locality entity. +type LocalityCreate struct { + config + mutation *LocalityMutation + hooks []Hook +} + +// SetSlug sets the "slug" field. +func (_c *LocalityCreate) SetSlug(v string) *LocalityCreate { + _c.mutation.SetSlug(v) + return _c +} + +// SetName sets the "name" field. +func (_c *LocalityCreate) SetName(v string) *LocalityCreate { + _c.mutation.SetName(v) + return _c +} + +// SetParentParishID sets the "parent_parish_id" field. +func (_c *LocalityCreate) SetParentParishID(v uuid.UUID) *LocalityCreate { + _c.mutation.SetParentParishID(v) + return _c +} + +// SetSource sets the "source" field. +func (_c *LocalityCreate) SetSource(v string) *LocalityCreate { + _c.mutation.SetSource(v) + return _c +} + +// SetSourceElementID sets the "source_element_id" field. +func (_c *LocalityCreate) SetSourceElementID(v string) *LocalityCreate { + _c.mutation.SetSourceElementID(v) + return _c +} + +// SetSourceVersion sets the "source_version" field. +func (_c *LocalityCreate) SetSourceVersion(v string) *LocalityCreate { + _c.mutation.SetSourceVersion(v) + return _c +} + +// SetSourceRetrievedAt sets the "source_retrieved_at" field. +func (_c *LocalityCreate) SetSourceRetrievedAt(v time.Time) *LocalityCreate { + _c.mutation.SetSourceRetrievedAt(v) + return _c +} + +// SetLatitude sets the "latitude" field. +func (_c *LocalityCreate) SetLatitude(v float64) *LocalityCreate { + _c.mutation.SetLatitude(v) + return _c +} + +// SetLongitude sets the "longitude" field. +func (_c *LocalityCreate) SetLongitude(v float64) *LocalityCreate { + _c.mutation.SetLongitude(v) + return _c +} + +// SetActive sets the "active" field. +func (_c *LocalityCreate) SetActive(v bool) *LocalityCreate { + _c.mutation.SetActive(v) + return _c +} + +// SetNillableActive sets the "active" field if the given value is not nil. +func (_c *LocalityCreate) SetNillableActive(v *bool) *LocalityCreate { + if v != nil { + _c.SetActive(*v) + } + return _c +} + +// SetCreatedAt sets the "created_at" field. +func (_c *LocalityCreate) SetCreatedAt(v time.Time) *LocalityCreate { + _c.mutation.SetCreatedAt(v) + return _c +} + +// SetNillableCreatedAt sets the "created_at" field if the given value is not nil. +func (_c *LocalityCreate) SetNillableCreatedAt(v *time.Time) *LocalityCreate { + if v != nil { + _c.SetCreatedAt(*v) + } + return _c +} + +// SetUpdatedAt sets the "updated_at" field. +func (_c *LocalityCreate) SetUpdatedAt(v time.Time) *LocalityCreate { + _c.mutation.SetUpdatedAt(v) + return _c +} + +// SetNillableUpdatedAt sets the "updated_at" field if the given value is not nil. +func (_c *LocalityCreate) SetNillableUpdatedAt(v *time.Time) *LocalityCreate { + if v != nil { + _c.SetUpdatedAt(*v) + } + return _c +} + +// SetID sets the "id" field. +func (_c *LocalityCreate) SetID(v uuid.UUID) *LocalityCreate { + _c.mutation.SetID(v) + return _c +} + +// SetNillableID sets the "id" field if the given value is not nil. +func (_c *LocalityCreate) SetNillableID(v *uuid.UUID) *LocalityCreate { + if v != nil { + _c.SetID(*v) + } + return _c +} + +// SetParentParish sets the "parent_parish" edge to the AdministrativeArea entity. +func (_c *LocalityCreate) SetParentParish(v *AdministrativeArea) *LocalityCreate { + return _c.SetParentParishID(v.ID) +} + +// AddProviderProfileIDs adds the "provider_profiles" edge to the ProviderProfile entity by IDs. +func (_c *LocalityCreate) AddProviderProfileIDs(ids ...uuid.UUID) *LocalityCreate { + _c.mutation.AddProviderProfileIDs(ids...) + return _c +} + +// AddProviderProfiles adds the "provider_profiles" edges to the ProviderProfile entity. +func (_c *LocalityCreate) AddProviderProfiles(v ...*ProviderProfile) *LocalityCreate { + ids := make([]uuid.UUID, len(v)) + for i := range v { + ids[i] = v[i].ID + } + return _c.AddProviderProfileIDs(ids...) +} + +// Mutation returns the LocalityMutation object of the builder. +func (_c *LocalityCreate) Mutation() *LocalityMutation { + return _c.mutation +} + +// Save creates the Locality in the database. +func (_c *LocalityCreate) Save(ctx context.Context) (*Locality, error) { + _c.defaults() + return withHooks(ctx, _c.sqlSave, _c.mutation, _c.hooks) +} + +// SaveX calls Save and panics if Save returns an error. +func (_c *LocalityCreate) SaveX(ctx context.Context) *Locality { + v, err := _c.Save(ctx) + if err != nil { + panic(err) + } + return v +} + +// Exec executes the query. +func (_c *LocalityCreate) Exec(ctx context.Context) error { + _, err := _c.Save(ctx) + return err +} + +// ExecX is like Exec, but panics if an error occurs. +func (_c *LocalityCreate) 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 *LocalityCreate) defaults() { + if _, ok := _c.mutation.Active(); !ok { + v := locality.DefaultActive + _c.mutation.SetActive(v) + } + if _, ok := _c.mutation.CreatedAt(); !ok { + v := locality.DefaultCreatedAt() + _c.mutation.SetCreatedAt(v) + } + if _, ok := _c.mutation.UpdatedAt(); !ok { + v := locality.DefaultUpdatedAt() + _c.mutation.SetUpdatedAt(v) + } + if _, ok := _c.mutation.ID(); !ok { + v := locality.DefaultID() + _c.mutation.SetID(v) + } +} + +// check runs all checks and user-defined validators on the builder. +func (_c *LocalityCreate) check() error { + if _, ok := _c.mutation.Slug(); !ok { + return &ValidationError{Name: "slug", err: errors.New(`ent: missing required field "Locality.slug"`)} + } + if v, ok := _c.mutation.Slug(); ok { + if err := locality.SlugValidator(v); err != nil { + return &ValidationError{Name: "slug", err: fmt.Errorf(`ent: validator failed for field "Locality.slug": %w`, err)} + } + } + if _, ok := _c.mutation.Name(); !ok { + return &ValidationError{Name: "name", err: errors.New(`ent: missing required field "Locality.name"`)} + } + if v, ok := _c.mutation.Name(); ok { + if err := locality.NameValidator(v); err != nil { + return &ValidationError{Name: "name", err: fmt.Errorf(`ent: validator failed for field "Locality.name": %w`, err)} + } + } + if _, ok := _c.mutation.ParentParishID(); !ok { + return &ValidationError{Name: "parent_parish_id", err: errors.New(`ent: missing required field "Locality.parent_parish_id"`)} + } + if _, ok := _c.mutation.Source(); !ok { + return &ValidationError{Name: "source", err: errors.New(`ent: missing required field "Locality.source"`)} + } + if v, ok := _c.mutation.Source(); ok { + if err := locality.SourceValidator(v); err != nil { + return &ValidationError{Name: "source", err: fmt.Errorf(`ent: validator failed for field "Locality.source": %w`, err)} + } + } + if _, ok := _c.mutation.SourceElementID(); !ok { + return &ValidationError{Name: "source_element_id", err: errors.New(`ent: missing required field "Locality.source_element_id"`)} + } + if v, ok := _c.mutation.SourceElementID(); ok { + if err := locality.SourceElementIDValidator(v); err != nil { + return &ValidationError{Name: "source_element_id", err: fmt.Errorf(`ent: validator failed for field "Locality.source_element_id": %w`, err)} + } + } + if _, ok := _c.mutation.SourceVersion(); !ok { + return &ValidationError{Name: "source_version", err: errors.New(`ent: missing required field "Locality.source_version"`)} + } + if v, ok := _c.mutation.SourceVersion(); ok { + if err := locality.SourceVersionValidator(v); err != nil { + return &ValidationError{Name: "source_version", err: fmt.Errorf(`ent: validator failed for field "Locality.source_version": %w`, err)} + } + } + if _, ok := _c.mutation.SourceRetrievedAt(); !ok { + return &ValidationError{Name: "source_retrieved_at", err: errors.New(`ent: missing required field "Locality.source_retrieved_at"`)} + } + if _, ok := _c.mutation.Latitude(); !ok { + return &ValidationError{Name: "latitude", err: errors.New(`ent: missing required field "Locality.latitude"`)} + } + if v, ok := _c.mutation.Latitude(); ok { + if err := locality.LatitudeValidator(v); err != nil { + return &ValidationError{Name: "latitude", err: fmt.Errorf(`ent: validator failed for field "Locality.latitude": %w`, err)} + } + } + if _, ok := _c.mutation.Longitude(); !ok { + return &ValidationError{Name: "longitude", err: errors.New(`ent: missing required field "Locality.longitude"`)} + } + if v, ok := _c.mutation.Longitude(); ok { + if err := locality.LongitudeValidator(v); err != nil { + return &ValidationError{Name: "longitude", err: fmt.Errorf(`ent: validator failed for field "Locality.longitude": %w`, err)} + } + } + if _, ok := _c.mutation.Active(); !ok { + return &ValidationError{Name: "active", err: errors.New(`ent: missing required field "Locality.active"`)} + } + if _, ok := _c.mutation.CreatedAt(); !ok { + return &ValidationError{Name: "created_at", err: errors.New(`ent: missing required field "Locality.created_at"`)} + } + if _, ok := _c.mutation.UpdatedAt(); !ok { + return &ValidationError{Name: "updated_at", err: errors.New(`ent: missing required field "Locality.updated_at"`)} + } + if len(_c.mutation.ParentParishIDs()) == 0 { + return &ValidationError{Name: "parent_parish", err: errors.New(`ent: missing required edge "Locality.parent_parish"`)} + } + return nil +} + +func (_c *LocalityCreate) sqlSave(ctx context.Context) (*Locality, 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 *LocalityCreate) createSpec() (*Locality, *sqlgraph.CreateSpec) { + var ( + _node = &Locality{config: _c.config} + _spec = sqlgraph.NewCreateSpec(locality.Table, sqlgraph.NewFieldSpec(locality.FieldID, field.TypeUUID)) + ) + if id, ok := _c.mutation.ID(); ok { + _node.ID = id + _spec.ID.Value = &id + } + if value, ok := _c.mutation.Slug(); ok { + _spec.SetField(locality.FieldSlug, field.TypeString, value) + _node.Slug = value + } + if value, ok := _c.mutation.Name(); ok { + _spec.SetField(locality.FieldName, field.TypeString, value) + _node.Name = value + } + if value, ok := _c.mutation.Source(); ok { + _spec.SetField(locality.FieldSource, field.TypeString, value) + _node.Source = value + } + if value, ok := _c.mutation.SourceElementID(); ok { + _spec.SetField(locality.FieldSourceElementID, field.TypeString, value) + _node.SourceElementID = value + } + if value, ok := _c.mutation.SourceVersion(); ok { + _spec.SetField(locality.FieldSourceVersion, field.TypeString, value) + _node.SourceVersion = value + } + if value, ok := _c.mutation.SourceRetrievedAt(); ok { + _spec.SetField(locality.FieldSourceRetrievedAt, field.TypeTime, value) + _node.SourceRetrievedAt = value + } + if value, ok := _c.mutation.Latitude(); ok { + _spec.SetField(locality.FieldLatitude, field.TypeFloat64, value) + _node.Latitude = value + } + if value, ok := _c.mutation.Longitude(); ok { + _spec.SetField(locality.FieldLongitude, field.TypeFloat64, value) + _node.Longitude = value + } + if value, ok := _c.mutation.Active(); ok { + _spec.SetField(locality.FieldActive, field.TypeBool, value) + _node.Active = value + } + if value, ok := _c.mutation.CreatedAt(); ok { + _spec.SetField(locality.FieldCreatedAt, field.TypeTime, value) + _node.CreatedAt = value + } + if value, ok := _c.mutation.UpdatedAt(); ok { + _spec.SetField(locality.FieldUpdatedAt, field.TypeTime, value) + _node.UpdatedAt = value + } + if nodes := _c.mutation.ParentParishIDs(); len(nodes) > 0 { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.M2O, + Inverse: true, + Table: locality.ParentParishTable, + Columns: []string{locality.ParentParishColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(administrativearea.FieldID, field.TypeUUID), + }, + } + for _, k := range nodes { + edge.Target.Nodes = append(edge.Target.Nodes, k) + } + _node.ParentParishID = nodes[0] + _spec.Edges = append(_spec.Edges, edge) + } + if nodes := _c.mutation.ProviderProfilesIDs(); len(nodes) > 0 { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.M2M, + Inverse: true, + Table: locality.ProviderProfilesTable, + Columns: locality.ProviderProfilesPrimaryKey, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(providerprofile.FieldID, field.TypeUUID), + }, + } + for _, k := range nodes { + edge.Target.Nodes = append(edge.Target.Nodes, k) + } + _spec.Edges = append(_spec.Edges, edge) + } + return _node, _spec +} + +// LocalityCreateBulk is the builder for creating many Locality entities in bulk. +type LocalityCreateBulk struct { + config + err error + builders []*LocalityCreate +} + +// Save creates the Locality entities in the database. +func (_c *LocalityCreateBulk) Save(ctx context.Context) ([]*Locality, error) { + if _c.err != nil { + return nil, _c.err + } + specs := make([]*sqlgraph.CreateSpec, len(_c.builders)) + nodes := make([]*Locality, 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.(*LocalityMutation) + 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 *LocalityCreateBulk) SaveX(ctx context.Context) []*Locality { + v, err := _c.Save(ctx) + if err != nil { + panic(err) + } + return v +} + +// Exec executes the query. +func (_c *LocalityCreateBulk) Exec(ctx context.Context) error { + _, err := _c.Save(ctx) + return err +} + +// ExecX is like Exec, but panics if an error occurs. +func (_c *LocalityCreateBulk) ExecX(ctx context.Context) { + if err := _c.Exec(ctx); err != nil { + panic(err) + } +} diff --git a/backend/ent/locality_delete.go b/backend/ent/locality_delete.go new file mode 100644 index 0000000..5478bc6 --- /dev/null +++ b/backend/ent/locality_delete.go @@ -0,0 +1,88 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "context" + + "entgo.io/ent/dialect/sql" + "entgo.io/ent/dialect/sql/sqlgraph" + "entgo.io/ent/schema/field" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/locality" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/predicate" +) + +// LocalityDelete is the builder for deleting a Locality entity. +type LocalityDelete struct { + config + hooks []Hook + mutation *LocalityMutation +} + +// Where appends a list predicates to the LocalityDelete builder. +func (_d *LocalityDelete) Where(ps ...predicate.Locality) *LocalityDelete { + _d.mutation.Where(ps...) + return _d +} + +// Exec executes the deletion query and returns how many vertices were deleted. +func (_d *LocalityDelete) 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 *LocalityDelete) ExecX(ctx context.Context) int { + n, err := _d.Exec(ctx) + if err != nil { + panic(err) + } + return n +} + +func (_d *LocalityDelete) sqlExec(ctx context.Context) (int, error) { + _spec := sqlgraph.NewDeleteSpec(locality.Table, sqlgraph.NewFieldSpec(locality.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 +} + +// LocalityDeleteOne is the builder for deleting a single Locality entity. +type LocalityDeleteOne struct { + _d *LocalityDelete +} + +// Where appends a list predicates to the LocalityDelete builder. +func (_d *LocalityDeleteOne) Where(ps ...predicate.Locality) *LocalityDeleteOne { + _d._d.mutation.Where(ps...) + return _d +} + +// Exec executes the deletion query. +func (_d *LocalityDeleteOne) Exec(ctx context.Context) error { + n, err := _d._d.Exec(ctx) + switch { + case err != nil: + return err + case n == 0: + return &NotFoundError{locality.Label} + default: + return nil + } +} + +// ExecX is like Exec, but panics if an error occurs. +func (_d *LocalityDeleteOne) ExecX(ctx context.Context) { + if err := _d.Exec(ctx); err != nil { + panic(err) + } +} diff --git a/backend/ent/locality_query.go b/backend/ent/locality_query.go new file mode 100644 index 0000000..5a3896c --- /dev/null +++ b/backend/ent/locality_query.go @@ -0,0 +1,713 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "context" + "database/sql/driver" + "fmt" + "math" + + "entgo.io/ent" + "entgo.io/ent/dialect/sql" + "entgo.io/ent/dialect/sql/sqlgraph" + "entgo.io/ent/schema/field" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/administrativearea" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/locality" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/predicate" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/providerprofile" + "github.com/google/uuid" +) + +// LocalityQuery is the builder for querying Locality entities. +type LocalityQuery struct { + config + ctx *QueryContext + order []locality.OrderOption + inters []Interceptor + predicates []predicate.Locality + withParentParish *AdministrativeAreaQuery + withProviderProfiles *ProviderProfileQuery + // intermediate query (i.e. traversal path). + sql *sql.Selector + path func(context.Context) (*sql.Selector, error) +} + +// Where adds a new predicate for the LocalityQuery builder. +func (_q *LocalityQuery) Where(ps ...predicate.Locality) *LocalityQuery { + _q.predicates = append(_q.predicates, ps...) + return _q +} + +// Limit the number of records to be returned by this query. +func (_q *LocalityQuery) Limit(limit int) *LocalityQuery { + _q.ctx.Limit = &limit + return _q +} + +// Offset to start from. +func (_q *LocalityQuery) Offset(offset int) *LocalityQuery { + _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 *LocalityQuery) Unique(unique bool) *LocalityQuery { + _q.ctx.Unique = &unique + return _q +} + +// Order specifies how the records should be ordered. +func (_q *LocalityQuery) Order(o ...locality.OrderOption) *LocalityQuery { + _q.order = append(_q.order, o...) + return _q +} + +// QueryParentParish chains the current query on the "parent_parish" edge. +func (_q *LocalityQuery) QueryParentParish() *AdministrativeAreaQuery { + query := (&AdministrativeAreaClient{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(locality.Table, locality.FieldID, selector), + sqlgraph.To(administrativearea.Table, administrativearea.FieldID), + sqlgraph.Edge(sqlgraph.M2O, true, locality.ParentParishTable, locality.ParentParishColumn), + ) + fromU = sqlgraph.SetNeighbors(_q.driver.Dialect(), step) + return fromU, nil + } + return query +} + +// QueryProviderProfiles chains the current query on the "provider_profiles" edge. +func (_q *LocalityQuery) QueryProviderProfiles() *ProviderProfileQuery { + query := (&ProviderProfileClient{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(locality.Table, locality.FieldID, selector), + sqlgraph.To(providerprofile.Table, providerprofile.FieldID), + sqlgraph.Edge(sqlgraph.M2M, true, locality.ProviderProfilesTable, locality.ProviderProfilesPrimaryKey...), + ) + fromU = sqlgraph.SetNeighbors(_q.driver.Dialect(), step) + return fromU, nil + } + return query +} + +// First returns the first Locality entity from the query. +// Returns a *NotFoundError when no Locality was found. +func (_q *LocalityQuery) First(ctx context.Context) (*Locality, 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{locality.Label} + } + return nodes[0], nil +} + +// FirstX is like First, but panics if an error occurs. +func (_q *LocalityQuery) FirstX(ctx context.Context) *Locality { + node, err := _q.First(ctx) + if err != nil && !IsNotFound(err) { + panic(err) + } + return node +} + +// FirstID returns the first Locality ID from the query. +// Returns a *NotFoundError when no Locality ID was found. +func (_q *LocalityQuery) 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{locality.Label} + return + } + return ids[0], nil +} + +// FirstIDX is like FirstID, but panics if an error occurs. +func (_q *LocalityQuery) FirstIDX(ctx context.Context) uuid.UUID { + id, err := _q.FirstID(ctx) + if err != nil && !IsNotFound(err) { + panic(err) + } + return id +} + +// Only returns a single Locality entity found by the query, ensuring it only returns one. +// Returns a *NotSingularError when more than one Locality entity is found. +// Returns a *NotFoundError when no Locality entities are found. +func (_q *LocalityQuery) Only(ctx context.Context) (*Locality, 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{locality.Label} + default: + return nil, &NotSingularError{locality.Label} + } +} + +// OnlyX is like Only, but panics if an error occurs. +func (_q *LocalityQuery) OnlyX(ctx context.Context) *Locality { + node, err := _q.Only(ctx) + if err != nil { + panic(err) + } + return node +} + +// OnlyID is like Only, but returns the only Locality ID in the query. +// Returns a *NotSingularError when more than one Locality ID is found. +// Returns a *NotFoundError when no entities are found. +func (_q *LocalityQuery) 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{locality.Label} + default: + err = &NotSingularError{locality.Label} + } + return +} + +// OnlyIDX is like OnlyID, but panics if an error occurs. +func (_q *LocalityQuery) 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 Localities. +func (_q *LocalityQuery) All(ctx context.Context) ([]*Locality, error) { + ctx = setContextOp(ctx, _q.ctx, ent.OpQueryAll) + if err := _q.prepareQuery(ctx); err != nil { + return nil, err + } + qr := querierAll[[]*Locality, *LocalityQuery]() + return withInterceptors[[]*Locality](ctx, _q, qr, _q.inters) +} + +// AllX is like All, but panics if an error occurs. +func (_q *LocalityQuery) AllX(ctx context.Context) []*Locality { + nodes, err := _q.All(ctx) + if err != nil { + panic(err) + } + return nodes +} + +// IDs executes the query and returns a list of Locality IDs. +func (_q *LocalityQuery) 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(locality.FieldID).Scan(ctx, &ids); err != nil { + return nil, err + } + return ids, nil +} + +// IDsX is like IDs, but panics if an error occurs. +func (_q *LocalityQuery) 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 *LocalityQuery) 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[*LocalityQuery](), _q.inters) +} + +// CountX is like Count, but panics if an error occurs. +func (_q *LocalityQuery) 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 *LocalityQuery) 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 *LocalityQuery) ExistX(ctx context.Context) bool { + exist, err := _q.Exist(ctx) + if err != nil { + panic(err) + } + return exist +} + +// Clone returns a duplicate of the LocalityQuery 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 *LocalityQuery) Clone() *LocalityQuery { + if _q == nil { + return nil + } + return &LocalityQuery{ + config: _q.config, + ctx: _q.ctx.Clone(), + order: append([]locality.OrderOption{}, _q.order...), + inters: append([]Interceptor{}, _q.inters...), + predicates: append([]predicate.Locality{}, _q.predicates...), + withParentParish: _q.withParentParish.Clone(), + withProviderProfiles: _q.withProviderProfiles.Clone(), + // clone intermediate query. + sql: _q.sql.Clone(), + path: _q.path, + } +} + +// WithParentParish tells the query-builder to eager-load the nodes that are connected to +// the "parent_parish" edge. The optional arguments are used to configure the query builder of the edge. +func (_q *LocalityQuery) WithParentParish(opts ...func(*AdministrativeAreaQuery)) *LocalityQuery { + query := (&AdministrativeAreaClient{config: _q.config}).Query() + for _, opt := range opts { + opt(query) + } + _q.withParentParish = query + return _q +} + +// WithProviderProfiles tells the query-builder to eager-load the nodes that are connected to +// the "provider_profiles" edge. The optional arguments are used to configure the query builder of the edge. +func (_q *LocalityQuery) WithProviderProfiles(opts ...func(*ProviderProfileQuery)) *LocalityQuery { + query := (&ProviderProfileClient{config: _q.config}).Query() + for _, opt := range opts { + opt(query) + } + _q.withProviderProfiles = 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 { +// Slug string `json:"slug,omitempty"` +// Count int `json:"count,omitempty"` +// } +// +// client.Locality.Query(). +// GroupBy(locality.FieldSlug). +// Aggregate(ent.Count()). +// Scan(ctx, &v) +func (_q *LocalityQuery) GroupBy(field string, fields ...string) *LocalityGroupBy { + _q.ctx.Fields = append([]string{field}, fields...) + grbuild := &LocalityGroupBy{build: _q} + grbuild.flds = &_q.ctx.Fields + grbuild.label = locality.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 { +// Slug string `json:"slug,omitempty"` +// } +// +// client.Locality.Query(). +// Select(locality.FieldSlug). +// Scan(ctx, &v) +func (_q *LocalityQuery) Select(fields ...string) *LocalitySelect { + _q.ctx.Fields = append(_q.ctx.Fields, fields...) + sbuild := &LocalitySelect{LocalityQuery: _q} + sbuild.label = locality.Label + sbuild.flds, sbuild.scan = &_q.ctx.Fields, sbuild.Scan + return sbuild +} + +// Aggregate returns a LocalitySelect configured with the given aggregations. +func (_q *LocalityQuery) Aggregate(fns ...AggregateFunc) *LocalitySelect { + return _q.Select().Aggregate(fns...) +} + +func (_q *LocalityQuery) 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 !locality.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 *LocalityQuery) sqlAll(ctx context.Context, hooks ...queryHook) ([]*Locality, error) { + var ( + nodes = []*Locality{} + _spec = _q.querySpec() + loadedTypes = [2]bool{ + _q.withParentParish != nil, + _q.withProviderProfiles != nil, + } + ) + _spec.ScanValues = func(columns []string) ([]any, error) { + return (*Locality).scanValues(nil, columns) + } + _spec.Assign = func(columns []string, values []any) error { + node := &Locality{config: _q.config} + nodes = append(nodes, node) + node.Edges.loadedTypes = loadedTypes + return node.assignValues(columns, values) + } + 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.withParentParish; query != nil { + if err := _q.loadParentParish(ctx, query, nodes, nil, + func(n *Locality, e *AdministrativeArea) { n.Edges.ParentParish = e }); err != nil { + return nil, err + } + } + if query := _q.withProviderProfiles; query != nil { + if err := _q.loadProviderProfiles(ctx, query, nodes, + func(n *Locality) { n.Edges.ProviderProfiles = []*ProviderProfile{} }, + func(n *Locality, e *ProviderProfile) { n.Edges.ProviderProfiles = append(n.Edges.ProviderProfiles, e) }); err != nil { + return nil, err + } + } + return nodes, nil +} + +func (_q *LocalityQuery) loadParentParish(ctx context.Context, query *AdministrativeAreaQuery, nodes []*Locality, init func(*Locality), assign func(*Locality, *AdministrativeArea)) error { + ids := make([]uuid.UUID, 0, len(nodes)) + nodeids := make(map[uuid.UUID][]*Locality) + for i := range nodes { + fk := nodes[i].ParentParishID + if _, ok := nodeids[fk]; !ok { + ids = append(ids, fk) + } + nodeids[fk] = append(nodeids[fk], nodes[i]) + } + if len(ids) == 0 { + return nil + } + query.Where(administrativearea.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 "parent_parish_id" returned %v`, n.ID) + } + for i := range nodes { + assign(nodes[i], n) + } + } + return nil +} +func (_q *LocalityQuery) loadProviderProfiles(ctx context.Context, query *ProviderProfileQuery, nodes []*Locality, init func(*Locality), assign func(*Locality, *ProviderProfile)) error { + edgeIDs := make([]driver.Value, len(nodes)) + byID := make(map[uuid.UUID]*Locality) + nids := make(map[uuid.UUID]map[*Locality]struct{}) + for i, node := range nodes { + edgeIDs[i] = node.ID + byID[node.ID] = node + if init != nil { + init(node) + } + } + query.Where(func(s *sql.Selector) { + joinT := sql.Table(locality.ProviderProfilesTable) + s.Join(joinT).On(s.C(providerprofile.FieldID), joinT.C(locality.ProviderProfilesPrimaryKey[0])) + s.Where(sql.InValues(joinT.C(locality.ProviderProfilesPrimaryKey[1]), edgeIDs...)) + columns := s.SelectedColumns() + s.Select(joinT.C(locality.ProviderProfilesPrimaryKey[1])) + s.AppendSelect(columns...) + s.SetDistinct(false) + }) + if err := query.prepareQuery(ctx); err != nil { + return err + } + qr := QuerierFunc(func(ctx context.Context, q Query) (Value, error) { + return query.sqlAll(ctx, func(_ context.Context, spec *sqlgraph.QuerySpec) { + assign := spec.Assign + values := spec.ScanValues + spec.ScanValues = func(columns []string) ([]any, error) { + values, err := values(columns[1:]) + if err != nil { + return nil, err + } + return append([]any{new(uuid.UUID)}, values...), nil + } + spec.Assign = func(columns []string, values []any) error { + outValue := *values[0].(*uuid.UUID) + inValue := *values[1].(*uuid.UUID) + if nids[inValue] == nil { + nids[inValue] = map[*Locality]struct{}{byID[outValue]: {}} + return assign(columns[1:], values[1:]) + } + nids[inValue][byID[outValue]] = struct{}{} + return nil + } + }) + }) + neighbors, err := withInterceptors[[]*ProviderProfile](ctx, query, qr, query.inters) + if err != nil { + return err + } + for _, n := range neighbors { + nodes, ok := nids[n.ID] + if !ok { + return fmt.Errorf(`unexpected "provider_profiles" node returned %v`, n.ID) + } + for kn := range nodes { + assign(kn, n) + } + } + return nil +} + +func (_q *LocalityQuery) sqlCount(ctx context.Context) (int, error) { + _spec := _q.querySpec() + _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 *LocalityQuery) querySpec() *sqlgraph.QuerySpec { + _spec := sqlgraph.NewQuerySpec(locality.Table, locality.Columns, sqlgraph.NewFieldSpec(locality.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, locality.FieldID) + for i := range fields { + if fields[i] != locality.FieldID { + _spec.Node.Columns = append(_spec.Node.Columns, fields[i]) + } + } + if _q.withParentParish != nil { + _spec.Node.AddColumnOnce(locality.FieldParentParishID) + } + } + 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 *LocalityQuery) sqlQuery(ctx context.Context) *sql.Selector { + builder := sql.Dialect(_q.driver.Dialect()) + t1 := builder.Table(locality.Table) + columns := _q.ctx.Fields + if len(columns) == 0 { + columns = locality.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 _, 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 +} + +// LocalityGroupBy is the group-by builder for Locality entities. +type LocalityGroupBy struct { + selector + build *LocalityQuery +} + +// Aggregate adds the given aggregation functions to the group-by query. +func (_g *LocalityGroupBy) Aggregate(fns ...AggregateFunc) *LocalityGroupBy { + _g.fns = append(_g.fns, fns...) + return _g +} + +// Scan applies the selector query and scans the result into the given value. +func (_g *LocalityGroupBy) 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[*LocalityQuery, *LocalityGroupBy](ctx, _g.build, _g, _g.build.inters, v) +} + +func (_g *LocalityGroupBy) sqlScan(ctx context.Context, root *LocalityQuery, 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) +} + +// LocalitySelect is the builder for selecting fields of Locality entities. +type LocalitySelect struct { + *LocalityQuery + selector +} + +// Aggregate adds the given aggregation functions to the selector query. +func (_s *LocalitySelect) Aggregate(fns ...AggregateFunc) *LocalitySelect { + _s.fns = append(_s.fns, fns...) + return _s +} + +// Scan applies the selector query and scans the result into the given value. +func (_s *LocalitySelect) 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[*LocalityQuery, *LocalitySelect](ctx, _s.LocalityQuery, _s, _s.inters, v) +} + +func (_s *LocalitySelect) sqlScan(ctx context.Context, root *LocalityQuery, 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/backend/ent/locality_update.go b/backend/ent/locality_update.go new file mode 100644 index 0000000..cff622e --- /dev/null +++ b/backend/ent/locality_update.go @@ -0,0 +1,476 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "context" + "errors" + "fmt" + "time" + + "entgo.io/ent/dialect/sql" + "entgo.io/ent/dialect/sql/sqlgraph" + "entgo.io/ent/schema/field" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/locality" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/predicate" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/providerprofile" + "github.com/google/uuid" +) + +// LocalityUpdate is the builder for updating Locality entities. +type LocalityUpdate struct { + config + hooks []Hook + mutation *LocalityMutation +} + +// Where appends a list predicates to the LocalityUpdate builder. +func (_u *LocalityUpdate) Where(ps ...predicate.Locality) *LocalityUpdate { + _u.mutation.Where(ps...) + return _u +} + +// SetName sets the "name" field. +func (_u *LocalityUpdate) SetName(v string) *LocalityUpdate { + _u.mutation.SetName(v) + return _u +} + +// SetNillableName sets the "name" field if the given value is not nil. +func (_u *LocalityUpdate) SetNillableName(v *string) *LocalityUpdate { + if v != nil { + _u.SetName(*v) + } + return _u +} + +// SetActive sets the "active" field. +func (_u *LocalityUpdate) SetActive(v bool) *LocalityUpdate { + _u.mutation.SetActive(v) + return _u +} + +// SetNillableActive sets the "active" field if the given value is not nil. +func (_u *LocalityUpdate) SetNillableActive(v *bool) *LocalityUpdate { + if v != nil { + _u.SetActive(*v) + } + return _u +} + +// SetUpdatedAt sets the "updated_at" field. +func (_u *LocalityUpdate) SetUpdatedAt(v time.Time) *LocalityUpdate { + _u.mutation.SetUpdatedAt(v) + return _u +} + +// AddProviderProfileIDs adds the "provider_profiles" edge to the ProviderProfile entity by IDs. +func (_u *LocalityUpdate) AddProviderProfileIDs(ids ...uuid.UUID) *LocalityUpdate { + _u.mutation.AddProviderProfileIDs(ids...) + return _u +} + +// AddProviderProfiles adds the "provider_profiles" edges to the ProviderProfile entity. +func (_u *LocalityUpdate) AddProviderProfiles(v ...*ProviderProfile) *LocalityUpdate { + ids := make([]uuid.UUID, len(v)) + for i := range v { + ids[i] = v[i].ID + } + return _u.AddProviderProfileIDs(ids...) +} + +// Mutation returns the LocalityMutation object of the builder. +func (_u *LocalityUpdate) Mutation() *LocalityMutation { + return _u.mutation +} + +// ClearProviderProfiles clears all "provider_profiles" edges to the ProviderProfile entity. +func (_u *LocalityUpdate) ClearProviderProfiles() *LocalityUpdate { + _u.mutation.ClearProviderProfiles() + return _u +} + +// RemoveProviderProfileIDs removes the "provider_profiles" edge to ProviderProfile entities by IDs. +func (_u *LocalityUpdate) RemoveProviderProfileIDs(ids ...uuid.UUID) *LocalityUpdate { + _u.mutation.RemoveProviderProfileIDs(ids...) + return _u +} + +// RemoveProviderProfiles removes "provider_profiles" edges to ProviderProfile entities. +func (_u *LocalityUpdate) RemoveProviderProfiles(v ...*ProviderProfile) *LocalityUpdate { + ids := make([]uuid.UUID, len(v)) + for i := range v { + ids[i] = v[i].ID + } + return _u.RemoveProviderProfileIDs(ids...) +} + +// Save executes the query and returns the number of nodes affected by the update operation. +func (_u *LocalityUpdate) Save(ctx context.Context) (int, error) { + _u.defaults() + return withHooks(ctx, _u.sqlSave, _u.mutation, _u.hooks) +} + +// SaveX is like Save, but panics if an error occurs. +func (_u *LocalityUpdate) SaveX(ctx context.Context) int { + affected, err := _u.Save(ctx) + if err != nil { + panic(err) + } + return affected +} + +// Exec executes the query. +func (_u *LocalityUpdate) Exec(ctx context.Context) error { + _, err := _u.Save(ctx) + return err +} + +// ExecX is like Exec, but panics if an error occurs. +func (_u *LocalityUpdate) ExecX(ctx context.Context) { + if err := _u.Exec(ctx); err != nil { + panic(err) + } +} + +// defaults sets the default values of the builder before save. +func (_u *LocalityUpdate) defaults() { + if _, ok := _u.mutation.UpdatedAt(); !ok { + v := locality.UpdateDefaultUpdatedAt() + _u.mutation.SetUpdatedAt(v) + } +} + +// check runs all checks and user-defined validators on the builder. +func (_u *LocalityUpdate) check() error { + if v, ok := _u.mutation.Name(); ok { + if err := locality.NameValidator(v); err != nil { + return &ValidationError{Name: "name", err: fmt.Errorf(`ent: validator failed for field "Locality.name": %w`, err)} + } + } + if _u.mutation.ParentParishCleared() && len(_u.mutation.ParentParishIDs()) > 0 { + return errors.New(`ent: clearing a required unique edge "Locality.parent_parish"`) + } + return nil +} + +func (_u *LocalityUpdate) sqlSave(ctx context.Context) (_node int, err error) { + if err := _u.check(); err != nil { + return _node, err + } + _spec := sqlgraph.NewUpdateSpec(locality.Table, locality.Columns, sqlgraph.NewFieldSpec(locality.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(locality.FieldName, field.TypeString, value) + } + if value, ok := _u.mutation.Active(); ok { + _spec.SetField(locality.FieldActive, field.TypeBool, value) + } + if value, ok := _u.mutation.UpdatedAt(); ok { + _spec.SetField(locality.FieldUpdatedAt, field.TypeTime, value) + } + if _u.mutation.ProviderProfilesCleared() { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.M2M, + Inverse: true, + Table: locality.ProviderProfilesTable, + Columns: locality.ProviderProfilesPrimaryKey, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(providerprofile.FieldID, field.TypeUUID), + }, + } + _spec.Edges.Clear = append(_spec.Edges.Clear, edge) + } + if nodes := _u.mutation.RemovedProviderProfilesIDs(); len(nodes) > 0 && !_u.mutation.ProviderProfilesCleared() { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.M2M, + Inverse: true, + Table: locality.ProviderProfilesTable, + Columns: locality.ProviderProfilesPrimaryKey, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(providerprofile.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.ProviderProfilesIDs(); len(nodes) > 0 { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.M2M, + Inverse: true, + Table: locality.ProviderProfilesTable, + Columns: locality.ProviderProfilesPrimaryKey, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(providerprofile.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{locality.Label} + } else if sqlgraph.IsConstraintError(err) { + err = &ConstraintError{msg: err.Error(), wrap: err} + } + return 0, err + } + _u.mutation.done = true + return _node, nil +} + +// LocalityUpdateOne is the builder for updating a single Locality entity. +type LocalityUpdateOne struct { + config + fields []string + hooks []Hook + mutation *LocalityMutation +} + +// SetName sets the "name" field. +func (_u *LocalityUpdateOne) SetName(v string) *LocalityUpdateOne { + _u.mutation.SetName(v) + return _u +} + +// SetNillableName sets the "name" field if the given value is not nil. +func (_u *LocalityUpdateOne) SetNillableName(v *string) *LocalityUpdateOne { + if v != nil { + _u.SetName(*v) + } + return _u +} + +// SetActive sets the "active" field. +func (_u *LocalityUpdateOne) SetActive(v bool) *LocalityUpdateOne { + _u.mutation.SetActive(v) + return _u +} + +// SetNillableActive sets the "active" field if the given value is not nil. +func (_u *LocalityUpdateOne) SetNillableActive(v *bool) *LocalityUpdateOne { + if v != nil { + _u.SetActive(*v) + } + return _u +} + +// SetUpdatedAt sets the "updated_at" field. +func (_u *LocalityUpdateOne) SetUpdatedAt(v time.Time) *LocalityUpdateOne { + _u.mutation.SetUpdatedAt(v) + return _u +} + +// AddProviderProfileIDs adds the "provider_profiles" edge to the ProviderProfile entity by IDs. +func (_u *LocalityUpdateOne) AddProviderProfileIDs(ids ...uuid.UUID) *LocalityUpdateOne { + _u.mutation.AddProviderProfileIDs(ids...) + return _u +} + +// AddProviderProfiles adds the "provider_profiles" edges to the ProviderProfile entity. +func (_u *LocalityUpdateOne) AddProviderProfiles(v ...*ProviderProfile) *LocalityUpdateOne { + ids := make([]uuid.UUID, len(v)) + for i := range v { + ids[i] = v[i].ID + } + return _u.AddProviderProfileIDs(ids...) +} + +// Mutation returns the LocalityMutation object of the builder. +func (_u *LocalityUpdateOne) Mutation() *LocalityMutation { + return _u.mutation +} + +// ClearProviderProfiles clears all "provider_profiles" edges to the ProviderProfile entity. +func (_u *LocalityUpdateOne) ClearProviderProfiles() *LocalityUpdateOne { + _u.mutation.ClearProviderProfiles() + return _u +} + +// RemoveProviderProfileIDs removes the "provider_profiles" edge to ProviderProfile entities by IDs. +func (_u *LocalityUpdateOne) RemoveProviderProfileIDs(ids ...uuid.UUID) *LocalityUpdateOne { + _u.mutation.RemoveProviderProfileIDs(ids...) + return _u +} + +// RemoveProviderProfiles removes "provider_profiles" edges to ProviderProfile entities. +func (_u *LocalityUpdateOne) RemoveProviderProfiles(v ...*ProviderProfile) *LocalityUpdateOne { + ids := make([]uuid.UUID, len(v)) + for i := range v { + ids[i] = v[i].ID + } + return _u.RemoveProviderProfileIDs(ids...) +} + +// Where appends a list predicates to the LocalityUpdate builder. +func (_u *LocalityUpdateOne) Where(ps ...predicate.Locality) *LocalityUpdateOne { + _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 *LocalityUpdateOne) Select(field string, fields ...string) *LocalityUpdateOne { + _u.fields = append([]string{field}, fields...) + return _u +} + +// Save executes the query and returns the updated Locality entity. +func (_u *LocalityUpdateOne) Save(ctx context.Context) (*Locality, error) { + _u.defaults() + return withHooks(ctx, _u.sqlSave, _u.mutation, _u.hooks) +} + +// SaveX is like Save, but panics if an error occurs. +func (_u *LocalityUpdateOne) SaveX(ctx context.Context) *Locality { + node, err := _u.Save(ctx) + if err != nil { + panic(err) + } + return node +} + +// Exec executes the query on the entity. +func (_u *LocalityUpdateOne) Exec(ctx context.Context) error { + _, err := _u.Save(ctx) + return err +} + +// ExecX is like Exec, but panics if an error occurs. +func (_u *LocalityUpdateOne) ExecX(ctx context.Context) { + if err := _u.Exec(ctx); err != nil { + panic(err) + } +} + +// defaults sets the default values of the builder before save. +func (_u *LocalityUpdateOne) defaults() { + if _, ok := _u.mutation.UpdatedAt(); !ok { + v := locality.UpdateDefaultUpdatedAt() + _u.mutation.SetUpdatedAt(v) + } +} + +// check runs all checks and user-defined validators on the builder. +func (_u *LocalityUpdateOne) check() error { + if v, ok := _u.mutation.Name(); ok { + if err := locality.NameValidator(v); err != nil { + return &ValidationError{Name: "name", err: fmt.Errorf(`ent: validator failed for field "Locality.name": %w`, err)} + } + } + if _u.mutation.ParentParishCleared() && len(_u.mutation.ParentParishIDs()) > 0 { + return errors.New(`ent: clearing a required unique edge "Locality.parent_parish"`) + } + return nil +} + +func (_u *LocalityUpdateOne) sqlSave(ctx context.Context) (_node *Locality, err error) { + if err := _u.check(); err != nil { + return _node, err + } + _spec := sqlgraph.NewUpdateSpec(locality.Table, locality.Columns, sqlgraph.NewFieldSpec(locality.FieldID, field.TypeUUID)) + id, ok := _u.mutation.ID() + if !ok { + return nil, &ValidationError{Name: "id", err: errors.New(`ent: missing "Locality.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, locality.FieldID) + for _, f := range fields { + if !locality.ValidColumn(f) { + return nil, &ValidationError{Name: f, err: fmt.Errorf("ent: invalid field %q for query", f)} + } + if f != locality.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(locality.FieldName, field.TypeString, value) + } + if value, ok := _u.mutation.Active(); ok { + _spec.SetField(locality.FieldActive, field.TypeBool, value) + } + if value, ok := _u.mutation.UpdatedAt(); ok { + _spec.SetField(locality.FieldUpdatedAt, field.TypeTime, value) + } + if _u.mutation.ProviderProfilesCleared() { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.M2M, + Inverse: true, + Table: locality.ProviderProfilesTable, + Columns: locality.ProviderProfilesPrimaryKey, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(providerprofile.FieldID, field.TypeUUID), + }, + } + _spec.Edges.Clear = append(_spec.Edges.Clear, edge) + } + if nodes := _u.mutation.RemovedProviderProfilesIDs(); len(nodes) > 0 && !_u.mutation.ProviderProfilesCleared() { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.M2M, + Inverse: true, + Table: locality.ProviderProfilesTable, + Columns: locality.ProviderProfilesPrimaryKey, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(providerprofile.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.ProviderProfilesIDs(); len(nodes) > 0 { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.M2M, + Inverse: true, + Table: locality.ProviderProfilesTable, + Columns: locality.ProviderProfilesPrimaryKey, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(providerprofile.FieldID, field.TypeUUID), + }, + } + for _, k := range nodes { + edge.Target.Nodes = append(edge.Target.Nodes, k) + } + _spec.Edges.Add = append(_spec.Edges.Add, edge) + } + _node = &Locality{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{locality.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/backend/ent/migrate/migrate.go b/backend/ent/migrate/migrate.go new file mode 100644 index 0000000..1956a6b --- /dev/null +++ b/backend/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/backend/ent/migrate/schema.go b/backend/ent/migrate/schema.go new file mode 100644 index 0000000..f85474a --- /dev/null +++ b/backend/ent/migrate/schema.go @@ -0,0 +1,532 @@ +// Code generated by ent, DO NOT EDIT. + +package migrate + +import ( + "entgo.io/ent/dialect/entsql" + "entgo.io/ent/dialect/sql/schema" + "entgo.io/ent/schema/field" +) + +var ( + // AdministrativeAreasColumns holds the columns for the "administrative_areas" table. + AdministrativeAreasColumns = []*schema.Column{ + {Name: "id", Type: field.TypeUUID}, + {Name: "source", Type: field.TypeString, Size: 40}, + {Name: "source_version", Type: field.TypeString, Size: 20}, + {Name: "external_code", Type: field.TypeString, Size: 32}, + {Name: "kind", Type: field.TypeString, Size: 20}, + {Name: "name", Type: field.TypeString, Size: 160}, + {Name: "active", Type: field.TypeBool, Default: true}, + {Name: "created_at", Type: field.TypeTime}, + {Name: "updated_at", Type: field.TypeTime}, + {Name: "parent_id", Type: field.TypeUUID, Nullable: true}, + } + // AdministrativeAreasTable holds the schema information for the "administrative_areas" table. + AdministrativeAreasTable = &schema.Table{ + Name: "administrative_areas", + Columns: AdministrativeAreasColumns, + PrimaryKey: []*schema.Column{AdministrativeAreasColumns[0]}, + ForeignKeys: []*schema.ForeignKey{ + { + Symbol: "administrative_areas_administrative_areas_children", + Columns: []*schema.Column{AdministrativeAreasColumns[9]}, + RefColumns: []*schema.Column{AdministrativeAreasColumns[0]}, + OnDelete: schema.SetNull, + }, + }, + Indexes: []*schema.Index{ + { + Name: "administrativearea_source_external_code", + Unique: true, + Columns: []*schema.Column{AdministrativeAreasColumns[1], AdministrativeAreasColumns[3]}, + }, + { + Name: "administrativearea_parent_id_kind", + Unique: false, + Columns: []*schema.Column{AdministrativeAreasColumns[9], AdministrativeAreasColumns[4]}, + }, + }, + } + // ContactRevealDailyLimitsColumns holds the columns for the "contact_reveal_daily_limits" table. + ContactRevealDailyLimitsColumns = []*schema.Column{ + {Name: "id", Type: field.TypeUUID}, + {Name: "customer_internal_user_id", Type: field.TypeUUID}, + {Name: "utc_day", Type: field.TypeTime}, + {Name: "successful_count", Type: field.TypeInt, Default: 0}, + {Name: "created_at", Type: field.TypeTime}, + {Name: "updated_at", Type: field.TypeTime}, + } + // ContactRevealDailyLimitsTable holds the schema information for the "contact_reveal_daily_limits" table. + ContactRevealDailyLimitsTable = &schema.Table{ + Name: "contact_reveal_daily_limits", + Columns: ContactRevealDailyLimitsColumns, + PrimaryKey: []*schema.Column{ContactRevealDailyLimitsColumns[0]}, + Indexes: []*schema.Index{ + { + Name: "contactrevealdailylimit_customer_internal_user_id_utc_day", + Unique: true, + Columns: []*schema.Column{ContactRevealDailyLimitsColumns[1], ContactRevealDailyLimitsColumns[2]}, + }, + }, + } + // ContactRevealEventsColumns holds the columns for the "contact_reveal_events" table. + ContactRevealEventsColumns = []*schema.Column{ + {Name: "id", Type: field.TypeUUID}, + {Name: "customer_internal_user_id", Type: field.TypeUUID}, + {Name: "provider_internal_user_id", Type: field.TypeUUID}, + {Name: "listing_id", Type: field.TypeUUID}, + {Name: "channel", Type: field.TypeEnum, Enums: []string{"phone", "whatsapp"}}, + {Name: "utc_day", Type: field.TypeTime}, + {Name: "revealed_at", Type: field.TypeTime}, + } + // ContactRevealEventsTable holds the schema information for the "contact_reveal_events" table. + ContactRevealEventsTable = &schema.Table{ + Name: "contact_reveal_events", + Columns: ContactRevealEventsColumns, + PrimaryKey: []*schema.Column{ContactRevealEventsColumns[0]}, + Indexes: []*schema.Index{ + { + Name: "contactrevealevent_customer_internal_user_id_listing_id_channel_utc_day", + Unique: true, + Columns: []*schema.Column{ContactRevealEventsColumns[1], ContactRevealEventsColumns[3], ContactRevealEventsColumns[4], ContactRevealEventsColumns[5]}, + }, + }, + } + // InternalUsersColumns holds the columns for the "internal_users" table. + InternalUsersColumns = []*schema.Column{ + {Name: "id", Type: field.TypeUUID}, + {Name: "clerk_subject", Type: field.TypeString, Unique: true, Size: 255}, + {Name: "created_at", Type: field.TypeTime}, + {Name: "updated_at", Type: field.TypeTime}, + } + // InternalUsersTable holds the schema information for the "internal_users" table. + InternalUsersTable = &schema.Table{ + Name: "internal_users", + Columns: InternalUsersColumns, + PrimaryKey: []*schema.Column{InternalUsersColumns[0]}, + } + // ListingsColumns holds the columns for the "listings" table. + ListingsColumns = []*schema.Column{ + {Name: "id", Type: field.TypeUUID}, + {Name: "internal_user_id", Type: field.TypeUUID}, + {Name: "category_id", Type: field.TypeUUID}, + {Name: "primary_locality_id", Type: field.TypeUUID}, + {Name: "title", Type: field.TypeString, Size: 140}, + {Name: "description", Type: field.TypeString, Size: 4000}, + {Name: "price_type", Type: field.TypeEnum, Enums: []string{"fixed", "hourly", "daily", "quote", "negotiable"}}, + {Name: "price_minor", Type: field.TypeInt, Nullable: true}, + {Name: "currency", Type: field.TypeString, Size: 3, Default: "EUR"}, + {Name: "travels_to_customer", Type: field.TypeBool, Default: false}, + {Name: "receives_customer", Type: field.TypeBool, Default: false}, + {Name: "remote_services", Type: field.TypeBool, Default: false}, + {Name: "state", Type: field.TypeEnum, Enums: []string{"draft", "pending_review", "active", "rejected", "paused", "archived"}, Default: "draft"}, + {Name: "revision", Type: field.TypeInt, Default: 1}, + {Name: "created_at", Type: field.TypeTime}, + {Name: "updated_at", Type: field.TypeTime}, + } + // ListingsTable holds the schema information for the "listings" table. + ListingsTable = &schema.Table{ + Name: "listings", + Columns: ListingsColumns, + PrimaryKey: []*schema.Column{ListingsColumns[0]}, + } + // ListingEventsColumns holds the columns for the "listing_events" table. + ListingEventsColumns = []*schema.Column{ + {Name: "id", Type: field.TypeUUID}, + {Name: "listing_id", Type: field.TypeUUID}, + {Name: "actor_internal_user_id", Type: field.TypeUUID}, + {Name: "event_type", Type: field.TypeEnum, Enums: []string{"created", "updated", "submitted", "approved", "rejected", "paused", "archived"}}, + {Name: "from_state", Type: field.TypeString, Nullable: true, Size: 32}, + {Name: "to_state", Type: field.TypeString, Size: 32}, + {Name: "revision", Type: field.TypeInt}, + {Name: "reason", Type: field.TypeString, Nullable: true, Size: 500}, + {Name: "created_at", Type: field.TypeTime}, + } + // ListingEventsTable holds the schema information for the "listing_events" table. + ListingEventsTable = &schema.Table{ + Name: "listing_events", + Columns: ListingEventsColumns, + PrimaryKey: []*schema.Column{ListingEventsColumns[0]}, + } + // ListingMediaColumns holds the columns for the "listing_media" table. + ListingMediaColumns = []*schema.Column{ + {Name: "id", Type: field.TypeUUID}, + {Name: "listing_id", Type: field.TypeUUID}, + {Name: "ordinal", Type: field.TypeInt}, + {Name: "content_type", Type: field.TypeString, Size: 100}, + {Name: "byte_size", Type: field.TypeInt64}, + {Name: "checksum_sha256", Type: field.TypeString, Size: 64}, + {Name: "object_reference", Type: field.TypeString, Size: 512}, + {Name: "state", Type: field.TypeEnum, Enums: []string{"pending_upload", "ready", "deleted"}, Default: "pending_upload"}, + {Name: "created_at", Type: field.TypeTime}, + {Name: "updated_at", Type: field.TypeTime}, + } + // ListingMediaTable holds the schema information for the "listing_media" table. + ListingMediaTable = &schema.Table{ + Name: "listing_media", + Columns: ListingMediaColumns, + PrimaryKey: []*schema.Column{ListingMediaColumns[0]}, + } + // LocalitiesColumns holds the columns for the "localities" table. + LocalitiesColumns = []*schema.Column{ + {Name: "id", Type: field.TypeUUID}, + {Name: "slug", Type: field.TypeString, Unique: true, Size: 100}, + {Name: "name", Type: field.TypeString, Size: 160}, + {Name: "source", Type: field.TypeString, Size: 40}, + {Name: "source_element_id", Type: field.TypeString, Unique: true, Size: 32}, + {Name: "source_version", Type: field.TypeString, Size: 20}, + {Name: "source_retrieved_at", Type: field.TypeTime}, + {Name: "latitude", Type: field.TypeFloat64}, + {Name: "longitude", Type: field.TypeFloat64}, + {Name: "active", Type: field.TypeBool, Default: true}, + {Name: "created_at", Type: field.TypeTime}, + {Name: "updated_at", Type: field.TypeTime}, + {Name: "parent_parish_id", Type: field.TypeUUID}, + } + // LocalitiesTable holds the schema information for the "localities" table. + LocalitiesTable = &schema.Table{ + Name: "localities", + Columns: LocalitiesColumns, + PrimaryKey: []*schema.Column{LocalitiesColumns[0]}, + ForeignKeys: []*schema.ForeignKey{ + { + Symbol: "localities_administrative_areas_localities", + Columns: []*schema.Column{LocalitiesColumns[12]}, + RefColumns: []*schema.Column{AdministrativeAreasColumns[0]}, + OnDelete: schema.NoAction, + }, + }, + } + // PlatformRolesColumns holds the columns for the "platform_roles" table. + PlatformRolesColumns = []*schema.Column{ + {Name: "id", Type: field.TypeUUID}, + {Name: "internal_user_id", Type: field.TypeUUID}, + {Name: "role", Type: field.TypeString, Size: 20}, + {Name: "granted_at", Type: field.TypeTime}, + } + // PlatformRolesTable holds the schema information for the "platform_roles" table. + PlatformRolesTable = &schema.Table{ + Name: "platform_roles", + Columns: PlatformRolesColumns, + PrimaryKey: []*schema.Column{PlatformRolesColumns[0]}, + Indexes: []*schema.Index{ + { + Name: "platformrole_internal_user_id_role", + Unique: true, + Columns: []*schema.Column{PlatformRolesColumns[1], PlatformRolesColumns[2]}, + }, + }, + } + // ProviderContactChannelsColumns holds the columns for the "provider_contact_channels" table. + ProviderContactChannelsColumns = []*schema.Column{ + {Name: "id", Type: field.TypeUUID}, + {Name: "internal_user_id", Type: field.TypeUUID}, + {Name: "channel", Type: field.TypeEnum, Enums: []string{"phone", "whatsapp"}}, + {Name: "ciphertext", Type: field.TypeBytes}, + {Name: "nonce", Type: field.TypeBytes}, + {Name: "key_version", Type: field.TypeString, Size: 32}, + {Name: "enabled", Type: field.TypeBool, Default: false}, + {Name: "reveal_consent", Type: field.TypeBool, Default: false}, + {Name: "created_at", Type: field.TypeTime}, + {Name: "updated_at", Type: field.TypeTime}, + } + // ProviderContactChannelsTable holds the schema information for the "provider_contact_channels" table. + ProviderContactChannelsTable = &schema.Table{ + Name: "provider_contact_channels", + Columns: ProviderContactChannelsColumns, + PrimaryKey: []*schema.Column{ProviderContactChannelsColumns[0]}, + Indexes: []*schema.Index{ + { + Name: "providercontactchannel_internal_user_id_channel", + Unique: true, + Columns: []*schema.Column{ProviderContactChannelsColumns[1], ProviderContactChannelsColumns[2]}, + }, + }, + } + // ProviderProfilesColumns holds the columns for the "provider_profiles" table. + ProviderProfilesColumns = []*schema.Column{ + {Name: "internal_user_id", Type: field.TypeUUID}, + {Name: "display_name", Type: field.TypeString, Size: 100}, + {Name: "provider_type", Type: field.TypeString, Size: 20}, + {Name: "bio", Type: field.TypeString, Size: 1000}, + {Name: "primary_locality_id", Type: field.TypeUUID}, + {Name: "max_travel_distance_km", Type: field.TypeInt}, + {Name: "travels_to_customer", Type: field.TypeBool, Default: false}, + {Name: "receives_customer", Type: field.TypeBool, Default: false}, + {Name: "remote_services", Type: field.TypeBool, Default: false}, + {Name: "created_at", Type: field.TypeTime}, + {Name: "updated_at", Type: field.TypeTime}, + } + // ProviderProfilesTable holds the schema information for the "provider_profiles" table. + ProviderProfilesTable = &schema.Table{ + Name: "provider_profiles", + Columns: ProviderProfilesColumns, + PrimaryKey: []*schema.Column{ProviderProfilesColumns[0]}, + } + // ProviderServiceLocalitiesColumns holds the columns for the "provider_service_localities" table. + ProviderServiceLocalitiesColumns = []*schema.Column{ + {Name: "internal_user_id", Type: field.TypeUUID}, + {Name: "locality_id", Type: field.TypeUUID}, + } + // ProviderServiceLocalitiesTable holds the schema information for the "provider_service_localities" table. + ProviderServiceLocalitiesTable = &schema.Table{ + Name: "provider_service_localities", + Columns: ProviderServiceLocalitiesColumns, + PrimaryKey: []*schema.Column{ProviderServiceLocalitiesColumns[0], ProviderServiceLocalitiesColumns[1]}, + ForeignKeys: []*schema.ForeignKey{ + { + Symbol: "provider_service_localities_provider_profiles_profile", + Columns: []*schema.Column{ProviderServiceLocalitiesColumns[0]}, + RefColumns: []*schema.Column{ProviderProfilesColumns[0]}, + OnDelete: schema.NoAction, + }, + { + Symbol: "provider_service_localities_localities_locality", + Columns: []*schema.Column{ProviderServiceLocalitiesColumns[1]}, + RefColumns: []*schema.Column{LocalitiesColumns[0]}, + OnDelete: schema.NoAction, + }, + }, + } + // ProviderSpokenLanguagesColumns holds the columns for the "provider_spoken_languages" table. + ProviderSpokenLanguagesColumns = []*schema.Column{ + {Name: "internal_user_id", Type: field.TypeUUID}, + {Name: "language_code", Type: field.TypeString, Size: 10}, + } + // ProviderSpokenLanguagesTable holds the schema information for the "provider_spoken_languages" table. + ProviderSpokenLanguagesTable = &schema.Table{ + Name: "provider_spoken_languages", + Columns: ProviderSpokenLanguagesColumns, + PrimaryKey: []*schema.Column{ProviderSpokenLanguagesColumns[0], ProviderSpokenLanguagesColumns[1]}, + ForeignKeys: []*schema.ForeignKey{ + { + Symbol: "provider_spoken_languages_provider_profiles_profile", + Columns: []*schema.Column{ProviderSpokenLanguagesColumns[0]}, + RefColumns: []*schema.Column{ProviderProfilesColumns[0]}, + OnDelete: schema.NoAction, + }, + { + Symbol: "provider_spoken_languages_spoken_languages_language", + Columns: []*schema.Column{ProviderSpokenLanguagesColumns[1]}, + RefColumns: []*schema.Column{SpokenLanguagesColumns[0]}, + OnDelete: schema.NoAction, + }, + }, + } + // ServiceCategoriesColumns holds the columns for the "service_categories" table. + ServiceCategoriesColumns = []*schema.Column{ + {Name: "id", Type: field.TypeUUID}, + {Name: "slug", Type: field.TypeString, Unique: true, Size: 80}, + {Name: "active", Type: field.TypeBool, Default: true}, + {Name: "sort_order", Type: field.TypeInt}, + {Name: "created_at", Type: field.TypeTime}, + {Name: "updated_at", Type: field.TypeTime}, + {Name: "parent_id", Type: field.TypeUUID, Nullable: true}, + } + // ServiceCategoriesTable holds the schema information for the "service_categories" table. + ServiceCategoriesTable = &schema.Table{ + Name: "service_categories", + Columns: ServiceCategoriesColumns, + PrimaryKey: []*schema.Column{ServiceCategoriesColumns[0]}, + ForeignKeys: []*schema.ForeignKey{ + { + Symbol: "service_categories_service_categories_children", + Columns: []*schema.Column{ServiceCategoriesColumns[6]}, + RefColumns: []*schema.Column{ServiceCategoriesColumns[0]}, + OnDelete: schema.SetNull, + }, + }, + Indexes: []*schema.Index{ + { + Name: "servicecategory_parent_id_sort_order", + Unique: false, + Columns: []*schema.Column{ServiceCategoriesColumns[6], ServiceCategoriesColumns[3]}, + }, + }, + } + // ServiceCategoryTranslationsColumns holds the columns for the "service_category_translations" table. + ServiceCategoryTranslationsColumns = []*schema.Column{ + {Name: "name", Type: field.TypeString, Size: 120}, + {Name: "description", Type: field.TypeString, Nullable: true, Size: 500}, + {Name: "category_id", Type: field.TypeUUID}, + {Name: "locale", Type: field.TypeString, Size: 10}, + } + // ServiceCategoryTranslationsTable holds the schema information for the "service_category_translations" table. + ServiceCategoryTranslationsTable = &schema.Table{ + Name: "service_category_translations", + Columns: ServiceCategoryTranslationsColumns, + PrimaryKey: []*schema.Column{ServiceCategoryTranslationsColumns[2], ServiceCategoryTranslationsColumns[3]}, + ForeignKeys: []*schema.ForeignKey{ + { + Symbol: "service_category_translations_service_categories_category", + Columns: []*schema.Column{ServiceCategoryTranslationsColumns[2]}, + RefColumns: []*schema.Column{ServiceCategoriesColumns[0]}, + OnDelete: schema.NoAction, + }, + { + Symbol: "service_category_translations_supported_locales_locale_record", + Columns: []*schema.Column{ServiceCategoryTranslationsColumns[3]}, + RefColumns: []*schema.Column{SupportedLocalesColumns[0]}, + OnDelete: schema.NoAction, + }, + }, + } + // SpokenLanguagesColumns holds the columns for the "spoken_languages" table. + SpokenLanguagesColumns = []*schema.Column{ + {Name: "id", Type: field.TypeString, Size: 10}, + {Name: "active", Type: field.TypeBool, Default: true}, + {Name: "sort_order", Type: field.TypeInt}, + } + // SpokenLanguagesTable holds the schema information for the "spoken_languages" table. + SpokenLanguagesTable = &schema.Table{ + Name: "spoken_languages", + Columns: SpokenLanguagesColumns, + PrimaryKey: []*schema.Column{SpokenLanguagesColumns[0]}, + } + // SpokenLanguageTranslationsColumns holds the columns for the "spoken_language_translations" table. + SpokenLanguageTranslationsColumns = []*schema.Column{ + {Name: "name", Type: field.TypeString, Size: 80}, + {Name: "language_code", Type: field.TypeString, Size: 10}, + {Name: "locale", Type: field.TypeString, Size: 10}, + } + // SpokenLanguageTranslationsTable holds the schema information for the "spoken_language_translations" table. + SpokenLanguageTranslationsTable = &schema.Table{ + Name: "spoken_language_translations", + Columns: SpokenLanguageTranslationsColumns, + PrimaryKey: []*schema.Column{SpokenLanguageTranslationsColumns[1], SpokenLanguageTranslationsColumns[2]}, + ForeignKeys: []*schema.ForeignKey{ + { + Symbol: "spoken_language_translations_spoken_languages_language", + Columns: []*schema.Column{SpokenLanguageTranslationsColumns[1]}, + RefColumns: []*schema.Column{SpokenLanguagesColumns[0]}, + OnDelete: schema.NoAction, + }, + { + Symbol: "spoken_language_translations_supported_locales_locale_record", + Columns: []*schema.Column{SpokenLanguageTranslationsColumns[2]}, + RefColumns: []*schema.Column{SupportedLocalesColumns[0]}, + OnDelete: schema.NoAction, + }, + }, + } + // SupportedLocalesColumns holds the columns for the "supported_locales" table. + SupportedLocalesColumns = []*schema.Column{ + {Name: "id", Type: field.TypeString, Size: 10}, + {Name: "active", Type: field.TypeBool, Default: true}, + {Name: "sort_order", Type: field.TypeInt}, + } + // SupportedLocalesTable holds the schema information for the "supported_locales" table. + SupportedLocalesTable = &schema.Table{ + Name: "supported_locales", + Columns: SupportedLocalesColumns, + PrimaryKey: []*schema.Column{SupportedLocalesColumns[0]}, + } + // UserAccountsColumns holds the columns for the "user_accounts" table. + UserAccountsColumns = []*schema.Column{ + {Name: "internal_user_id", Type: field.TypeUUID}, + {Name: "provider_enabled", Type: field.TypeBool, Default: false}, + {Name: "onboarding_completed_at", Type: field.TypeTime}, + {Name: "created_at", Type: field.TypeTime}, + {Name: "updated_at", Type: field.TypeTime}, + } + // UserAccountsTable holds the schema information for the "user_accounts" table. + UserAccountsTable = &schema.Table{ + Name: "user_accounts", + Columns: UserAccountsColumns, + PrimaryKey: []*schema.Column{UserAccountsColumns[0]}, + } + // Tables holds all the tables in the schema. + Tables = []*schema.Table{ + AdministrativeAreasTable, + ContactRevealDailyLimitsTable, + ContactRevealEventsTable, + InternalUsersTable, + ListingsTable, + ListingEventsTable, + ListingMediaTable, + LocalitiesTable, + PlatformRolesTable, + ProviderContactChannelsTable, + ProviderProfilesTable, + ProviderServiceLocalitiesTable, + ProviderSpokenLanguagesTable, + ServiceCategoriesTable, + ServiceCategoryTranslationsTable, + SpokenLanguagesTable, + SpokenLanguageTranslationsTable, + SupportedLocalesTable, + UserAccountsTable, + } +) + +func init() { + AdministrativeAreasTable.ForeignKeys[0].RefTable = AdministrativeAreasTable + AdministrativeAreasTable.Annotation = &entsql.Annotation{ + Table: "administrative_areas", + } + ContactRevealDailyLimitsTable.Annotation = &entsql.Annotation{ + Table: "contact_reveal_daily_limits", + } + ContactRevealEventsTable.Annotation = &entsql.Annotation{ + Table: "contact_reveal_events", + } + InternalUsersTable.Annotation = &entsql.Annotation{ + Table: "internal_users", + } + ListingsTable.Annotation = &entsql.Annotation{ + Table: "listings", + } + ListingEventsTable.Annotation = &entsql.Annotation{ + Table: "listing_events", + } + ListingMediaTable.Annotation = &entsql.Annotation{ + Table: "listing_media", + } + LocalitiesTable.ForeignKeys[0].RefTable = AdministrativeAreasTable + LocalitiesTable.Annotation = &entsql.Annotation{ + Table: "localities", + } + PlatformRolesTable.Annotation = &entsql.Annotation{ + Table: "platform_roles", + } + ProviderContactChannelsTable.Annotation = &entsql.Annotation{ + Table: "provider_contact_channels", + } + ProviderProfilesTable.Annotation = &entsql.Annotation{ + Table: "provider_profiles", + } + ProviderServiceLocalitiesTable.ForeignKeys[0].RefTable = ProviderProfilesTable + ProviderServiceLocalitiesTable.ForeignKeys[1].RefTable = LocalitiesTable + ProviderServiceLocalitiesTable.Annotation = &entsql.Annotation{ + Table: "provider_service_localities", + } + ProviderSpokenLanguagesTable.ForeignKeys[0].RefTable = ProviderProfilesTable + ProviderSpokenLanguagesTable.ForeignKeys[1].RefTable = SpokenLanguagesTable + ProviderSpokenLanguagesTable.Annotation = &entsql.Annotation{ + Table: "provider_spoken_languages", + } + ServiceCategoriesTable.ForeignKeys[0].RefTable = ServiceCategoriesTable + ServiceCategoriesTable.Annotation = &entsql.Annotation{ + Table: "service_categories", + } + ServiceCategoryTranslationsTable.ForeignKeys[0].RefTable = ServiceCategoriesTable + ServiceCategoryTranslationsTable.ForeignKeys[1].RefTable = SupportedLocalesTable + ServiceCategoryTranslationsTable.Annotation = &entsql.Annotation{ + Table: "service_category_translations", + } + SpokenLanguagesTable.Annotation = &entsql.Annotation{ + Table: "spoken_languages", + } + SpokenLanguageTranslationsTable.ForeignKeys[0].RefTable = SpokenLanguagesTable + SpokenLanguageTranslationsTable.ForeignKeys[1].RefTable = SupportedLocalesTable + SpokenLanguageTranslationsTable.Annotation = &entsql.Annotation{ + Table: "spoken_language_translations", + } + SupportedLocalesTable.Annotation = &entsql.Annotation{ + Table: "supported_locales", + } + UserAccountsTable.Annotation = &entsql.Annotation{ + Table: "user_accounts", + } +} diff --git a/backend/ent/mutation.go b/backend/ent/mutation.go new file mode 100644 index 0000000..c6ada9c --- /dev/null +++ b/backend/ent/mutation.go @@ -0,0 +1,13160 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "context" + "errors" + "fmt" + "sync" + "time" + + "entgo.io/ent" + "entgo.io/ent/dialect/sql" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/administrativearea" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/contactrevealdailylimit" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/contactrevealevent" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/internaluser" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/listing" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/listingevent" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/listingmedia" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/locality" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/platformrole" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/predicate" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/providercontactchannel" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/providerprofile" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/providerservicelocality" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/providerspokenlanguage" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/servicecategory" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/servicecategorytranslation" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/spokenlanguage" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/spokenlanguagetranslation" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/supportedlocale" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/useraccount" + "github.com/google/uuid" +) + +const ( + // Operation types. + OpCreate = ent.OpCreate + OpDelete = ent.OpDelete + OpDeleteOne = ent.OpDeleteOne + OpUpdate = ent.OpUpdate + OpUpdateOne = ent.OpUpdateOne + + // Node types. + TypeAdministrativeArea = "AdministrativeArea" + TypeContactRevealDailyLimit = "ContactRevealDailyLimit" + TypeContactRevealEvent = "ContactRevealEvent" + TypeInternalUser = "InternalUser" + TypeListing = "Listing" + TypeListingEvent = "ListingEvent" + TypeListingMedia = "ListingMedia" + TypeLocality = "Locality" + TypePlatformRole = "PlatformRole" + TypeProviderContactChannel = "ProviderContactChannel" + TypeProviderProfile = "ProviderProfile" + TypeProviderServiceLocality = "ProviderServiceLocality" + TypeProviderSpokenLanguage = "ProviderSpokenLanguage" + TypeServiceCategory = "ServiceCategory" + TypeServiceCategoryTranslation = "ServiceCategoryTranslation" + TypeSpokenLanguage = "SpokenLanguage" + TypeSpokenLanguageTranslation = "SpokenLanguageTranslation" + TypeSupportedLocale = "SupportedLocale" + TypeUserAccount = "UserAccount" +) + +// AdministrativeAreaMutation represents an operation that mutates the AdministrativeArea nodes in the graph. +type AdministrativeAreaMutation struct { + config + op Op + typ string + id *uuid.UUID + source *string + source_version *string + external_code *string + kind *string + name *string + active *bool + created_at *time.Time + updated_at *time.Time + clearedFields map[string]struct{} + children map[uuid.UUID]struct{} + removedchildren map[uuid.UUID]struct{} + clearedchildren bool + parent *uuid.UUID + clearedparent bool + localities map[uuid.UUID]struct{} + removedlocalities map[uuid.UUID]struct{} + clearedlocalities bool + done bool + oldValue func(context.Context) (*AdministrativeArea, error) + predicates []predicate.AdministrativeArea +} + +var _ ent.Mutation = (*AdministrativeAreaMutation)(nil) + +// administrativeareaOption allows management of the mutation configuration using functional options. +type administrativeareaOption func(*AdministrativeAreaMutation) + +// newAdministrativeAreaMutation creates new mutation for the AdministrativeArea entity. +func newAdministrativeAreaMutation(c config, op Op, opts ...administrativeareaOption) *AdministrativeAreaMutation { + m := &AdministrativeAreaMutation{ + config: c, + op: op, + typ: TypeAdministrativeArea, + clearedFields: make(map[string]struct{}), + } + for _, opt := range opts { + opt(m) + } + return m +} + +// withAdministrativeAreaID sets the ID field of the mutation. +func withAdministrativeAreaID(id uuid.UUID) administrativeareaOption { + return func(m *AdministrativeAreaMutation) { + var ( + err error + once sync.Once + value *AdministrativeArea + ) + m.oldValue = func(ctx context.Context) (*AdministrativeArea, error) { + once.Do(func() { + if m.done { + err = errors.New("querying old values post mutation is not allowed") + } else { + value, err = m.Client().AdministrativeArea.Get(ctx, id) + } + }) + return value, err + } + m.id = &id + } +} + +// withAdministrativeArea sets the old AdministrativeArea of the mutation. +func withAdministrativeArea(node *AdministrativeArea) administrativeareaOption { + return func(m *AdministrativeAreaMutation) { + m.oldValue = func(context.Context) (*AdministrativeArea, 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 AdministrativeAreaMutation) 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 AdministrativeAreaMutation) 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 AdministrativeArea entities. +func (m *AdministrativeAreaMutation) 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 *AdministrativeAreaMutation) 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 *AdministrativeAreaMutation) 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().AdministrativeArea.Query().Where(m.predicates...).IDs(ctx) + default: + return nil, fmt.Errorf("IDs is not allowed on %s operations", m.op) + } +} + +// SetSource sets the "source" field. +func (m *AdministrativeAreaMutation) SetSource(s string) { + m.source = &s +} + +// Source returns the value of the "source" field in the mutation. +func (m *AdministrativeAreaMutation) Source() (r string, exists bool) { + v := m.source + if v == nil { + return + } + return *v, true +} + +// OldSource returns the old "source" field's value of the AdministrativeArea entity. +// If the AdministrativeArea object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *AdministrativeAreaMutation) OldSource(ctx context.Context) (v string, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldSource is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldSource requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldSource: %w", err) + } + return oldValue.Source, nil +} + +// ResetSource resets all changes to the "source" field. +func (m *AdministrativeAreaMutation) ResetSource() { + m.source = nil +} + +// SetSourceVersion sets the "source_version" field. +func (m *AdministrativeAreaMutation) SetSourceVersion(s string) { + m.source_version = &s +} + +// SourceVersion returns the value of the "source_version" field in the mutation. +func (m *AdministrativeAreaMutation) SourceVersion() (r string, exists bool) { + v := m.source_version + if v == nil { + return + } + return *v, true +} + +// OldSourceVersion returns the old "source_version" field's value of the AdministrativeArea entity. +// If the AdministrativeArea object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *AdministrativeAreaMutation) OldSourceVersion(ctx context.Context) (v string, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldSourceVersion is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldSourceVersion requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldSourceVersion: %w", err) + } + return oldValue.SourceVersion, nil +} + +// ResetSourceVersion resets all changes to the "source_version" field. +func (m *AdministrativeAreaMutation) ResetSourceVersion() { + m.source_version = nil +} + +// SetExternalCode sets the "external_code" field. +func (m *AdministrativeAreaMutation) SetExternalCode(s string) { + m.external_code = &s +} + +// ExternalCode returns the value of the "external_code" field in the mutation. +func (m *AdministrativeAreaMutation) ExternalCode() (r string, exists bool) { + v := m.external_code + if v == nil { + return + } + return *v, true +} + +// OldExternalCode returns the old "external_code" field's value of the AdministrativeArea entity. +// If the AdministrativeArea object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *AdministrativeAreaMutation) OldExternalCode(ctx context.Context) (v string, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldExternalCode is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldExternalCode requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldExternalCode: %w", err) + } + return oldValue.ExternalCode, nil +} + +// ResetExternalCode resets all changes to the "external_code" field. +func (m *AdministrativeAreaMutation) ResetExternalCode() { + m.external_code = nil +} + +// SetKind sets the "kind" field. +func (m *AdministrativeAreaMutation) SetKind(s string) { + m.kind = &s +} + +// Kind returns the value of the "kind" field in the mutation. +func (m *AdministrativeAreaMutation) Kind() (r string, exists bool) { + v := m.kind + if v == nil { + return + } + return *v, true +} + +// OldKind returns the old "kind" field's value of the AdministrativeArea entity. +// If the AdministrativeArea object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *AdministrativeAreaMutation) OldKind(ctx context.Context) (v string, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldKind is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldKind requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldKind: %w", err) + } + return oldValue.Kind, nil +} + +// ResetKind resets all changes to the "kind" field. +func (m *AdministrativeAreaMutation) ResetKind() { + m.kind = nil +} + +// SetName sets the "name" field. +func (m *AdministrativeAreaMutation) SetName(s string) { + m.name = &s +} + +// Name returns the value of the "name" field in the mutation. +func (m *AdministrativeAreaMutation) 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 AdministrativeArea entity. +// If the AdministrativeArea object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *AdministrativeAreaMutation) 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 *AdministrativeAreaMutation) ResetName() { + m.name = nil +} + +// SetParentID sets the "parent_id" field. +func (m *AdministrativeAreaMutation) SetParentID(u uuid.UUID) { + m.parent = &u +} + +// ParentID returns the value of the "parent_id" field in the mutation. +func (m *AdministrativeAreaMutation) ParentID() (r uuid.UUID, exists bool) { + v := m.parent + if v == nil { + return + } + return *v, true +} + +// OldParentID returns the old "parent_id" field's value of the AdministrativeArea entity. +// If the AdministrativeArea object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *AdministrativeAreaMutation) OldParentID(ctx context.Context) (v *uuid.UUID, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldParentID is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldParentID requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldParentID: %w", err) + } + return oldValue.ParentID, nil +} + +// ClearParentID clears the value of the "parent_id" field. +func (m *AdministrativeAreaMutation) ClearParentID() { + m.parent = nil + m.clearedFields[administrativearea.FieldParentID] = struct{}{} +} + +// ParentIDCleared returns if the "parent_id" field was cleared in this mutation. +func (m *AdministrativeAreaMutation) ParentIDCleared() bool { + _, ok := m.clearedFields[administrativearea.FieldParentID] + return ok +} + +// ResetParentID resets all changes to the "parent_id" field. +func (m *AdministrativeAreaMutation) ResetParentID() { + m.parent = nil + delete(m.clearedFields, administrativearea.FieldParentID) +} + +// SetActive sets the "active" field. +func (m *AdministrativeAreaMutation) SetActive(b bool) { + m.active = &b +} + +// Active returns the value of the "active" field in the mutation. +func (m *AdministrativeAreaMutation) Active() (r bool, exists bool) { + v := m.active + if v == nil { + return + } + return *v, true +} + +// OldActive returns the old "active" field's value of the AdministrativeArea entity. +// If the AdministrativeArea object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *AdministrativeAreaMutation) OldActive(ctx context.Context) (v bool, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldActive is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldActive requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldActive: %w", err) + } + return oldValue.Active, nil +} + +// ResetActive resets all changes to the "active" field. +func (m *AdministrativeAreaMutation) ResetActive() { + m.active = nil +} + +// SetCreatedAt sets the "created_at" field. +func (m *AdministrativeAreaMutation) SetCreatedAt(t time.Time) { + m.created_at = &t +} + +// CreatedAt returns the value of the "created_at" field in the mutation. +func (m *AdministrativeAreaMutation) CreatedAt() (r time.Time, exists bool) { + v := m.created_at + if v == nil { + return + } + return *v, true +} + +// OldCreatedAt returns the old "created_at" field's value of the AdministrativeArea entity. +// If the AdministrativeArea object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *AdministrativeAreaMutation) OldCreatedAt(ctx context.Context) (v time.Time, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldCreatedAt is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldCreatedAt requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldCreatedAt: %w", err) + } + return oldValue.CreatedAt, nil +} + +// ResetCreatedAt resets all changes to the "created_at" field. +func (m *AdministrativeAreaMutation) ResetCreatedAt() { + m.created_at = nil +} + +// SetUpdatedAt sets the "updated_at" field. +func (m *AdministrativeAreaMutation) SetUpdatedAt(t time.Time) { + m.updated_at = &t +} + +// UpdatedAt returns the value of the "updated_at" field in the mutation. +func (m *AdministrativeAreaMutation) UpdatedAt() (r time.Time, exists bool) { + v := m.updated_at + if v == nil { + return + } + return *v, true +} + +// OldUpdatedAt returns the old "updated_at" field's value of the AdministrativeArea entity. +// If the AdministrativeArea object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *AdministrativeAreaMutation) OldUpdatedAt(ctx context.Context) (v time.Time, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldUpdatedAt is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldUpdatedAt requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldUpdatedAt: %w", err) + } + return oldValue.UpdatedAt, nil +} + +// ResetUpdatedAt resets all changes to the "updated_at" field. +func (m *AdministrativeAreaMutation) ResetUpdatedAt() { + m.updated_at = nil +} + +// AddChildIDs adds the "children" edge to the AdministrativeArea entity by ids. +func (m *AdministrativeAreaMutation) AddChildIDs(ids ...uuid.UUID) { + if m.children == nil { + m.children = make(map[uuid.UUID]struct{}) + } + for i := range ids { + m.children[ids[i]] = struct{}{} + } +} + +// ClearChildren clears the "children" edge to the AdministrativeArea entity. +func (m *AdministrativeAreaMutation) ClearChildren() { + m.clearedchildren = true +} + +// ChildrenCleared reports if the "children" edge to the AdministrativeArea entity was cleared. +func (m *AdministrativeAreaMutation) ChildrenCleared() bool { + return m.clearedchildren +} + +// RemoveChildIDs removes the "children" edge to the AdministrativeArea entity by IDs. +func (m *AdministrativeAreaMutation) RemoveChildIDs(ids ...uuid.UUID) { + if m.removedchildren == nil { + m.removedchildren = make(map[uuid.UUID]struct{}) + } + for i := range ids { + delete(m.children, ids[i]) + m.removedchildren[ids[i]] = struct{}{} + } +} + +// RemovedChildren returns the removed IDs of the "children" edge to the AdministrativeArea entity. +func (m *AdministrativeAreaMutation) RemovedChildrenIDs() (ids []uuid.UUID) { + for id := range m.removedchildren { + ids = append(ids, id) + } + return +} + +// ChildrenIDs returns the "children" edge IDs in the mutation. +func (m *AdministrativeAreaMutation) ChildrenIDs() (ids []uuid.UUID) { + for id := range m.children { + ids = append(ids, id) + } + return +} + +// ResetChildren resets all changes to the "children" edge. +func (m *AdministrativeAreaMutation) ResetChildren() { + m.children = nil + m.clearedchildren = false + m.removedchildren = nil +} + +// ClearParent clears the "parent" edge to the AdministrativeArea entity. +func (m *AdministrativeAreaMutation) ClearParent() { + m.clearedparent = true + m.clearedFields[administrativearea.FieldParentID] = struct{}{} +} + +// ParentCleared reports if the "parent" edge to the AdministrativeArea entity was cleared. +func (m *AdministrativeAreaMutation) ParentCleared() bool { + return m.ParentIDCleared() || m.clearedparent +} + +// ParentIDs returns the "parent" edge IDs in the mutation. +// Note that IDs always returns len(IDs) <= 1 for unique edges, and you should use +// ParentID instead. It exists only for internal usage by the builders. +func (m *AdministrativeAreaMutation) ParentIDs() (ids []uuid.UUID) { + if id := m.parent; id != nil { + ids = append(ids, *id) + } + return +} + +// ResetParent resets all changes to the "parent" edge. +func (m *AdministrativeAreaMutation) ResetParent() { + m.parent = nil + m.clearedparent = false +} + +// AddLocalityIDs adds the "localities" edge to the Locality entity by ids. +func (m *AdministrativeAreaMutation) AddLocalityIDs(ids ...uuid.UUID) { + if m.localities == nil { + m.localities = make(map[uuid.UUID]struct{}) + } + for i := range ids { + m.localities[ids[i]] = struct{}{} + } +} + +// ClearLocalities clears the "localities" edge to the Locality entity. +func (m *AdministrativeAreaMutation) ClearLocalities() { + m.clearedlocalities = true +} + +// LocalitiesCleared reports if the "localities" edge to the Locality entity was cleared. +func (m *AdministrativeAreaMutation) LocalitiesCleared() bool { + return m.clearedlocalities +} + +// RemoveLocalityIDs removes the "localities" edge to the Locality entity by IDs. +func (m *AdministrativeAreaMutation) RemoveLocalityIDs(ids ...uuid.UUID) { + if m.removedlocalities == nil { + m.removedlocalities = make(map[uuid.UUID]struct{}) + } + for i := range ids { + delete(m.localities, ids[i]) + m.removedlocalities[ids[i]] = struct{}{} + } +} + +// RemovedLocalities returns the removed IDs of the "localities" edge to the Locality entity. +func (m *AdministrativeAreaMutation) RemovedLocalitiesIDs() (ids []uuid.UUID) { + for id := range m.removedlocalities { + ids = append(ids, id) + } + return +} + +// LocalitiesIDs returns the "localities" edge IDs in the mutation. +func (m *AdministrativeAreaMutation) LocalitiesIDs() (ids []uuid.UUID) { + for id := range m.localities { + ids = append(ids, id) + } + return +} + +// ResetLocalities resets all changes to the "localities" edge. +func (m *AdministrativeAreaMutation) ResetLocalities() { + m.localities = nil + m.clearedlocalities = false + m.removedlocalities = nil +} + +// Where appends a list predicates to the AdministrativeAreaMutation builder. +func (m *AdministrativeAreaMutation) Where(ps ...predicate.AdministrativeArea) { + m.predicates = append(m.predicates, ps...) +} + +// WhereP appends storage-level predicates to the AdministrativeAreaMutation builder. Using this method, +// users can use type-assertion to append predicates that do not depend on any generated package. +func (m *AdministrativeAreaMutation) WhereP(ps ...func(*sql.Selector)) { + p := make([]predicate.AdministrativeArea, len(ps)) + for i := range ps { + p[i] = ps[i] + } + m.Where(p...) +} + +// Op returns the operation name. +func (m *AdministrativeAreaMutation) Op() Op { + return m.op +} + +// SetOp allows setting the mutation operation. +func (m *AdministrativeAreaMutation) SetOp(op Op) { + m.op = op +} + +// Type returns the node type of this mutation (AdministrativeArea). +func (m *AdministrativeAreaMutation) 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 *AdministrativeAreaMutation) Fields() []string { + fields := make([]string, 0, 9) + if m.source != nil { + fields = append(fields, administrativearea.FieldSource) + } + if m.source_version != nil { + fields = append(fields, administrativearea.FieldSourceVersion) + } + if m.external_code != nil { + fields = append(fields, administrativearea.FieldExternalCode) + } + if m.kind != nil { + fields = append(fields, administrativearea.FieldKind) + } + if m.name != nil { + fields = append(fields, administrativearea.FieldName) + } + if m.parent != nil { + fields = append(fields, administrativearea.FieldParentID) + } + if m.active != nil { + fields = append(fields, administrativearea.FieldActive) + } + if m.created_at != nil { + fields = append(fields, administrativearea.FieldCreatedAt) + } + if m.updated_at != nil { + fields = append(fields, administrativearea.FieldUpdatedAt) + } + 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 *AdministrativeAreaMutation) Field(name string) (ent.Value, bool) { + switch name { + case administrativearea.FieldSource: + return m.Source() + case administrativearea.FieldSourceVersion: + return m.SourceVersion() + case administrativearea.FieldExternalCode: + return m.ExternalCode() + case administrativearea.FieldKind: + return m.Kind() + case administrativearea.FieldName: + return m.Name() + case administrativearea.FieldParentID: + return m.ParentID() + case administrativearea.FieldActive: + return m.Active() + case administrativearea.FieldCreatedAt: + return m.CreatedAt() + case administrativearea.FieldUpdatedAt: + return m.UpdatedAt() + } + 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 *AdministrativeAreaMutation) OldField(ctx context.Context, name string) (ent.Value, error) { + switch name { + case administrativearea.FieldSource: + return m.OldSource(ctx) + case administrativearea.FieldSourceVersion: + return m.OldSourceVersion(ctx) + case administrativearea.FieldExternalCode: + return m.OldExternalCode(ctx) + case administrativearea.FieldKind: + return m.OldKind(ctx) + case administrativearea.FieldName: + return m.OldName(ctx) + case administrativearea.FieldParentID: + return m.OldParentID(ctx) + case administrativearea.FieldActive: + return m.OldActive(ctx) + case administrativearea.FieldCreatedAt: + return m.OldCreatedAt(ctx) + case administrativearea.FieldUpdatedAt: + return m.OldUpdatedAt(ctx) + } + return nil, fmt.Errorf("unknown AdministrativeArea 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 *AdministrativeAreaMutation) SetField(name string, value ent.Value) error { + switch name { + case administrativearea.FieldSource: + v, ok := value.(string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetSource(v) + return nil + case administrativearea.FieldSourceVersion: + v, ok := value.(string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetSourceVersion(v) + return nil + case administrativearea.FieldExternalCode: + v, ok := value.(string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetExternalCode(v) + return nil + case administrativearea.FieldKind: + v, ok := value.(string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetKind(v) + return nil + case administrativearea.FieldName: + v, ok := value.(string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetName(v) + return nil + case administrativearea.FieldParentID: + v, ok := value.(uuid.UUID) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetParentID(v) + return nil + case administrativearea.FieldActive: + v, ok := value.(bool) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetActive(v) + return nil + case administrativearea.FieldCreatedAt: + v, ok := value.(time.Time) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetCreatedAt(v) + return nil + case administrativearea.FieldUpdatedAt: + v, ok := value.(time.Time) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetUpdatedAt(v) + return nil + } + return fmt.Errorf("unknown AdministrativeArea field %s", name) +} + +// AddedFields returns all numeric fields that were incremented/decremented during +// this mutation. +func (m *AdministrativeAreaMutation) 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 *AdministrativeAreaMutation) 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 *AdministrativeAreaMutation) AddField(name string, value ent.Value) error { + switch name { + } + return fmt.Errorf("unknown AdministrativeArea numeric field %s", name) +} + +// ClearedFields returns all nullable fields that were cleared during this +// mutation. +func (m *AdministrativeAreaMutation) ClearedFields() []string { + var fields []string + if m.FieldCleared(administrativearea.FieldParentID) { + fields = append(fields, administrativearea.FieldParentID) + } + return fields +} + +// FieldCleared returns a boolean indicating if a field with the given name was +// cleared in this mutation. +func (m *AdministrativeAreaMutation) 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 *AdministrativeAreaMutation) ClearField(name string) error { + switch name { + case administrativearea.FieldParentID: + m.ClearParentID() + return nil + } + return fmt.Errorf("unknown AdministrativeArea 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 *AdministrativeAreaMutation) ResetField(name string) error { + switch name { + case administrativearea.FieldSource: + m.ResetSource() + return nil + case administrativearea.FieldSourceVersion: + m.ResetSourceVersion() + return nil + case administrativearea.FieldExternalCode: + m.ResetExternalCode() + return nil + case administrativearea.FieldKind: + m.ResetKind() + return nil + case administrativearea.FieldName: + m.ResetName() + return nil + case administrativearea.FieldParentID: + m.ResetParentID() + return nil + case administrativearea.FieldActive: + m.ResetActive() + return nil + case administrativearea.FieldCreatedAt: + m.ResetCreatedAt() + return nil + case administrativearea.FieldUpdatedAt: + m.ResetUpdatedAt() + return nil + } + return fmt.Errorf("unknown AdministrativeArea field %s", name) +} + +// AddedEdges returns all edge names that were set/added in this mutation. +func (m *AdministrativeAreaMutation) AddedEdges() []string { + edges := make([]string, 0, 3) + if m.children != nil { + edges = append(edges, administrativearea.EdgeChildren) + } + if m.parent != nil { + edges = append(edges, administrativearea.EdgeParent) + } + if m.localities != nil { + edges = append(edges, administrativearea.EdgeLocalities) + } + return edges +} + +// AddedIDs returns all IDs (to other nodes) that were added for the given edge +// name in this mutation. +func (m *AdministrativeAreaMutation) AddedIDs(name string) []ent.Value { + switch name { + case administrativearea.EdgeChildren: + ids := make([]ent.Value, 0, len(m.children)) + for id := range m.children { + ids = append(ids, id) + } + return ids + case administrativearea.EdgeParent: + if id := m.parent; id != nil { + return []ent.Value{*id} + } + case administrativearea.EdgeLocalities: + ids := make([]ent.Value, 0, len(m.localities)) + for id := range m.localities { + ids = append(ids, id) + } + return ids + } + return nil +} + +// RemovedEdges returns all edge names that were removed in this mutation. +func (m *AdministrativeAreaMutation) RemovedEdges() []string { + edges := make([]string, 0, 3) + if m.removedchildren != nil { + edges = append(edges, administrativearea.EdgeChildren) + } + if m.removedlocalities != nil { + edges = append(edges, administrativearea.EdgeLocalities) + } + return edges +} + +// RemovedIDs returns all IDs (to other nodes) that were removed for the edge with +// the given name in this mutation. +func (m *AdministrativeAreaMutation) RemovedIDs(name string) []ent.Value { + switch name { + case administrativearea.EdgeChildren: + ids := make([]ent.Value, 0, len(m.removedchildren)) + for id := range m.removedchildren { + ids = append(ids, id) + } + return ids + case administrativearea.EdgeLocalities: + ids := make([]ent.Value, 0, len(m.removedlocalities)) + for id := range m.removedlocalities { + ids = append(ids, id) + } + return ids + } + return nil +} + +// ClearedEdges returns all edge names that were cleared in this mutation. +func (m *AdministrativeAreaMutation) ClearedEdges() []string { + edges := make([]string, 0, 3) + if m.clearedchildren { + edges = append(edges, administrativearea.EdgeChildren) + } + if m.clearedparent { + edges = append(edges, administrativearea.EdgeParent) + } + if m.clearedlocalities { + edges = append(edges, administrativearea.EdgeLocalities) + } + return edges +} + +// EdgeCleared returns a boolean which indicates if the edge with the given name +// was cleared in this mutation. +func (m *AdministrativeAreaMutation) EdgeCleared(name string) bool { + switch name { + case administrativearea.EdgeChildren: + return m.clearedchildren + case administrativearea.EdgeParent: + return m.clearedparent + case administrativearea.EdgeLocalities: + return m.clearedlocalities + } + 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 *AdministrativeAreaMutation) ClearEdge(name string) error { + switch name { + case administrativearea.EdgeParent: + m.ClearParent() + return nil + } + return fmt.Errorf("unknown AdministrativeArea 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 *AdministrativeAreaMutation) ResetEdge(name string) error { + switch name { + case administrativearea.EdgeChildren: + m.ResetChildren() + return nil + case administrativearea.EdgeParent: + m.ResetParent() + return nil + case administrativearea.EdgeLocalities: + m.ResetLocalities() + return nil + } + return fmt.Errorf("unknown AdministrativeArea edge %s", name) +} + +// ContactRevealDailyLimitMutation represents an operation that mutates the ContactRevealDailyLimit nodes in the graph. +type ContactRevealDailyLimitMutation struct { + config + op Op + typ string + id *uuid.UUID + customer_internal_user_id *uuid.UUID + utc_day *time.Time + successful_count *int + addsuccessful_count *int + created_at *time.Time + updated_at *time.Time + clearedFields map[string]struct{} + done bool + oldValue func(context.Context) (*ContactRevealDailyLimit, error) + predicates []predicate.ContactRevealDailyLimit +} + +var _ ent.Mutation = (*ContactRevealDailyLimitMutation)(nil) + +// contactrevealdailylimitOption allows management of the mutation configuration using functional options. +type contactrevealdailylimitOption func(*ContactRevealDailyLimitMutation) + +// newContactRevealDailyLimitMutation creates new mutation for the ContactRevealDailyLimit entity. +func newContactRevealDailyLimitMutation(c config, op Op, opts ...contactrevealdailylimitOption) *ContactRevealDailyLimitMutation { + m := &ContactRevealDailyLimitMutation{ + config: c, + op: op, + typ: TypeContactRevealDailyLimit, + clearedFields: make(map[string]struct{}), + } + for _, opt := range opts { + opt(m) + } + return m +} + +// withContactRevealDailyLimitID sets the ID field of the mutation. +func withContactRevealDailyLimitID(id uuid.UUID) contactrevealdailylimitOption { + return func(m *ContactRevealDailyLimitMutation) { + var ( + err error + once sync.Once + value *ContactRevealDailyLimit + ) + m.oldValue = func(ctx context.Context) (*ContactRevealDailyLimit, error) { + once.Do(func() { + if m.done { + err = errors.New("querying old values post mutation is not allowed") + } else { + value, err = m.Client().ContactRevealDailyLimit.Get(ctx, id) + } + }) + return value, err + } + m.id = &id + } +} + +// withContactRevealDailyLimit sets the old ContactRevealDailyLimit of the mutation. +func withContactRevealDailyLimit(node *ContactRevealDailyLimit) contactrevealdailylimitOption { + return func(m *ContactRevealDailyLimitMutation) { + m.oldValue = func(context.Context) (*ContactRevealDailyLimit, 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 ContactRevealDailyLimitMutation) 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 ContactRevealDailyLimitMutation) 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 ContactRevealDailyLimit entities. +func (m *ContactRevealDailyLimitMutation) 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 *ContactRevealDailyLimitMutation) 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 *ContactRevealDailyLimitMutation) 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().ContactRevealDailyLimit.Query().Where(m.predicates...).IDs(ctx) + default: + return nil, fmt.Errorf("IDs is not allowed on %s operations", m.op) + } +} + +// SetCustomerInternalUserID sets the "customer_internal_user_id" field. +func (m *ContactRevealDailyLimitMutation) SetCustomerInternalUserID(u uuid.UUID) { + m.customer_internal_user_id = &u +} + +// CustomerInternalUserID returns the value of the "customer_internal_user_id" field in the mutation. +func (m *ContactRevealDailyLimitMutation) CustomerInternalUserID() (r uuid.UUID, exists bool) { + v := m.customer_internal_user_id + if v == nil { + return + } + return *v, true +} + +// OldCustomerInternalUserID returns the old "customer_internal_user_id" field's value of the ContactRevealDailyLimit entity. +// If the ContactRevealDailyLimit object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *ContactRevealDailyLimitMutation) OldCustomerInternalUserID(ctx context.Context) (v uuid.UUID, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldCustomerInternalUserID is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldCustomerInternalUserID requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldCustomerInternalUserID: %w", err) + } + return oldValue.CustomerInternalUserID, nil +} + +// ResetCustomerInternalUserID resets all changes to the "customer_internal_user_id" field. +func (m *ContactRevealDailyLimitMutation) ResetCustomerInternalUserID() { + m.customer_internal_user_id = nil +} + +// SetUtcDay sets the "utc_day" field. +func (m *ContactRevealDailyLimitMutation) SetUtcDay(t time.Time) { + m.utc_day = &t +} + +// UtcDay returns the value of the "utc_day" field in the mutation. +func (m *ContactRevealDailyLimitMutation) UtcDay() (r time.Time, exists bool) { + v := m.utc_day + if v == nil { + return + } + return *v, true +} + +// OldUtcDay returns the old "utc_day" field's value of the ContactRevealDailyLimit entity. +// If the ContactRevealDailyLimit object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *ContactRevealDailyLimitMutation) OldUtcDay(ctx context.Context) (v time.Time, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldUtcDay is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldUtcDay requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldUtcDay: %w", err) + } + return oldValue.UtcDay, nil +} + +// ResetUtcDay resets all changes to the "utc_day" field. +func (m *ContactRevealDailyLimitMutation) ResetUtcDay() { + m.utc_day = nil +} + +// SetSuccessfulCount sets the "successful_count" field. +func (m *ContactRevealDailyLimitMutation) SetSuccessfulCount(i int) { + m.successful_count = &i + m.addsuccessful_count = nil +} + +// SuccessfulCount returns the value of the "successful_count" field in the mutation. +func (m *ContactRevealDailyLimitMutation) SuccessfulCount() (r int, exists bool) { + v := m.successful_count + if v == nil { + return + } + return *v, true +} + +// OldSuccessfulCount returns the old "successful_count" field's value of the ContactRevealDailyLimit entity. +// If the ContactRevealDailyLimit object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *ContactRevealDailyLimitMutation) OldSuccessfulCount(ctx context.Context) (v int, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldSuccessfulCount is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldSuccessfulCount requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldSuccessfulCount: %w", err) + } + return oldValue.SuccessfulCount, nil +} + +// AddSuccessfulCount adds i to the "successful_count" field. +func (m *ContactRevealDailyLimitMutation) AddSuccessfulCount(i int) { + if m.addsuccessful_count != nil { + *m.addsuccessful_count += i + } else { + m.addsuccessful_count = &i + } +} + +// AddedSuccessfulCount returns the value that was added to the "successful_count" field in this mutation. +func (m *ContactRevealDailyLimitMutation) AddedSuccessfulCount() (r int, exists bool) { + v := m.addsuccessful_count + if v == nil { + return + } + return *v, true +} + +// ResetSuccessfulCount resets all changes to the "successful_count" field. +func (m *ContactRevealDailyLimitMutation) ResetSuccessfulCount() { + m.successful_count = nil + m.addsuccessful_count = nil +} + +// SetCreatedAt sets the "created_at" field. +func (m *ContactRevealDailyLimitMutation) SetCreatedAt(t time.Time) { + m.created_at = &t +} + +// CreatedAt returns the value of the "created_at" field in the mutation. +func (m *ContactRevealDailyLimitMutation) CreatedAt() (r time.Time, exists bool) { + v := m.created_at + if v == nil { + return + } + return *v, true +} + +// OldCreatedAt returns the old "created_at" field's value of the ContactRevealDailyLimit entity. +// If the ContactRevealDailyLimit object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *ContactRevealDailyLimitMutation) OldCreatedAt(ctx context.Context) (v time.Time, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldCreatedAt is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldCreatedAt requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldCreatedAt: %w", err) + } + return oldValue.CreatedAt, nil +} + +// ResetCreatedAt resets all changes to the "created_at" field. +func (m *ContactRevealDailyLimitMutation) ResetCreatedAt() { + m.created_at = nil +} + +// SetUpdatedAt sets the "updated_at" field. +func (m *ContactRevealDailyLimitMutation) SetUpdatedAt(t time.Time) { + m.updated_at = &t +} + +// UpdatedAt returns the value of the "updated_at" field in the mutation. +func (m *ContactRevealDailyLimitMutation) UpdatedAt() (r time.Time, exists bool) { + v := m.updated_at + if v == nil { + return + } + return *v, true +} + +// OldUpdatedAt returns the old "updated_at" field's value of the ContactRevealDailyLimit entity. +// If the ContactRevealDailyLimit object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *ContactRevealDailyLimitMutation) OldUpdatedAt(ctx context.Context) (v time.Time, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldUpdatedAt is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldUpdatedAt requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldUpdatedAt: %w", err) + } + return oldValue.UpdatedAt, nil +} + +// ResetUpdatedAt resets all changes to the "updated_at" field. +func (m *ContactRevealDailyLimitMutation) ResetUpdatedAt() { + m.updated_at = nil +} + +// Where appends a list predicates to the ContactRevealDailyLimitMutation builder. +func (m *ContactRevealDailyLimitMutation) Where(ps ...predicate.ContactRevealDailyLimit) { + m.predicates = append(m.predicates, ps...) +} + +// WhereP appends storage-level predicates to the ContactRevealDailyLimitMutation builder. Using this method, +// users can use type-assertion to append predicates that do not depend on any generated package. +func (m *ContactRevealDailyLimitMutation) WhereP(ps ...func(*sql.Selector)) { + p := make([]predicate.ContactRevealDailyLimit, len(ps)) + for i := range ps { + p[i] = ps[i] + } + m.Where(p...) +} + +// Op returns the operation name. +func (m *ContactRevealDailyLimitMutation) Op() Op { + return m.op +} + +// SetOp allows setting the mutation operation. +func (m *ContactRevealDailyLimitMutation) SetOp(op Op) { + m.op = op +} + +// Type returns the node type of this mutation (ContactRevealDailyLimit). +func (m *ContactRevealDailyLimitMutation) 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 *ContactRevealDailyLimitMutation) Fields() []string { + fields := make([]string, 0, 5) + if m.customer_internal_user_id != nil { + fields = append(fields, contactrevealdailylimit.FieldCustomerInternalUserID) + } + if m.utc_day != nil { + fields = append(fields, contactrevealdailylimit.FieldUtcDay) + } + if m.successful_count != nil { + fields = append(fields, contactrevealdailylimit.FieldSuccessfulCount) + } + if m.created_at != nil { + fields = append(fields, contactrevealdailylimit.FieldCreatedAt) + } + if m.updated_at != nil { + fields = append(fields, contactrevealdailylimit.FieldUpdatedAt) + } + 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 *ContactRevealDailyLimitMutation) Field(name string) (ent.Value, bool) { + switch name { + case contactrevealdailylimit.FieldCustomerInternalUserID: + return m.CustomerInternalUserID() + case contactrevealdailylimit.FieldUtcDay: + return m.UtcDay() + case contactrevealdailylimit.FieldSuccessfulCount: + return m.SuccessfulCount() + case contactrevealdailylimit.FieldCreatedAt: + return m.CreatedAt() + case contactrevealdailylimit.FieldUpdatedAt: + return m.UpdatedAt() + } + 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 *ContactRevealDailyLimitMutation) OldField(ctx context.Context, name string) (ent.Value, error) { + switch name { + case contactrevealdailylimit.FieldCustomerInternalUserID: + return m.OldCustomerInternalUserID(ctx) + case contactrevealdailylimit.FieldUtcDay: + return m.OldUtcDay(ctx) + case contactrevealdailylimit.FieldSuccessfulCount: + return m.OldSuccessfulCount(ctx) + case contactrevealdailylimit.FieldCreatedAt: + return m.OldCreatedAt(ctx) + case contactrevealdailylimit.FieldUpdatedAt: + return m.OldUpdatedAt(ctx) + } + return nil, fmt.Errorf("unknown ContactRevealDailyLimit 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 *ContactRevealDailyLimitMutation) SetField(name string, value ent.Value) error { + switch name { + case contactrevealdailylimit.FieldCustomerInternalUserID: + v, ok := value.(uuid.UUID) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetCustomerInternalUserID(v) + return nil + case contactrevealdailylimit.FieldUtcDay: + v, ok := value.(time.Time) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetUtcDay(v) + return nil + case contactrevealdailylimit.FieldSuccessfulCount: + v, ok := value.(int) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetSuccessfulCount(v) + return nil + case contactrevealdailylimit.FieldCreatedAt: + v, ok := value.(time.Time) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetCreatedAt(v) + return nil + case contactrevealdailylimit.FieldUpdatedAt: + v, ok := value.(time.Time) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetUpdatedAt(v) + return nil + } + return fmt.Errorf("unknown ContactRevealDailyLimit field %s", name) +} + +// AddedFields returns all numeric fields that were incremented/decremented during +// this mutation. +func (m *ContactRevealDailyLimitMutation) AddedFields() []string { + var fields []string + if m.addsuccessful_count != nil { + fields = append(fields, contactrevealdailylimit.FieldSuccessfulCount) + } + 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 *ContactRevealDailyLimitMutation) AddedField(name string) (ent.Value, bool) { + switch name { + case contactrevealdailylimit.FieldSuccessfulCount: + return m.AddedSuccessfulCount() + } + 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 *ContactRevealDailyLimitMutation) AddField(name string, value ent.Value) error { + switch name { + case contactrevealdailylimit.FieldSuccessfulCount: + v, ok := value.(int) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.AddSuccessfulCount(v) + return nil + } + return fmt.Errorf("unknown ContactRevealDailyLimit numeric field %s", name) +} + +// ClearedFields returns all nullable fields that were cleared during this +// mutation. +func (m *ContactRevealDailyLimitMutation) ClearedFields() []string { + return nil +} + +// FieldCleared returns a boolean indicating if a field with the given name was +// cleared in this mutation. +func (m *ContactRevealDailyLimitMutation) 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 *ContactRevealDailyLimitMutation) ClearField(name string) error { + return fmt.Errorf("unknown ContactRevealDailyLimit 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 *ContactRevealDailyLimitMutation) ResetField(name string) error { + switch name { + case contactrevealdailylimit.FieldCustomerInternalUserID: + m.ResetCustomerInternalUserID() + return nil + case contactrevealdailylimit.FieldUtcDay: + m.ResetUtcDay() + return nil + case contactrevealdailylimit.FieldSuccessfulCount: + m.ResetSuccessfulCount() + return nil + case contactrevealdailylimit.FieldCreatedAt: + m.ResetCreatedAt() + return nil + case contactrevealdailylimit.FieldUpdatedAt: + m.ResetUpdatedAt() + return nil + } + return fmt.Errorf("unknown ContactRevealDailyLimit field %s", name) +} + +// AddedEdges returns all edge names that were set/added in this mutation. +func (m *ContactRevealDailyLimitMutation) AddedEdges() []string { + edges := make([]string, 0, 0) + return edges +} + +// AddedIDs returns all IDs (to other nodes) that were added for the given edge +// name in this mutation. +func (m *ContactRevealDailyLimitMutation) AddedIDs(name string) []ent.Value { + return nil +} + +// RemovedEdges returns all edge names that were removed in this mutation. +func (m *ContactRevealDailyLimitMutation) RemovedEdges() []string { + edges := make([]string, 0, 0) + return edges +} + +// RemovedIDs returns all IDs (to other nodes) that were removed for the edge with +// the given name in this mutation. +func (m *ContactRevealDailyLimitMutation) RemovedIDs(name string) []ent.Value { + return nil +} + +// ClearedEdges returns all edge names that were cleared in this mutation. +func (m *ContactRevealDailyLimitMutation) ClearedEdges() []string { + edges := make([]string, 0, 0) + return edges +} + +// EdgeCleared returns a boolean which indicates if the edge with the given name +// was cleared in this mutation. +func (m *ContactRevealDailyLimitMutation) EdgeCleared(name string) bool { + return false +} + +// ClearEdge clears the value of the edge with the given name. It returns an error +// if that edge is not defined in the schema. +func (m *ContactRevealDailyLimitMutation) ClearEdge(name string) error { + return fmt.Errorf("unknown ContactRevealDailyLimit 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 *ContactRevealDailyLimitMutation) ResetEdge(name string) error { + return fmt.Errorf("unknown ContactRevealDailyLimit edge %s", name) +} + +// ContactRevealEventMutation represents an operation that mutates the ContactRevealEvent nodes in the graph. +type ContactRevealEventMutation struct { + config + op Op + typ string + id *uuid.UUID + customer_internal_user_id *uuid.UUID + provider_internal_user_id *uuid.UUID + listing_id *uuid.UUID + channel *contactrevealevent.Channel + utc_day *time.Time + revealed_at *time.Time + clearedFields map[string]struct{} + done bool + oldValue func(context.Context) (*ContactRevealEvent, error) + predicates []predicate.ContactRevealEvent +} + +var _ ent.Mutation = (*ContactRevealEventMutation)(nil) + +// contactrevealeventOption allows management of the mutation configuration using functional options. +type contactrevealeventOption func(*ContactRevealEventMutation) + +// newContactRevealEventMutation creates new mutation for the ContactRevealEvent entity. +func newContactRevealEventMutation(c config, op Op, opts ...contactrevealeventOption) *ContactRevealEventMutation { + m := &ContactRevealEventMutation{ + config: c, + op: op, + typ: TypeContactRevealEvent, + clearedFields: make(map[string]struct{}), + } + for _, opt := range opts { + opt(m) + } + return m +} + +// withContactRevealEventID sets the ID field of the mutation. +func withContactRevealEventID(id uuid.UUID) contactrevealeventOption { + return func(m *ContactRevealEventMutation) { + var ( + err error + once sync.Once + value *ContactRevealEvent + ) + m.oldValue = func(ctx context.Context) (*ContactRevealEvent, error) { + once.Do(func() { + if m.done { + err = errors.New("querying old values post mutation is not allowed") + } else { + value, err = m.Client().ContactRevealEvent.Get(ctx, id) + } + }) + return value, err + } + m.id = &id + } +} + +// withContactRevealEvent sets the old ContactRevealEvent of the mutation. +func withContactRevealEvent(node *ContactRevealEvent) contactrevealeventOption { + return func(m *ContactRevealEventMutation) { + m.oldValue = func(context.Context) (*ContactRevealEvent, 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 ContactRevealEventMutation) 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 ContactRevealEventMutation) 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 ContactRevealEvent entities. +func (m *ContactRevealEventMutation) 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 *ContactRevealEventMutation) 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 *ContactRevealEventMutation) 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().ContactRevealEvent.Query().Where(m.predicates...).IDs(ctx) + default: + return nil, fmt.Errorf("IDs is not allowed on %s operations", m.op) + } +} + +// SetCustomerInternalUserID sets the "customer_internal_user_id" field. +func (m *ContactRevealEventMutation) SetCustomerInternalUserID(u uuid.UUID) { + m.customer_internal_user_id = &u +} + +// CustomerInternalUserID returns the value of the "customer_internal_user_id" field in the mutation. +func (m *ContactRevealEventMutation) CustomerInternalUserID() (r uuid.UUID, exists bool) { + v := m.customer_internal_user_id + if v == nil { + return + } + return *v, true +} + +// OldCustomerInternalUserID returns the old "customer_internal_user_id" field's value of the ContactRevealEvent entity. +// If the ContactRevealEvent object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *ContactRevealEventMutation) OldCustomerInternalUserID(ctx context.Context) (v uuid.UUID, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldCustomerInternalUserID is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldCustomerInternalUserID requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldCustomerInternalUserID: %w", err) + } + return oldValue.CustomerInternalUserID, nil +} + +// ResetCustomerInternalUserID resets all changes to the "customer_internal_user_id" field. +func (m *ContactRevealEventMutation) ResetCustomerInternalUserID() { + m.customer_internal_user_id = nil +} + +// SetProviderInternalUserID sets the "provider_internal_user_id" field. +func (m *ContactRevealEventMutation) SetProviderInternalUserID(u uuid.UUID) { + m.provider_internal_user_id = &u +} + +// ProviderInternalUserID returns the value of the "provider_internal_user_id" field in the mutation. +func (m *ContactRevealEventMutation) ProviderInternalUserID() (r uuid.UUID, exists bool) { + v := m.provider_internal_user_id + if v == nil { + return + } + return *v, true +} + +// OldProviderInternalUserID returns the old "provider_internal_user_id" field's value of the ContactRevealEvent entity. +// If the ContactRevealEvent object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *ContactRevealEventMutation) OldProviderInternalUserID(ctx context.Context) (v uuid.UUID, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldProviderInternalUserID is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldProviderInternalUserID requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldProviderInternalUserID: %w", err) + } + return oldValue.ProviderInternalUserID, nil +} + +// ResetProviderInternalUserID resets all changes to the "provider_internal_user_id" field. +func (m *ContactRevealEventMutation) ResetProviderInternalUserID() { + m.provider_internal_user_id = nil +} + +// SetListingID sets the "listing_id" field. +func (m *ContactRevealEventMutation) SetListingID(u uuid.UUID) { + m.listing_id = &u +} + +// ListingID returns the value of the "listing_id" field in the mutation. +func (m *ContactRevealEventMutation) ListingID() (r uuid.UUID, exists bool) { + v := m.listing_id + if v == nil { + return + } + return *v, true +} + +// OldListingID returns the old "listing_id" field's value of the ContactRevealEvent entity. +// If the ContactRevealEvent object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *ContactRevealEventMutation) OldListingID(ctx context.Context) (v uuid.UUID, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldListingID is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldListingID requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldListingID: %w", err) + } + return oldValue.ListingID, nil +} + +// ResetListingID resets all changes to the "listing_id" field. +func (m *ContactRevealEventMutation) ResetListingID() { + m.listing_id = nil +} + +// SetChannel sets the "channel" field. +func (m *ContactRevealEventMutation) SetChannel(c contactrevealevent.Channel) { + m.channel = &c +} + +// Channel returns the value of the "channel" field in the mutation. +func (m *ContactRevealEventMutation) Channel() (r contactrevealevent.Channel, exists bool) { + v := m.channel + if v == nil { + return + } + return *v, true +} + +// OldChannel returns the old "channel" field's value of the ContactRevealEvent entity. +// If the ContactRevealEvent object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *ContactRevealEventMutation) OldChannel(ctx context.Context) (v contactrevealevent.Channel, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldChannel is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldChannel requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldChannel: %w", err) + } + return oldValue.Channel, nil +} + +// ResetChannel resets all changes to the "channel" field. +func (m *ContactRevealEventMutation) ResetChannel() { + m.channel = nil +} + +// SetUtcDay sets the "utc_day" field. +func (m *ContactRevealEventMutation) SetUtcDay(t time.Time) { + m.utc_day = &t +} + +// UtcDay returns the value of the "utc_day" field in the mutation. +func (m *ContactRevealEventMutation) UtcDay() (r time.Time, exists bool) { + v := m.utc_day + if v == nil { + return + } + return *v, true +} + +// OldUtcDay returns the old "utc_day" field's value of the ContactRevealEvent entity. +// If the ContactRevealEvent object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *ContactRevealEventMutation) OldUtcDay(ctx context.Context) (v time.Time, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldUtcDay is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldUtcDay requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldUtcDay: %w", err) + } + return oldValue.UtcDay, nil +} + +// ResetUtcDay resets all changes to the "utc_day" field. +func (m *ContactRevealEventMutation) ResetUtcDay() { + m.utc_day = nil +} + +// SetRevealedAt sets the "revealed_at" field. +func (m *ContactRevealEventMutation) SetRevealedAt(t time.Time) { + m.revealed_at = &t +} + +// RevealedAt returns the value of the "revealed_at" field in the mutation. +func (m *ContactRevealEventMutation) RevealedAt() (r time.Time, exists bool) { + v := m.revealed_at + if v == nil { + return + } + return *v, true +} + +// OldRevealedAt returns the old "revealed_at" field's value of the ContactRevealEvent entity. +// If the ContactRevealEvent object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *ContactRevealEventMutation) OldRevealedAt(ctx context.Context) (v time.Time, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldRevealedAt is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldRevealedAt requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldRevealedAt: %w", err) + } + return oldValue.RevealedAt, nil +} + +// ResetRevealedAt resets all changes to the "revealed_at" field. +func (m *ContactRevealEventMutation) ResetRevealedAt() { + m.revealed_at = nil +} + +// Where appends a list predicates to the ContactRevealEventMutation builder. +func (m *ContactRevealEventMutation) Where(ps ...predicate.ContactRevealEvent) { + m.predicates = append(m.predicates, ps...) +} + +// WhereP appends storage-level predicates to the ContactRevealEventMutation builder. Using this method, +// users can use type-assertion to append predicates that do not depend on any generated package. +func (m *ContactRevealEventMutation) WhereP(ps ...func(*sql.Selector)) { + p := make([]predicate.ContactRevealEvent, len(ps)) + for i := range ps { + p[i] = ps[i] + } + m.Where(p...) +} + +// Op returns the operation name. +func (m *ContactRevealEventMutation) Op() Op { + return m.op +} + +// SetOp allows setting the mutation operation. +func (m *ContactRevealEventMutation) SetOp(op Op) { + m.op = op +} + +// Type returns the node type of this mutation (ContactRevealEvent). +func (m *ContactRevealEventMutation) 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 *ContactRevealEventMutation) Fields() []string { + fields := make([]string, 0, 6) + if m.customer_internal_user_id != nil { + fields = append(fields, contactrevealevent.FieldCustomerInternalUserID) + } + if m.provider_internal_user_id != nil { + fields = append(fields, contactrevealevent.FieldProviderInternalUserID) + } + if m.listing_id != nil { + fields = append(fields, contactrevealevent.FieldListingID) + } + if m.channel != nil { + fields = append(fields, contactrevealevent.FieldChannel) + } + if m.utc_day != nil { + fields = append(fields, contactrevealevent.FieldUtcDay) + } + if m.revealed_at != nil { + fields = append(fields, contactrevealevent.FieldRevealedAt) + } + 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 *ContactRevealEventMutation) Field(name string) (ent.Value, bool) { + switch name { + case contactrevealevent.FieldCustomerInternalUserID: + return m.CustomerInternalUserID() + case contactrevealevent.FieldProviderInternalUserID: + return m.ProviderInternalUserID() + case contactrevealevent.FieldListingID: + return m.ListingID() + case contactrevealevent.FieldChannel: + return m.Channel() + case contactrevealevent.FieldUtcDay: + return m.UtcDay() + case contactrevealevent.FieldRevealedAt: + return m.RevealedAt() + } + 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 *ContactRevealEventMutation) OldField(ctx context.Context, name string) (ent.Value, error) { + switch name { + case contactrevealevent.FieldCustomerInternalUserID: + return m.OldCustomerInternalUserID(ctx) + case contactrevealevent.FieldProviderInternalUserID: + return m.OldProviderInternalUserID(ctx) + case contactrevealevent.FieldListingID: + return m.OldListingID(ctx) + case contactrevealevent.FieldChannel: + return m.OldChannel(ctx) + case contactrevealevent.FieldUtcDay: + return m.OldUtcDay(ctx) + case contactrevealevent.FieldRevealedAt: + return m.OldRevealedAt(ctx) + } + return nil, fmt.Errorf("unknown ContactRevealEvent 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 *ContactRevealEventMutation) SetField(name string, value ent.Value) error { + switch name { + case contactrevealevent.FieldCustomerInternalUserID: + v, ok := value.(uuid.UUID) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetCustomerInternalUserID(v) + return nil + case contactrevealevent.FieldProviderInternalUserID: + v, ok := value.(uuid.UUID) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetProviderInternalUserID(v) + return nil + case contactrevealevent.FieldListingID: + v, ok := value.(uuid.UUID) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetListingID(v) + return nil + case contactrevealevent.FieldChannel: + v, ok := value.(contactrevealevent.Channel) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetChannel(v) + return nil + case contactrevealevent.FieldUtcDay: + v, ok := value.(time.Time) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetUtcDay(v) + return nil + case contactrevealevent.FieldRevealedAt: + v, ok := value.(time.Time) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetRevealedAt(v) + return nil + } + return fmt.Errorf("unknown ContactRevealEvent field %s", name) +} + +// AddedFields returns all numeric fields that were incremented/decremented during +// this mutation. +func (m *ContactRevealEventMutation) 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 *ContactRevealEventMutation) 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 *ContactRevealEventMutation) AddField(name string, value ent.Value) error { + switch name { + } + return fmt.Errorf("unknown ContactRevealEvent numeric field %s", name) +} + +// ClearedFields returns all nullable fields that were cleared during this +// mutation. +func (m *ContactRevealEventMutation) ClearedFields() []string { + return nil +} + +// FieldCleared returns a boolean indicating if a field with the given name was +// cleared in this mutation. +func (m *ContactRevealEventMutation) 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 *ContactRevealEventMutation) ClearField(name string) error { + return fmt.Errorf("unknown ContactRevealEvent 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 *ContactRevealEventMutation) ResetField(name string) error { + switch name { + case contactrevealevent.FieldCustomerInternalUserID: + m.ResetCustomerInternalUserID() + return nil + case contactrevealevent.FieldProviderInternalUserID: + m.ResetProviderInternalUserID() + return nil + case contactrevealevent.FieldListingID: + m.ResetListingID() + return nil + case contactrevealevent.FieldChannel: + m.ResetChannel() + return nil + case contactrevealevent.FieldUtcDay: + m.ResetUtcDay() + return nil + case contactrevealevent.FieldRevealedAt: + m.ResetRevealedAt() + return nil + } + return fmt.Errorf("unknown ContactRevealEvent field %s", name) +} + +// AddedEdges returns all edge names that were set/added in this mutation. +func (m *ContactRevealEventMutation) AddedEdges() []string { + edges := make([]string, 0, 0) + return edges +} + +// AddedIDs returns all IDs (to other nodes) that were added for the given edge +// name in this mutation. +func (m *ContactRevealEventMutation) AddedIDs(name string) []ent.Value { + return nil +} + +// RemovedEdges returns all edge names that were removed in this mutation. +func (m *ContactRevealEventMutation) RemovedEdges() []string { + edges := make([]string, 0, 0) + return edges +} + +// RemovedIDs returns all IDs (to other nodes) that were removed for the edge with +// the given name in this mutation. +func (m *ContactRevealEventMutation) RemovedIDs(name string) []ent.Value { + return nil +} + +// ClearedEdges returns all edge names that were cleared in this mutation. +func (m *ContactRevealEventMutation) ClearedEdges() []string { + edges := make([]string, 0, 0) + return edges +} + +// EdgeCleared returns a boolean which indicates if the edge with the given name +// was cleared in this mutation. +func (m *ContactRevealEventMutation) EdgeCleared(name string) bool { + return false +} + +// ClearEdge clears the value of the edge with the given name. It returns an error +// if that edge is not defined in the schema. +func (m *ContactRevealEventMutation) ClearEdge(name string) error { + return fmt.Errorf("unknown ContactRevealEvent 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 *ContactRevealEventMutation) ResetEdge(name string) error { + return fmt.Errorf("unknown ContactRevealEvent edge %s", name) +} + +// InternalUserMutation represents an operation that mutates the InternalUser nodes in the graph. +type InternalUserMutation struct { + config + op Op + typ string + id *uuid.UUID + clerk_subject *string + created_at *time.Time + updated_at *time.Time + clearedFields map[string]struct{} + done bool + oldValue func(context.Context) (*InternalUser, error) + predicates []predicate.InternalUser +} + +var _ ent.Mutation = (*InternalUserMutation)(nil) + +// internaluserOption allows management of the mutation configuration using functional options. +type internaluserOption func(*InternalUserMutation) + +// newInternalUserMutation creates new mutation for the InternalUser entity. +func newInternalUserMutation(c config, op Op, opts ...internaluserOption) *InternalUserMutation { + m := &InternalUserMutation{ + config: c, + op: op, + typ: TypeInternalUser, + clearedFields: make(map[string]struct{}), + } + for _, opt := range opts { + opt(m) + } + return m +} + +// withInternalUserID sets the ID field of the mutation. +func withInternalUserID(id uuid.UUID) internaluserOption { + return func(m *InternalUserMutation) { + var ( + err error + once sync.Once + value *InternalUser + ) + m.oldValue = func(ctx context.Context) (*InternalUser, error) { + once.Do(func() { + if m.done { + err = errors.New("querying old values post mutation is not allowed") + } else { + value, err = m.Client().InternalUser.Get(ctx, id) + } + }) + return value, err + } + m.id = &id + } +} + +// withInternalUser sets the old InternalUser of the mutation. +func withInternalUser(node *InternalUser) internaluserOption { + return func(m *InternalUserMutation) { + m.oldValue = func(context.Context) (*InternalUser, 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 InternalUserMutation) 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 InternalUserMutation) 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 InternalUser entities. +func (m *InternalUserMutation) 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 *InternalUserMutation) 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 *InternalUserMutation) 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().InternalUser.Query().Where(m.predicates...).IDs(ctx) + default: + return nil, fmt.Errorf("IDs is not allowed on %s operations", m.op) + } +} + +// SetClerkSubject sets the "clerk_subject" field. +func (m *InternalUserMutation) SetClerkSubject(s string) { + m.clerk_subject = &s +} + +// ClerkSubject returns the value of the "clerk_subject" field in the mutation. +func (m *InternalUserMutation) ClerkSubject() (r string, exists bool) { + v := m.clerk_subject + if v == nil { + return + } + return *v, true +} + +// OldClerkSubject returns the old "clerk_subject" field's value of the InternalUser entity. +// If the InternalUser object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *InternalUserMutation) OldClerkSubject(ctx context.Context) (v string, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldClerkSubject is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldClerkSubject requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldClerkSubject: %w", err) + } + return oldValue.ClerkSubject, nil +} + +// ResetClerkSubject resets all changes to the "clerk_subject" field. +func (m *InternalUserMutation) ResetClerkSubject() { + m.clerk_subject = nil +} + +// SetCreatedAt sets the "created_at" field. +func (m *InternalUserMutation) SetCreatedAt(t time.Time) { + m.created_at = &t +} + +// CreatedAt returns the value of the "created_at" field in the mutation. +func (m *InternalUserMutation) CreatedAt() (r time.Time, exists bool) { + v := m.created_at + if v == nil { + return + } + return *v, true +} + +// OldCreatedAt returns the old "created_at" field's value of the InternalUser entity. +// If the InternalUser object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *InternalUserMutation) OldCreatedAt(ctx context.Context) (v time.Time, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldCreatedAt is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldCreatedAt requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldCreatedAt: %w", err) + } + return oldValue.CreatedAt, nil +} + +// ResetCreatedAt resets all changes to the "created_at" field. +func (m *InternalUserMutation) ResetCreatedAt() { + m.created_at = nil +} + +// SetUpdatedAt sets the "updated_at" field. +func (m *InternalUserMutation) SetUpdatedAt(t time.Time) { + m.updated_at = &t +} + +// UpdatedAt returns the value of the "updated_at" field in the mutation. +func (m *InternalUserMutation) UpdatedAt() (r time.Time, exists bool) { + v := m.updated_at + if v == nil { + return + } + return *v, true +} + +// OldUpdatedAt returns the old "updated_at" field's value of the InternalUser entity. +// If the InternalUser object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *InternalUserMutation) OldUpdatedAt(ctx context.Context) (v time.Time, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldUpdatedAt is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldUpdatedAt requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldUpdatedAt: %w", err) + } + return oldValue.UpdatedAt, nil +} + +// ResetUpdatedAt resets all changes to the "updated_at" field. +func (m *InternalUserMutation) ResetUpdatedAt() { + m.updated_at = nil +} + +// Where appends a list predicates to the InternalUserMutation builder. +func (m *InternalUserMutation) Where(ps ...predicate.InternalUser) { + m.predicates = append(m.predicates, ps...) +} + +// WhereP appends storage-level predicates to the InternalUserMutation builder. Using this method, +// users can use type-assertion to append predicates that do not depend on any generated package. +func (m *InternalUserMutation) WhereP(ps ...func(*sql.Selector)) { + p := make([]predicate.InternalUser, len(ps)) + for i := range ps { + p[i] = ps[i] + } + m.Where(p...) +} + +// Op returns the operation name. +func (m *InternalUserMutation) Op() Op { + return m.op +} + +// SetOp allows setting the mutation operation. +func (m *InternalUserMutation) SetOp(op Op) { + m.op = op +} + +// Type returns the node type of this mutation (InternalUser). +func (m *InternalUserMutation) 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 *InternalUserMutation) Fields() []string { + fields := make([]string, 0, 3) + if m.clerk_subject != nil { + fields = append(fields, internaluser.FieldClerkSubject) + } + if m.created_at != nil { + fields = append(fields, internaluser.FieldCreatedAt) + } + if m.updated_at != nil { + fields = append(fields, internaluser.FieldUpdatedAt) + } + 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 *InternalUserMutation) Field(name string) (ent.Value, bool) { + switch name { + case internaluser.FieldClerkSubject: + return m.ClerkSubject() + case internaluser.FieldCreatedAt: + return m.CreatedAt() + case internaluser.FieldUpdatedAt: + return m.UpdatedAt() + } + 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 *InternalUserMutation) OldField(ctx context.Context, name string) (ent.Value, error) { + switch name { + case internaluser.FieldClerkSubject: + return m.OldClerkSubject(ctx) + case internaluser.FieldCreatedAt: + return m.OldCreatedAt(ctx) + case internaluser.FieldUpdatedAt: + return m.OldUpdatedAt(ctx) + } + return nil, fmt.Errorf("unknown InternalUser 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 *InternalUserMutation) SetField(name string, value ent.Value) error { + switch name { + case internaluser.FieldClerkSubject: + v, ok := value.(string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetClerkSubject(v) + return nil + case internaluser.FieldCreatedAt: + v, ok := value.(time.Time) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetCreatedAt(v) + return nil + case internaluser.FieldUpdatedAt: + v, ok := value.(time.Time) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetUpdatedAt(v) + return nil + } + return fmt.Errorf("unknown InternalUser field %s", name) +} + +// AddedFields returns all numeric fields that were incremented/decremented during +// this mutation. +func (m *InternalUserMutation) 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 *InternalUserMutation) 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 *InternalUserMutation) AddField(name string, value ent.Value) error { + switch name { + } + return fmt.Errorf("unknown InternalUser numeric field %s", name) +} + +// ClearedFields returns all nullable fields that were cleared during this +// mutation. +func (m *InternalUserMutation) ClearedFields() []string { + return nil +} + +// FieldCleared returns a boolean indicating if a field with the given name was +// cleared in this mutation. +func (m *InternalUserMutation) 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 *InternalUserMutation) ClearField(name string) error { + return fmt.Errorf("unknown InternalUser 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 *InternalUserMutation) ResetField(name string) error { + switch name { + case internaluser.FieldClerkSubject: + m.ResetClerkSubject() + return nil + case internaluser.FieldCreatedAt: + m.ResetCreatedAt() + return nil + case internaluser.FieldUpdatedAt: + m.ResetUpdatedAt() + return nil + } + return fmt.Errorf("unknown InternalUser field %s", name) +} + +// AddedEdges returns all edge names that were set/added in this mutation. +func (m *InternalUserMutation) AddedEdges() []string { + edges := make([]string, 0, 0) + return edges +} + +// AddedIDs returns all IDs (to other nodes) that were added for the given edge +// name in this mutation. +func (m *InternalUserMutation) AddedIDs(name string) []ent.Value { + return nil +} + +// RemovedEdges returns all edge names that were removed in this mutation. +func (m *InternalUserMutation) RemovedEdges() []string { + edges := make([]string, 0, 0) + return edges +} + +// RemovedIDs returns all IDs (to other nodes) that were removed for the edge with +// the given name in this mutation. +func (m *InternalUserMutation) RemovedIDs(name string) []ent.Value { + return nil +} + +// ClearedEdges returns all edge names that were cleared in this mutation. +func (m *InternalUserMutation) ClearedEdges() []string { + edges := make([]string, 0, 0) + return edges +} + +// EdgeCleared returns a boolean which indicates if the edge with the given name +// was cleared in this mutation. +func (m *InternalUserMutation) EdgeCleared(name string) bool { + return false +} + +// ClearEdge clears the value of the edge with the given name. It returns an error +// if that edge is not defined in the schema. +func (m *InternalUserMutation) ClearEdge(name string) error { + return fmt.Errorf("unknown InternalUser 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 *InternalUserMutation) ResetEdge(name string) error { + return fmt.Errorf("unknown InternalUser edge %s", name) +} + +// ListingMutation represents an operation that mutates the Listing nodes in the graph. +type ListingMutation struct { + config + op Op + typ string + id *uuid.UUID + internal_user_id *uuid.UUID + category_id *uuid.UUID + primary_locality_id *uuid.UUID + title *string + description *string + price_type *listing.PriceType + price_minor *int + addprice_minor *int + currency *string + travels_to_customer *bool + receives_customer *bool + remote_services *bool + state *listing.State + revision *int + addrevision *int + created_at *time.Time + updated_at *time.Time + clearedFields map[string]struct{} + done bool + oldValue func(context.Context) (*Listing, error) + predicates []predicate.Listing +} + +var _ ent.Mutation = (*ListingMutation)(nil) + +// listingOption allows management of the mutation configuration using functional options. +type listingOption func(*ListingMutation) + +// newListingMutation creates new mutation for the Listing entity. +func newListingMutation(c config, op Op, opts ...listingOption) *ListingMutation { + m := &ListingMutation{ + config: c, + op: op, + typ: TypeListing, + clearedFields: make(map[string]struct{}), + } + for _, opt := range opts { + opt(m) + } + return m +} + +// withListingID sets the ID field of the mutation. +func withListingID(id uuid.UUID) listingOption { + return func(m *ListingMutation) { + var ( + err error + once sync.Once + value *Listing + ) + m.oldValue = func(ctx context.Context) (*Listing, error) { + once.Do(func() { + if m.done { + err = errors.New("querying old values post mutation is not allowed") + } else { + value, err = m.Client().Listing.Get(ctx, id) + } + }) + return value, err + } + m.id = &id + } +} + +// withListing sets the old Listing of the mutation. +func withListing(node *Listing) listingOption { + return func(m *ListingMutation) { + m.oldValue = func(context.Context) (*Listing, 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 ListingMutation) 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 ListingMutation) 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 Listing entities. +func (m *ListingMutation) 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 *ListingMutation) 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 *ListingMutation) 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().Listing.Query().Where(m.predicates...).IDs(ctx) + default: + return nil, fmt.Errorf("IDs is not allowed on %s operations", m.op) + } +} + +// SetInternalUserID sets the "internal_user_id" field. +func (m *ListingMutation) SetInternalUserID(u uuid.UUID) { + m.internal_user_id = &u +} + +// InternalUserID returns the value of the "internal_user_id" field in the mutation. +func (m *ListingMutation) InternalUserID() (r uuid.UUID, exists bool) { + v := m.internal_user_id + if v == nil { + return + } + return *v, true +} + +// OldInternalUserID returns the old "internal_user_id" field's value of the Listing entity. +// If the Listing object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *ListingMutation) OldInternalUserID(ctx context.Context) (v uuid.UUID, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldInternalUserID is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldInternalUserID requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldInternalUserID: %w", err) + } + return oldValue.InternalUserID, nil +} + +// ResetInternalUserID resets all changes to the "internal_user_id" field. +func (m *ListingMutation) ResetInternalUserID() { + m.internal_user_id = nil +} + +// SetCategoryID sets the "category_id" field. +func (m *ListingMutation) SetCategoryID(u uuid.UUID) { + m.category_id = &u +} + +// CategoryID returns the value of the "category_id" field in the mutation. +func (m *ListingMutation) CategoryID() (r uuid.UUID, exists bool) { + v := m.category_id + if v == nil { + return + } + return *v, true +} + +// OldCategoryID returns the old "category_id" field's value of the Listing entity. +// If the Listing object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *ListingMutation) OldCategoryID(ctx context.Context) (v uuid.UUID, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldCategoryID is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldCategoryID requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldCategoryID: %w", err) + } + return oldValue.CategoryID, nil +} + +// ResetCategoryID resets all changes to the "category_id" field. +func (m *ListingMutation) ResetCategoryID() { + m.category_id = nil +} + +// SetPrimaryLocalityID sets the "primary_locality_id" field. +func (m *ListingMutation) SetPrimaryLocalityID(u uuid.UUID) { + m.primary_locality_id = &u +} + +// PrimaryLocalityID returns the value of the "primary_locality_id" field in the mutation. +func (m *ListingMutation) PrimaryLocalityID() (r uuid.UUID, exists bool) { + v := m.primary_locality_id + if v == nil { + return + } + return *v, true +} + +// OldPrimaryLocalityID returns the old "primary_locality_id" field's value of the Listing entity. +// If the Listing object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *ListingMutation) OldPrimaryLocalityID(ctx context.Context) (v uuid.UUID, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldPrimaryLocalityID is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldPrimaryLocalityID requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldPrimaryLocalityID: %w", err) + } + return oldValue.PrimaryLocalityID, nil +} + +// ResetPrimaryLocalityID resets all changes to the "primary_locality_id" field. +func (m *ListingMutation) ResetPrimaryLocalityID() { + m.primary_locality_id = nil +} + +// SetTitle sets the "title" field. +func (m *ListingMutation) SetTitle(s string) { + m.title = &s +} + +// Title returns the value of the "title" field in the mutation. +func (m *ListingMutation) 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 Listing entity. +// If the Listing object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *ListingMutation) 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 *ListingMutation) ResetTitle() { + m.title = nil +} + +// SetDescription sets the "description" field. +func (m *ListingMutation) SetDescription(s string) { + m.description = &s +} + +// Description returns the value of the "description" field in the mutation. +func (m *ListingMutation) Description() (r string, exists bool) { + v := m.description + if v == nil { + return + } + return *v, true +} + +// OldDescription returns the old "description" field's value of the Listing entity. +// If the Listing object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *ListingMutation) OldDescription(ctx context.Context) (v string, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldDescription is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldDescription requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldDescription: %w", err) + } + return oldValue.Description, nil +} + +// ResetDescription resets all changes to the "description" field. +func (m *ListingMutation) ResetDescription() { + m.description = nil +} + +// SetPriceType sets the "price_type" field. +func (m *ListingMutation) SetPriceType(lt listing.PriceType) { + m.price_type = < +} + +// PriceType returns the value of the "price_type" field in the mutation. +func (m *ListingMutation) PriceType() (r listing.PriceType, exists bool) { + v := m.price_type + if v == nil { + return + } + return *v, true +} + +// OldPriceType returns the old "price_type" field's value of the Listing entity. +// If the Listing object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *ListingMutation) OldPriceType(ctx context.Context) (v listing.PriceType, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldPriceType is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldPriceType requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldPriceType: %w", err) + } + return oldValue.PriceType, nil +} + +// ResetPriceType resets all changes to the "price_type" field. +func (m *ListingMutation) ResetPriceType() { + m.price_type = nil +} + +// SetPriceMinor sets the "price_minor" field. +func (m *ListingMutation) SetPriceMinor(i int) { + m.price_minor = &i + m.addprice_minor = nil +} + +// PriceMinor returns the value of the "price_minor" field in the mutation. +func (m *ListingMutation) PriceMinor() (r int, exists bool) { + v := m.price_minor + if v == nil { + return + } + return *v, true +} + +// OldPriceMinor returns the old "price_minor" field's value of the Listing entity. +// If the Listing object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *ListingMutation) OldPriceMinor(ctx context.Context) (v *int, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldPriceMinor is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldPriceMinor requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldPriceMinor: %w", err) + } + return oldValue.PriceMinor, nil +} + +// AddPriceMinor adds i to the "price_minor" field. +func (m *ListingMutation) AddPriceMinor(i int) { + if m.addprice_minor != nil { + *m.addprice_minor += i + } else { + m.addprice_minor = &i + } +} + +// AddedPriceMinor returns the value that was added to the "price_minor" field in this mutation. +func (m *ListingMutation) AddedPriceMinor() (r int, exists bool) { + v := m.addprice_minor + if v == nil { + return + } + return *v, true +} + +// ClearPriceMinor clears the value of the "price_minor" field. +func (m *ListingMutation) ClearPriceMinor() { + m.price_minor = nil + m.addprice_minor = nil + m.clearedFields[listing.FieldPriceMinor] = struct{}{} +} + +// PriceMinorCleared returns if the "price_minor" field was cleared in this mutation. +func (m *ListingMutation) PriceMinorCleared() bool { + _, ok := m.clearedFields[listing.FieldPriceMinor] + return ok +} + +// ResetPriceMinor resets all changes to the "price_minor" field. +func (m *ListingMutation) ResetPriceMinor() { + m.price_minor = nil + m.addprice_minor = nil + delete(m.clearedFields, listing.FieldPriceMinor) +} + +// SetCurrency sets the "currency" field. +func (m *ListingMutation) SetCurrency(s string) { + m.currency = &s +} + +// Currency returns the value of the "currency" field in the mutation. +func (m *ListingMutation) Currency() (r string, exists bool) { + v := m.currency + if v == nil { + return + } + return *v, true +} + +// OldCurrency returns the old "currency" field's value of the Listing entity. +// If the Listing object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *ListingMutation) OldCurrency(ctx context.Context) (v string, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldCurrency is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldCurrency requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldCurrency: %w", err) + } + return oldValue.Currency, nil +} + +// ResetCurrency resets all changes to the "currency" field. +func (m *ListingMutation) ResetCurrency() { + m.currency = nil +} + +// SetTravelsToCustomer sets the "travels_to_customer" field. +func (m *ListingMutation) SetTravelsToCustomer(b bool) { + m.travels_to_customer = &b +} + +// TravelsToCustomer returns the value of the "travels_to_customer" field in the mutation. +func (m *ListingMutation) TravelsToCustomer() (r bool, exists bool) { + v := m.travels_to_customer + if v == nil { + return + } + return *v, true +} + +// OldTravelsToCustomer returns the old "travels_to_customer" field's value of the Listing entity. +// If the Listing object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *ListingMutation) OldTravelsToCustomer(ctx context.Context) (v bool, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldTravelsToCustomer is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldTravelsToCustomer requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldTravelsToCustomer: %w", err) + } + return oldValue.TravelsToCustomer, nil +} + +// ResetTravelsToCustomer resets all changes to the "travels_to_customer" field. +func (m *ListingMutation) ResetTravelsToCustomer() { + m.travels_to_customer = nil +} + +// SetReceivesCustomer sets the "receives_customer" field. +func (m *ListingMutation) SetReceivesCustomer(b bool) { + m.receives_customer = &b +} + +// ReceivesCustomer returns the value of the "receives_customer" field in the mutation. +func (m *ListingMutation) ReceivesCustomer() (r bool, exists bool) { + v := m.receives_customer + if v == nil { + return + } + return *v, true +} + +// OldReceivesCustomer returns the old "receives_customer" field's value of the Listing entity. +// If the Listing object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *ListingMutation) OldReceivesCustomer(ctx context.Context) (v bool, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldReceivesCustomer is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldReceivesCustomer requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldReceivesCustomer: %w", err) + } + return oldValue.ReceivesCustomer, nil +} + +// ResetReceivesCustomer resets all changes to the "receives_customer" field. +func (m *ListingMutation) ResetReceivesCustomer() { + m.receives_customer = nil +} + +// SetRemoteServices sets the "remote_services" field. +func (m *ListingMutation) SetRemoteServices(b bool) { + m.remote_services = &b +} + +// RemoteServices returns the value of the "remote_services" field in the mutation. +func (m *ListingMutation) RemoteServices() (r bool, exists bool) { + v := m.remote_services + if v == nil { + return + } + return *v, true +} + +// OldRemoteServices returns the old "remote_services" field's value of the Listing entity. +// If the Listing object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *ListingMutation) OldRemoteServices(ctx context.Context) (v bool, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldRemoteServices is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldRemoteServices requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldRemoteServices: %w", err) + } + return oldValue.RemoteServices, nil +} + +// ResetRemoteServices resets all changes to the "remote_services" field. +func (m *ListingMutation) ResetRemoteServices() { + m.remote_services = nil +} + +// SetState sets the "state" field. +func (m *ListingMutation) SetState(l listing.State) { + m.state = &l +} + +// State returns the value of the "state" field in the mutation. +func (m *ListingMutation) State() (r listing.State, exists bool) { + v := m.state + if v == nil { + return + } + return *v, true +} + +// OldState returns the old "state" field's value of the Listing entity. +// If the Listing object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *ListingMutation) OldState(ctx context.Context) (v listing.State, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldState is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldState requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldState: %w", err) + } + return oldValue.State, nil +} + +// ResetState resets all changes to the "state" field. +func (m *ListingMutation) ResetState() { + m.state = nil +} + +// SetRevision sets the "revision" field. +func (m *ListingMutation) SetRevision(i int) { + m.revision = &i + m.addrevision = nil +} + +// Revision returns the value of the "revision" field in the mutation. +func (m *ListingMutation) 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 Listing entity. +// If the Listing object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *ListingMutation) 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 *ListingMutation) 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 *ListingMutation) 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 *ListingMutation) ResetRevision() { + m.revision = nil + m.addrevision = nil +} + +// SetCreatedAt sets the "created_at" field. +func (m *ListingMutation) SetCreatedAt(t time.Time) { + m.created_at = &t +} + +// CreatedAt returns the value of the "created_at" field in the mutation. +func (m *ListingMutation) CreatedAt() (r time.Time, exists bool) { + v := m.created_at + if v == nil { + return + } + return *v, true +} + +// OldCreatedAt returns the old "created_at" field's value of the Listing entity. +// If the Listing object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *ListingMutation) OldCreatedAt(ctx context.Context) (v time.Time, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldCreatedAt is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldCreatedAt requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldCreatedAt: %w", err) + } + return oldValue.CreatedAt, nil +} + +// ResetCreatedAt resets all changes to the "created_at" field. +func (m *ListingMutation) ResetCreatedAt() { + m.created_at = nil +} + +// SetUpdatedAt sets the "updated_at" field. +func (m *ListingMutation) SetUpdatedAt(t time.Time) { + m.updated_at = &t +} + +// UpdatedAt returns the value of the "updated_at" field in the mutation. +func (m *ListingMutation) UpdatedAt() (r time.Time, exists bool) { + v := m.updated_at + if v == nil { + return + } + return *v, true +} + +// OldUpdatedAt returns the old "updated_at" field's value of the Listing entity. +// If the Listing object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *ListingMutation) OldUpdatedAt(ctx context.Context) (v time.Time, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldUpdatedAt is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldUpdatedAt requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldUpdatedAt: %w", err) + } + return oldValue.UpdatedAt, nil +} + +// ResetUpdatedAt resets all changes to the "updated_at" field. +func (m *ListingMutation) ResetUpdatedAt() { + m.updated_at = nil +} + +// Where appends a list predicates to the ListingMutation builder. +func (m *ListingMutation) Where(ps ...predicate.Listing) { + m.predicates = append(m.predicates, ps...) +} + +// WhereP appends storage-level predicates to the ListingMutation builder. Using this method, +// users can use type-assertion to append predicates that do not depend on any generated package. +func (m *ListingMutation) WhereP(ps ...func(*sql.Selector)) { + p := make([]predicate.Listing, len(ps)) + for i := range ps { + p[i] = ps[i] + } + m.Where(p...) +} + +// Op returns the operation name. +func (m *ListingMutation) Op() Op { + return m.op +} + +// SetOp allows setting the mutation operation. +func (m *ListingMutation) SetOp(op Op) { + m.op = op +} + +// Type returns the node type of this mutation (Listing). +func (m *ListingMutation) 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 *ListingMutation) Fields() []string { + fields := make([]string, 0, 15) + if m.internal_user_id != nil { + fields = append(fields, listing.FieldInternalUserID) + } + if m.category_id != nil { + fields = append(fields, listing.FieldCategoryID) + } + if m.primary_locality_id != nil { + fields = append(fields, listing.FieldPrimaryLocalityID) + } + if m.title != nil { + fields = append(fields, listing.FieldTitle) + } + if m.description != nil { + fields = append(fields, listing.FieldDescription) + } + if m.price_type != nil { + fields = append(fields, listing.FieldPriceType) + } + if m.price_minor != nil { + fields = append(fields, listing.FieldPriceMinor) + } + if m.currency != nil { + fields = append(fields, listing.FieldCurrency) + } + if m.travels_to_customer != nil { + fields = append(fields, listing.FieldTravelsToCustomer) + } + if m.receives_customer != nil { + fields = append(fields, listing.FieldReceivesCustomer) + } + if m.remote_services != nil { + fields = append(fields, listing.FieldRemoteServices) + } + if m.state != nil { + fields = append(fields, listing.FieldState) + } + if m.revision != nil { + fields = append(fields, listing.FieldRevision) + } + if m.created_at != nil { + fields = append(fields, listing.FieldCreatedAt) + } + if m.updated_at != nil { + fields = append(fields, listing.FieldUpdatedAt) + } + 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 *ListingMutation) Field(name string) (ent.Value, bool) { + switch name { + case listing.FieldInternalUserID: + return m.InternalUserID() + case listing.FieldCategoryID: + return m.CategoryID() + case listing.FieldPrimaryLocalityID: + return m.PrimaryLocalityID() + case listing.FieldTitle: + return m.Title() + case listing.FieldDescription: + return m.Description() + case listing.FieldPriceType: + return m.PriceType() + case listing.FieldPriceMinor: + return m.PriceMinor() + case listing.FieldCurrency: + return m.Currency() + case listing.FieldTravelsToCustomer: + return m.TravelsToCustomer() + case listing.FieldReceivesCustomer: + return m.ReceivesCustomer() + case listing.FieldRemoteServices: + return m.RemoteServices() + case listing.FieldState: + return m.State() + case listing.FieldRevision: + return m.Revision() + case listing.FieldCreatedAt: + return m.CreatedAt() + case listing.FieldUpdatedAt: + return m.UpdatedAt() + } + 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 *ListingMutation) OldField(ctx context.Context, name string) (ent.Value, error) { + switch name { + case listing.FieldInternalUserID: + return m.OldInternalUserID(ctx) + case listing.FieldCategoryID: + return m.OldCategoryID(ctx) + case listing.FieldPrimaryLocalityID: + return m.OldPrimaryLocalityID(ctx) + case listing.FieldTitle: + return m.OldTitle(ctx) + case listing.FieldDescription: + return m.OldDescription(ctx) + case listing.FieldPriceType: + return m.OldPriceType(ctx) + case listing.FieldPriceMinor: + return m.OldPriceMinor(ctx) + case listing.FieldCurrency: + return m.OldCurrency(ctx) + case listing.FieldTravelsToCustomer: + return m.OldTravelsToCustomer(ctx) + case listing.FieldReceivesCustomer: + return m.OldReceivesCustomer(ctx) + case listing.FieldRemoteServices: + return m.OldRemoteServices(ctx) + case listing.FieldState: + return m.OldState(ctx) + case listing.FieldRevision: + return m.OldRevision(ctx) + case listing.FieldCreatedAt: + return m.OldCreatedAt(ctx) + case listing.FieldUpdatedAt: + return m.OldUpdatedAt(ctx) + } + return nil, fmt.Errorf("unknown Listing 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 *ListingMutation) SetField(name string, value ent.Value) error { + switch name { + case listing.FieldInternalUserID: + v, ok := value.(uuid.UUID) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetInternalUserID(v) + return nil + case listing.FieldCategoryID: + v, ok := value.(uuid.UUID) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetCategoryID(v) + return nil + case listing.FieldPrimaryLocalityID: + v, ok := value.(uuid.UUID) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetPrimaryLocalityID(v) + return nil + case listing.FieldTitle: + v, ok := value.(string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetTitle(v) + return nil + case listing.FieldDescription: + v, ok := value.(string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetDescription(v) + return nil + case listing.FieldPriceType: + v, ok := value.(listing.PriceType) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetPriceType(v) + return nil + case listing.FieldPriceMinor: + v, ok := value.(int) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetPriceMinor(v) + return nil + case listing.FieldCurrency: + v, ok := value.(string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetCurrency(v) + return nil + case listing.FieldTravelsToCustomer: + v, ok := value.(bool) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetTravelsToCustomer(v) + return nil + case listing.FieldReceivesCustomer: + v, ok := value.(bool) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetReceivesCustomer(v) + return nil + case listing.FieldRemoteServices: + v, ok := value.(bool) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetRemoteServices(v) + return nil + case listing.FieldState: + v, ok := value.(listing.State) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetState(v) + return nil + case listing.FieldRevision: + v, ok := value.(int) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetRevision(v) + return nil + case listing.FieldCreatedAt: + v, ok := value.(time.Time) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetCreatedAt(v) + return nil + case listing.FieldUpdatedAt: + v, ok := value.(time.Time) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetUpdatedAt(v) + return nil + } + return fmt.Errorf("unknown Listing field %s", name) +} + +// AddedFields returns all numeric fields that were incremented/decremented during +// this mutation. +func (m *ListingMutation) AddedFields() []string { + var fields []string + if m.addprice_minor != nil { + fields = append(fields, listing.FieldPriceMinor) + } + if m.addrevision != nil { + fields = append(fields, listing.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 *ListingMutation) AddedField(name string) (ent.Value, bool) { + switch name { + case listing.FieldPriceMinor: + return m.AddedPriceMinor() + case listing.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 *ListingMutation) AddField(name string, value ent.Value) error { + switch name { + case listing.FieldPriceMinor: + v, ok := value.(int) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.AddPriceMinor(v) + return nil + case listing.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 Listing numeric field %s", name) +} + +// ClearedFields returns all nullable fields that were cleared during this +// mutation. +func (m *ListingMutation) ClearedFields() []string { + var fields []string + if m.FieldCleared(listing.FieldPriceMinor) { + fields = append(fields, listing.FieldPriceMinor) + } + return fields +} + +// FieldCleared returns a boolean indicating if a field with the given name was +// cleared in this mutation. +func (m *ListingMutation) 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 *ListingMutation) ClearField(name string) error { + switch name { + case listing.FieldPriceMinor: + m.ClearPriceMinor() + return nil + } + return fmt.Errorf("unknown Listing 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 *ListingMutation) ResetField(name string) error { + switch name { + case listing.FieldInternalUserID: + m.ResetInternalUserID() + return nil + case listing.FieldCategoryID: + m.ResetCategoryID() + return nil + case listing.FieldPrimaryLocalityID: + m.ResetPrimaryLocalityID() + return nil + case listing.FieldTitle: + m.ResetTitle() + return nil + case listing.FieldDescription: + m.ResetDescription() + return nil + case listing.FieldPriceType: + m.ResetPriceType() + return nil + case listing.FieldPriceMinor: + m.ResetPriceMinor() + return nil + case listing.FieldCurrency: + m.ResetCurrency() + return nil + case listing.FieldTravelsToCustomer: + m.ResetTravelsToCustomer() + return nil + case listing.FieldReceivesCustomer: + m.ResetReceivesCustomer() + return nil + case listing.FieldRemoteServices: + m.ResetRemoteServices() + return nil + case listing.FieldState: + m.ResetState() + return nil + case listing.FieldRevision: + m.ResetRevision() + return nil + case listing.FieldCreatedAt: + m.ResetCreatedAt() + return nil + case listing.FieldUpdatedAt: + m.ResetUpdatedAt() + return nil + } + return fmt.Errorf("unknown Listing field %s", name) +} + +// AddedEdges returns all edge names that were set/added in this mutation. +func (m *ListingMutation) AddedEdges() []string { + edges := make([]string, 0, 0) + return edges +} + +// AddedIDs returns all IDs (to other nodes) that were added for the given edge +// name in this mutation. +func (m *ListingMutation) AddedIDs(name string) []ent.Value { + return nil +} + +// RemovedEdges returns all edge names that were removed in this mutation. +func (m *ListingMutation) RemovedEdges() []string { + edges := make([]string, 0, 0) + return edges +} + +// RemovedIDs returns all IDs (to other nodes) that were removed for the edge with +// the given name in this mutation. +func (m *ListingMutation) RemovedIDs(name string) []ent.Value { + return nil +} + +// ClearedEdges returns all edge names that were cleared in this mutation. +func (m *ListingMutation) ClearedEdges() []string { + edges := make([]string, 0, 0) + return edges +} + +// EdgeCleared returns a boolean which indicates if the edge with the given name +// was cleared in this mutation. +func (m *ListingMutation) EdgeCleared(name string) bool { + return false +} + +// ClearEdge clears the value of the edge with the given name. It returns an error +// if that edge is not defined in the schema. +func (m *ListingMutation) ClearEdge(name string) error { + return fmt.Errorf("unknown Listing 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 *ListingMutation) ResetEdge(name string) error { + return fmt.Errorf("unknown Listing edge %s", name) +} + +// ListingEventMutation represents an operation that mutates the ListingEvent nodes in the graph. +type ListingEventMutation struct { + config + op Op + typ string + id *uuid.UUID + listing_id *uuid.UUID + actor_internal_user_id *uuid.UUID + event_type *listingevent.EventType + from_state *string + to_state *string + revision *int + addrevision *int + reason *string + created_at *time.Time + clearedFields map[string]struct{} + done bool + oldValue func(context.Context) (*ListingEvent, error) + predicates []predicate.ListingEvent +} + +var _ ent.Mutation = (*ListingEventMutation)(nil) + +// listingeventOption allows management of the mutation configuration using functional options. +type listingeventOption func(*ListingEventMutation) + +// newListingEventMutation creates new mutation for the ListingEvent entity. +func newListingEventMutation(c config, op Op, opts ...listingeventOption) *ListingEventMutation { + m := &ListingEventMutation{ + config: c, + op: op, + typ: TypeListingEvent, + clearedFields: make(map[string]struct{}), + } + for _, opt := range opts { + opt(m) + } + return m +} + +// withListingEventID sets the ID field of the mutation. +func withListingEventID(id uuid.UUID) listingeventOption { + return func(m *ListingEventMutation) { + var ( + err error + once sync.Once + value *ListingEvent + ) + m.oldValue = func(ctx context.Context) (*ListingEvent, error) { + once.Do(func() { + if m.done { + err = errors.New("querying old values post mutation is not allowed") + } else { + value, err = m.Client().ListingEvent.Get(ctx, id) + } + }) + return value, err + } + m.id = &id + } +} + +// withListingEvent sets the old ListingEvent of the mutation. +func withListingEvent(node *ListingEvent) listingeventOption { + return func(m *ListingEventMutation) { + m.oldValue = func(context.Context) (*ListingEvent, 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 ListingEventMutation) 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 ListingEventMutation) 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 ListingEvent entities. +func (m *ListingEventMutation) 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 *ListingEventMutation) 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 *ListingEventMutation) 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().ListingEvent.Query().Where(m.predicates...).IDs(ctx) + default: + return nil, fmt.Errorf("IDs is not allowed on %s operations", m.op) + } +} + +// SetListingID sets the "listing_id" field. +func (m *ListingEventMutation) SetListingID(u uuid.UUID) { + m.listing_id = &u +} + +// ListingID returns the value of the "listing_id" field in the mutation. +func (m *ListingEventMutation) ListingID() (r uuid.UUID, exists bool) { + v := m.listing_id + if v == nil { + return + } + return *v, true +} + +// OldListingID returns the old "listing_id" field's value of the ListingEvent entity. +// If the ListingEvent object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *ListingEventMutation) OldListingID(ctx context.Context) (v uuid.UUID, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldListingID is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldListingID requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldListingID: %w", err) + } + return oldValue.ListingID, nil +} + +// ResetListingID resets all changes to the "listing_id" field. +func (m *ListingEventMutation) ResetListingID() { + m.listing_id = nil +} + +// SetActorInternalUserID sets the "actor_internal_user_id" field. +func (m *ListingEventMutation) SetActorInternalUserID(u uuid.UUID) { + m.actor_internal_user_id = &u +} + +// ActorInternalUserID returns the value of the "actor_internal_user_id" field in the mutation. +func (m *ListingEventMutation) ActorInternalUserID() (r uuid.UUID, exists bool) { + v := m.actor_internal_user_id + if v == nil { + return + } + return *v, true +} + +// OldActorInternalUserID returns the old "actor_internal_user_id" field's value of the ListingEvent entity. +// If the ListingEvent object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *ListingEventMutation) OldActorInternalUserID(ctx context.Context) (v uuid.UUID, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldActorInternalUserID is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldActorInternalUserID requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldActorInternalUserID: %w", err) + } + return oldValue.ActorInternalUserID, nil +} + +// ResetActorInternalUserID resets all changes to the "actor_internal_user_id" field. +func (m *ListingEventMutation) ResetActorInternalUserID() { + m.actor_internal_user_id = nil +} + +// SetEventType sets the "event_type" field. +func (m *ListingEventMutation) SetEventType(lt listingevent.EventType) { + m.event_type = < +} + +// EventType returns the value of the "event_type" field in the mutation. +func (m *ListingEventMutation) EventType() (r listingevent.EventType, exists bool) { + v := m.event_type + if v == nil { + return + } + return *v, true +} + +// OldEventType returns the old "event_type" field's value of the ListingEvent entity. +// If the ListingEvent object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *ListingEventMutation) OldEventType(ctx context.Context) (v listingevent.EventType, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldEventType is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldEventType requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldEventType: %w", err) + } + return oldValue.EventType, nil +} + +// ResetEventType resets all changes to the "event_type" field. +func (m *ListingEventMutation) ResetEventType() { + m.event_type = nil +} + +// SetFromState sets the "from_state" field. +func (m *ListingEventMutation) SetFromState(s string) { + m.from_state = &s +} + +// FromState returns the value of the "from_state" field in the mutation. +func (m *ListingEventMutation) FromState() (r string, exists bool) { + v := m.from_state + if v == nil { + return + } + return *v, true +} + +// OldFromState returns the old "from_state" field's value of the ListingEvent entity. +// If the ListingEvent object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *ListingEventMutation) OldFromState(ctx context.Context) (v *string, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldFromState is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldFromState requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldFromState: %w", err) + } + return oldValue.FromState, nil +} + +// ClearFromState clears the value of the "from_state" field. +func (m *ListingEventMutation) ClearFromState() { + m.from_state = nil + m.clearedFields[listingevent.FieldFromState] = struct{}{} +} + +// FromStateCleared returns if the "from_state" field was cleared in this mutation. +func (m *ListingEventMutation) FromStateCleared() bool { + _, ok := m.clearedFields[listingevent.FieldFromState] + return ok +} + +// ResetFromState resets all changes to the "from_state" field. +func (m *ListingEventMutation) ResetFromState() { + m.from_state = nil + delete(m.clearedFields, listingevent.FieldFromState) +} + +// SetToState sets the "to_state" field. +func (m *ListingEventMutation) SetToState(s string) { + m.to_state = &s +} + +// ToState returns the value of the "to_state" field in the mutation. +func (m *ListingEventMutation) ToState() (r string, exists bool) { + v := m.to_state + if v == nil { + return + } + return *v, true +} + +// OldToState returns the old "to_state" field's value of the ListingEvent entity. +// If the ListingEvent object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *ListingEventMutation) OldToState(ctx context.Context) (v string, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldToState is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldToState requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldToState: %w", err) + } + return oldValue.ToState, nil +} + +// ResetToState resets all changes to the "to_state" field. +func (m *ListingEventMutation) ResetToState() { + m.to_state = nil +} + +// SetRevision sets the "revision" field. +func (m *ListingEventMutation) SetRevision(i int) { + m.revision = &i + m.addrevision = nil +} + +// Revision returns the value of the "revision" field in the mutation. +func (m *ListingEventMutation) 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 ListingEvent entity. +// If the ListingEvent object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *ListingEventMutation) 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 *ListingEventMutation) 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 *ListingEventMutation) 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 *ListingEventMutation) ResetRevision() { + m.revision = nil + m.addrevision = nil +} + +// SetReason sets the "reason" field. +func (m *ListingEventMutation) SetReason(s string) { + m.reason = &s +} + +// Reason returns the value of the "reason" field in the mutation. +func (m *ListingEventMutation) Reason() (r string, exists bool) { + v := m.reason + if v == nil { + return + } + return *v, true +} + +// OldReason returns the old "reason" field's value of the ListingEvent entity. +// If the ListingEvent object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *ListingEventMutation) OldReason(ctx context.Context) (v *string, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldReason is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldReason requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldReason: %w", err) + } + return oldValue.Reason, nil +} + +// ClearReason clears the value of the "reason" field. +func (m *ListingEventMutation) ClearReason() { + m.reason = nil + m.clearedFields[listingevent.FieldReason] = struct{}{} +} + +// ReasonCleared returns if the "reason" field was cleared in this mutation. +func (m *ListingEventMutation) ReasonCleared() bool { + _, ok := m.clearedFields[listingevent.FieldReason] + return ok +} + +// ResetReason resets all changes to the "reason" field. +func (m *ListingEventMutation) ResetReason() { + m.reason = nil + delete(m.clearedFields, listingevent.FieldReason) +} + +// SetCreatedAt sets the "created_at" field. +func (m *ListingEventMutation) SetCreatedAt(t time.Time) { + m.created_at = &t +} + +// CreatedAt returns the value of the "created_at" field in the mutation. +func (m *ListingEventMutation) CreatedAt() (r time.Time, exists bool) { + v := m.created_at + if v == nil { + return + } + return *v, true +} + +// OldCreatedAt returns the old "created_at" field's value of the ListingEvent entity. +// If the ListingEvent object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *ListingEventMutation) OldCreatedAt(ctx context.Context) (v time.Time, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldCreatedAt is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldCreatedAt requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldCreatedAt: %w", err) + } + return oldValue.CreatedAt, nil +} + +// ResetCreatedAt resets all changes to the "created_at" field. +func (m *ListingEventMutation) ResetCreatedAt() { + m.created_at = nil +} + +// Where appends a list predicates to the ListingEventMutation builder. +func (m *ListingEventMutation) Where(ps ...predicate.ListingEvent) { + m.predicates = append(m.predicates, ps...) +} + +// WhereP appends storage-level predicates to the ListingEventMutation builder. Using this method, +// users can use type-assertion to append predicates that do not depend on any generated package. +func (m *ListingEventMutation) WhereP(ps ...func(*sql.Selector)) { + p := make([]predicate.ListingEvent, len(ps)) + for i := range ps { + p[i] = ps[i] + } + m.Where(p...) +} + +// Op returns the operation name. +func (m *ListingEventMutation) Op() Op { + return m.op +} + +// SetOp allows setting the mutation operation. +func (m *ListingEventMutation) SetOp(op Op) { + m.op = op +} + +// Type returns the node type of this mutation (ListingEvent). +func (m *ListingEventMutation) 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 *ListingEventMutation) Fields() []string { + fields := make([]string, 0, 8) + if m.listing_id != nil { + fields = append(fields, listingevent.FieldListingID) + } + if m.actor_internal_user_id != nil { + fields = append(fields, listingevent.FieldActorInternalUserID) + } + if m.event_type != nil { + fields = append(fields, listingevent.FieldEventType) + } + if m.from_state != nil { + fields = append(fields, listingevent.FieldFromState) + } + if m.to_state != nil { + fields = append(fields, listingevent.FieldToState) + } + if m.revision != nil { + fields = append(fields, listingevent.FieldRevision) + } + if m.reason != nil { + fields = append(fields, listingevent.FieldReason) + } + if m.created_at != nil { + fields = append(fields, listingevent.FieldCreatedAt) + } + 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 *ListingEventMutation) Field(name string) (ent.Value, bool) { + switch name { + case listingevent.FieldListingID: + return m.ListingID() + case listingevent.FieldActorInternalUserID: + return m.ActorInternalUserID() + case listingevent.FieldEventType: + return m.EventType() + case listingevent.FieldFromState: + return m.FromState() + case listingevent.FieldToState: + return m.ToState() + case listingevent.FieldRevision: + return m.Revision() + case listingevent.FieldReason: + return m.Reason() + case listingevent.FieldCreatedAt: + return m.CreatedAt() + } + 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 *ListingEventMutation) OldField(ctx context.Context, name string) (ent.Value, error) { + switch name { + case listingevent.FieldListingID: + return m.OldListingID(ctx) + case listingevent.FieldActorInternalUserID: + return m.OldActorInternalUserID(ctx) + case listingevent.FieldEventType: + return m.OldEventType(ctx) + case listingevent.FieldFromState: + return m.OldFromState(ctx) + case listingevent.FieldToState: + return m.OldToState(ctx) + case listingevent.FieldRevision: + return m.OldRevision(ctx) + case listingevent.FieldReason: + return m.OldReason(ctx) + case listingevent.FieldCreatedAt: + return m.OldCreatedAt(ctx) + } + return nil, fmt.Errorf("unknown ListingEvent 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 *ListingEventMutation) SetField(name string, value ent.Value) error { + switch name { + case listingevent.FieldListingID: + v, ok := value.(uuid.UUID) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetListingID(v) + return nil + case listingevent.FieldActorInternalUserID: + v, ok := value.(uuid.UUID) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetActorInternalUserID(v) + return nil + case listingevent.FieldEventType: + v, ok := value.(listingevent.EventType) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetEventType(v) + return nil + case listingevent.FieldFromState: + v, ok := value.(string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetFromState(v) + return nil + case listingevent.FieldToState: + v, ok := value.(string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetToState(v) + return nil + case listingevent.FieldRevision: + v, ok := value.(int) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetRevision(v) + return nil + case listingevent.FieldReason: + v, ok := value.(string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetReason(v) + return nil + case listingevent.FieldCreatedAt: + v, ok := value.(time.Time) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetCreatedAt(v) + return nil + } + return fmt.Errorf("unknown ListingEvent field %s", name) +} + +// AddedFields returns all numeric fields that were incremented/decremented during +// this mutation. +func (m *ListingEventMutation) AddedFields() []string { + var fields []string + if m.addrevision != nil { + fields = append(fields, listingevent.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 *ListingEventMutation) AddedField(name string) (ent.Value, bool) { + switch name { + case listingevent.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 *ListingEventMutation) AddField(name string, value ent.Value) error { + switch name { + case listingevent.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 ListingEvent numeric field %s", name) +} + +// ClearedFields returns all nullable fields that were cleared during this +// mutation. +func (m *ListingEventMutation) ClearedFields() []string { + var fields []string + if m.FieldCleared(listingevent.FieldFromState) { + fields = append(fields, listingevent.FieldFromState) + } + if m.FieldCleared(listingevent.FieldReason) { + fields = append(fields, listingevent.FieldReason) + } + return fields +} + +// FieldCleared returns a boolean indicating if a field with the given name was +// cleared in this mutation. +func (m *ListingEventMutation) 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 *ListingEventMutation) ClearField(name string) error { + switch name { + case listingevent.FieldFromState: + m.ClearFromState() + return nil + case listingevent.FieldReason: + m.ClearReason() + return nil + } + return fmt.Errorf("unknown ListingEvent 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 *ListingEventMutation) ResetField(name string) error { + switch name { + case listingevent.FieldListingID: + m.ResetListingID() + return nil + case listingevent.FieldActorInternalUserID: + m.ResetActorInternalUserID() + return nil + case listingevent.FieldEventType: + m.ResetEventType() + return nil + case listingevent.FieldFromState: + m.ResetFromState() + return nil + case listingevent.FieldToState: + m.ResetToState() + return nil + case listingevent.FieldRevision: + m.ResetRevision() + return nil + case listingevent.FieldReason: + m.ResetReason() + return nil + case listingevent.FieldCreatedAt: + m.ResetCreatedAt() + return nil + } + return fmt.Errorf("unknown ListingEvent field %s", name) +} + +// AddedEdges returns all edge names that were set/added in this mutation. +func (m *ListingEventMutation) AddedEdges() []string { + edges := make([]string, 0, 0) + return edges +} + +// AddedIDs returns all IDs (to other nodes) that were added for the given edge +// name in this mutation. +func (m *ListingEventMutation) AddedIDs(name string) []ent.Value { + return nil +} + +// RemovedEdges returns all edge names that were removed in this mutation. +func (m *ListingEventMutation) RemovedEdges() []string { + edges := make([]string, 0, 0) + return edges +} + +// RemovedIDs returns all IDs (to other nodes) that were removed for the edge with +// the given name in this mutation. +func (m *ListingEventMutation) RemovedIDs(name string) []ent.Value { + return nil +} + +// ClearedEdges returns all edge names that were cleared in this mutation. +func (m *ListingEventMutation) ClearedEdges() []string { + edges := make([]string, 0, 0) + return edges +} + +// EdgeCleared returns a boolean which indicates if the edge with the given name +// was cleared in this mutation. +func (m *ListingEventMutation) EdgeCleared(name string) bool { + return false +} + +// ClearEdge clears the value of the edge with the given name. It returns an error +// if that edge is not defined in the schema. +func (m *ListingEventMutation) ClearEdge(name string) error { + return fmt.Errorf("unknown ListingEvent 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 *ListingEventMutation) ResetEdge(name string) error { + return fmt.Errorf("unknown ListingEvent edge %s", name) +} + +// ListingMediaMutation represents an operation that mutates the ListingMedia nodes in the graph. +type ListingMediaMutation struct { + config + op Op + typ string + id *uuid.UUID + listing_id *uuid.UUID + ordinal *int + addordinal *int + content_type *string + byte_size *int64 + addbyte_size *int64 + checksum_sha256 *string + object_reference *string + state *listingmedia.State + created_at *time.Time + updated_at *time.Time + clearedFields map[string]struct{} + done bool + oldValue func(context.Context) (*ListingMedia, error) + predicates []predicate.ListingMedia +} + +var _ ent.Mutation = (*ListingMediaMutation)(nil) + +// listingmediaOption allows management of the mutation configuration using functional options. +type listingmediaOption func(*ListingMediaMutation) + +// newListingMediaMutation creates new mutation for the ListingMedia entity. +func newListingMediaMutation(c config, op Op, opts ...listingmediaOption) *ListingMediaMutation { + m := &ListingMediaMutation{ + config: c, + op: op, + typ: TypeListingMedia, + clearedFields: make(map[string]struct{}), + } + for _, opt := range opts { + opt(m) + } + return m +} + +// withListingMediaID sets the ID field of the mutation. +func withListingMediaID(id uuid.UUID) listingmediaOption { + return func(m *ListingMediaMutation) { + var ( + err error + once sync.Once + value *ListingMedia + ) + m.oldValue = func(ctx context.Context) (*ListingMedia, error) { + once.Do(func() { + if m.done { + err = errors.New("querying old values post mutation is not allowed") + } else { + value, err = m.Client().ListingMedia.Get(ctx, id) + } + }) + return value, err + } + m.id = &id + } +} + +// withListingMedia sets the old ListingMedia of the mutation. +func withListingMedia(node *ListingMedia) listingmediaOption { + return func(m *ListingMediaMutation) { + m.oldValue = func(context.Context) (*ListingMedia, 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 ListingMediaMutation) 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 ListingMediaMutation) 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 ListingMedia entities. +func (m *ListingMediaMutation) 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 *ListingMediaMutation) 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 *ListingMediaMutation) 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().ListingMedia.Query().Where(m.predicates...).IDs(ctx) + default: + return nil, fmt.Errorf("IDs is not allowed on %s operations", m.op) + } +} + +// SetListingID sets the "listing_id" field. +func (m *ListingMediaMutation) SetListingID(u uuid.UUID) { + m.listing_id = &u +} + +// ListingID returns the value of the "listing_id" field in the mutation. +func (m *ListingMediaMutation) ListingID() (r uuid.UUID, exists bool) { + v := m.listing_id + if v == nil { + return + } + return *v, true +} + +// OldListingID returns the old "listing_id" field's value of the ListingMedia entity. +// If the ListingMedia object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *ListingMediaMutation) OldListingID(ctx context.Context) (v uuid.UUID, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldListingID is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldListingID requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldListingID: %w", err) + } + return oldValue.ListingID, nil +} + +// ResetListingID resets all changes to the "listing_id" field. +func (m *ListingMediaMutation) ResetListingID() { + m.listing_id = nil +} + +// SetOrdinal sets the "ordinal" field. +func (m *ListingMediaMutation) SetOrdinal(i int) { + m.ordinal = &i + m.addordinal = nil +} + +// Ordinal returns the value of the "ordinal" field in the mutation. +func (m *ListingMediaMutation) Ordinal() (r int, exists bool) { + v := m.ordinal + if v == nil { + return + } + return *v, true +} + +// OldOrdinal returns the old "ordinal" field's value of the ListingMedia entity. +// If the ListingMedia object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *ListingMediaMutation) OldOrdinal(ctx context.Context) (v int, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldOrdinal is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldOrdinal requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldOrdinal: %w", err) + } + return oldValue.Ordinal, nil +} + +// AddOrdinal adds i to the "ordinal" field. +func (m *ListingMediaMutation) AddOrdinal(i int) { + if m.addordinal != nil { + *m.addordinal += i + } else { + m.addordinal = &i + } +} + +// AddedOrdinal returns the value that was added to the "ordinal" field in this mutation. +func (m *ListingMediaMutation) AddedOrdinal() (r int, exists bool) { + v := m.addordinal + if v == nil { + return + } + return *v, true +} + +// ResetOrdinal resets all changes to the "ordinal" field. +func (m *ListingMediaMutation) ResetOrdinal() { + m.ordinal = nil + m.addordinal = nil +} + +// SetContentType sets the "content_type" field. +func (m *ListingMediaMutation) SetContentType(s string) { + m.content_type = &s +} + +// ContentType returns the value of the "content_type" field in the mutation. +func (m *ListingMediaMutation) ContentType() (r string, exists bool) { + v := m.content_type + if v == nil { + return + } + return *v, true +} + +// OldContentType returns the old "content_type" field's value of the ListingMedia entity. +// If the ListingMedia object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *ListingMediaMutation) OldContentType(ctx context.Context) (v string, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldContentType is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldContentType requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldContentType: %w", err) + } + return oldValue.ContentType, nil +} + +// ResetContentType resets all changes to the "content_type" field. +func (m *ListingMediaMutation) ResetContentType() { + m.content_type = nil +} + +// SetByteSize sets the "byte_size" field. +func (m *ListingMediaMutation) SetByteSize(i int64) { + m.byte_size = &i + m.addbyte_size = nil +} + +// ByteSize returns the value of the "byte_size" field in the mutation. +func (m *ListingMediaMutation) ByteSize() (r int64, exists bool) { + v := m.byte_size + if v == nil { + return + } + return *v, true +} + +// OldByteSize returns the old "byte_size" field's value of the ListingMedia entity. +// If the ListingMedia object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *ListingMediaMutation) OldByteSize(ctx context.Context) (v int64, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldByteSize is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldByteSize requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldByteSize: %w", err) + } + return oldValue.ByteSize, nil +} + +// AddByteSize adds i to the "byte_size" field. +func (m *ListingMediaMutation) AddByteSize(i int64) { + if m.addbyte_size != nil { + *m.addbyte_size += i + } else { + m.addbyte_size = &i + } +} + +// AddedByteSize returns the value that was added to the "byte_size" field in this mutation. +func (m *ListingMediaMutation) AddedByteSize() (r int64, exists bool) { + v := m.addbyte_size + if v == nil { + return + } + return *v, true +} + +// ResetByteSize resets all changes to the "byte_size" field. +func (m *ListingMediaMutation) ResetByteSize() { + m.byte_size = nil + m.addbyte_size = nil +} + +// SetChecksumSha256 sets the "checksum_sha256" field. +func (m *ListingMediaMutation) SetChecksumSha256(s string) { + m.checksum_sha256 = &s +} + +// ChecksumSha256 returns the value of the "checksum_sha256" field in the mutation. +func (m *ListingMediaMutation) ChecksumSha256() (r string, exists bool) { + v := m.checksum_sha256 + if v == nil { + return + } + return *v, true +} + +// OldChecksumSha256 returns the old "checksum_sha256" field's value of the ListingMedia entity. +// If the ListingMedia object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *ListingMediaMutation) OldChecksumSha256(ctx context.Context) (v string, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldChecksumSha256 is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldChecksumSha256 requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldChecksumSha256: %w", err) + } + return oldValue.ChecksumSha256, nil +} + +// ResetChecksumSha256 resets all changes to the "checksum_sha256" field. +func (m *ListingMediaMutation) ResetChecksumSha256() { + m.checksum_sha256 = nil +} + +// SetObjectReference sets the "object_reference" field. +func (m *ListingMediaMutation) SetObjectReference(s string) { + m.object_reference = &s +} + +// ObjectReference returns the value of the "object_reference" field in the mutation. +func (m *ListingMediaMutation) ObjectReference() (r string, exists bool) { + v := m.object_reference + if v == nil { + return + } + return *v, true +} + +// OldObjectReference returns the old "object_reference" field's value of the ListingMedia entity. +// If the ListingMedia object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *ListingMediaMutation) OldObjectReference(ctx context.Context) (v string, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldObjectReference is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldObjectReference requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldObjectReference: %w", err) + } + return oldValue.ObjectReference, nil +} + +// ResetObjectReference resets all changes to the "object_reference" field. +func (m *ListingMediaMutation) ResetObjectReference() { + m.object_reference = nil +} + +// SetState sets the "state" field. +func (m *ListingMediaMutation) SetState(l listingmedia.State) { + m.state = &l +} + +// State returns the value of the "state" field in the mutation. +func (m *ListingMediaMutation) State() (r listingmedia.State, exists bool) { + v := m.state + if v == nil { + return + } + return *v, true +} + +// OldState returns the old "state" field's value of the ListingMedia entity. +// If the ListingMedia object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *ListingMediaMutation) OldState(ctx context.Context) (v listingmedia.State, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldState is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldState requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldState: %w", err) + } + return oldValue.State, nil +} + +// ResetState resets all changes to the "state" field. +func (m *ListingMediaMutation) ResetState() { + m.state = nil +} + +// SetCreatedAt sets the "created_at" field. +func (m *ListingMediaMutation) SetCreatedAt(t time.Time) { + m.created_at = &t +} + +// CreatedAt returns the value of the "created_at" field in the mutation. +func (m *ListingMediaMutation) CreatedAt() (r time.Time, exists bool) { + v := m.created_at + if v == nil { + return + } + return *v, true +} + +// OldCreatedAt returns the old "created_at" field's value of the ListingMedia entity. +// If the ListingMedia object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *ListingMediaMutation) OldCreatedAt(ctx context.Context) (v time.Time, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldCreatedAt is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldCreatedAt requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldCreatedAt: %w", err) + } + return oldValue.CreatedAt, nil +} + +// ResetCreatedAt resets all changes to the "created_at" field. +func (m *ListingMediaMutation) ResetCreatedAt() { + m.created_at = nil +} + +// SetUpdatedAt sets the "updated_at" field. +func (m *ListingMediaMutation) SetUpdatedAt(t time.Time) { + m.updated_at = &t +} + +// UpdatedAt returns the value of the "updated_at" field in the mutation. +func (m *ListingMediaMutation) UpdatedAt() (r time.Time, exists bool) { + v := m.updated_at + if v == nil { + return + } + return *v, true +} + +// OldUpdatedAt returns the old "updated_at" field's value of the ListingMedia entity. +// If the ListingMedia object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *ListingMediaMutation) OldUpdatedAt(ctx context.Context) (v time.Time, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldUpdatedAt is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldUpdatedAt requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldUpdatedAt: %w", err) + } + return oldValue.UpdatedAt, nil +} + +// ResetUpdatedAt resets all changes to the "updated_at" field. +func (m *ListingMediaMutation) ResetUpdatedAt() { + m.updated_at = nil +} + +// Where appends a list predicates to the ListingMediaMutation builder. +func (m *ListingMediaMutation) Where(ps ...predicate.ListingMedia) { + m.predicates = append(m.predicates, ps...) +} + +// WhereP appends storage-level predicates to the ListingMediaMutation builder. Using this method, +// users can use type-assertion to append predicates that do not depend on any generated package. +func (m *ListingMediaMutation) WhereP(ps ...func(*sql.Selector)) { + p := make([]predicate.ListingMedia, len(ps)) + for i := range ps { + p[i] = ps[i] + } + m.Where(p...) +} + +// Op returns the operation name. +func (m *ListingMediaMutation) Op() Op { + return m.op +} + +// SetOp allows setting the mutation operation. +func (m *ListingMediaMutation) SetOp(op Op) { + m.op = op +} + +// Type returns the node type of this mutation (ListingMedia). +func (m *ListingMediaMutation) 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 *ListingMediaMutation) Fields() []string { + fields := make([]string, 0, 9) + if m.listing_id != nil { + fields = append(fields, listingmedia.FieldListingID) + } + if m.ordinal != nil { + fields = append(fields, listingmedia.FieldOrdinal) + } + if m.content_type != nil { + fields = append(fields, listingmedia.FieldContentType) + } + if m.byte_size != nil { + fields = append(fields, listingmedia.FieldByteSize) + } + if m.checksum_sha256 != nil { + fields = append(fields, listingmedia.FieldChecksumSha256) + } + if m.object_reference != nil { + fields = append(fields, listingmedia.FieldObjectReference) + } + if m.state != nil { + fields = append(fields, listingmedia.FieldState) + } + if m.created_at != nil { + fields = append(fields, listingmedia.FieldCreatedAt) + } + if m.updated_at != nil { + fields = append(fields, listingmedia.FieldUpdatedAt) + } + 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 *ListingMediaMutation) Field(name string) (ent.Value, bool) { + switch name { + case listingmedia.FieldListingID: + return m.ListingID() + case listingmedia.FieldOrdinal: + return m.Ordinal() + case listingmedia.FieldContentType: + return m.ContentType() + case listingmedia.FieldByteSize: + return m.ByteSize() + case listingmedia.FieldChecksumSha256: + return m.ChecksumSha256() + case listingmedia.FieldObjectReference: + return m.ObjectReference() + case listingmedia.FieldState: + return m.State() + case listingmedia.FieldCreatedAt: + return m.CreatedAt() + case listingmedia.FieldUpdatedAt: + return m.UpdatedAt() + } + 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 *ListingMediaMutation) OldField(ctx context.Context, name string) (ent.Value, error) { + switch name { + case listingmedia.FieldListingID: + return m.OldListingID(ctx) + case listingmedia.FieldOrdinal: + return m.OldOrdinal(ctx) + case listingmedia.FieldContentType: + return m.OldContentType(ctx) + case listingmedia.FieldByteSize: + return m.OldByteSize(ctx) + case listingmedia.FieldChecksumSha256: + return m.OldChecksumSha256(ctx) + case listingmedia.FieldObjectReference: + return m.OldObjectReference(ctx) + case listingmedia.FieldState: + return m.OldState(ctx) + case listingmedia.FieldCreatedAt: + return m.OldCreatedAt(ctx) + case listingmedia.FieldUpdatedAt: + return m.OldUpdatedAt(ctx) + } + return nil, fmt.Errorf("unknown ListingMedia 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 *ListingMediaMutation) SetField(name string, value ent.Value) error { + switch name { + case listingmedia.FieldListingID: + v, ok := value.(uuid.UUID) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetListingID(v) + return nil + case listingmedia.FieldOrdinal: + v, ok := value.(int) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetOrdinal(v) + return nil + case listingmedia.FieldContentType: + v, ok := value.(string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetContentType(v) + return nil + case listingmedia.FieldByteSize: + v, ok := value.(int64) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetByteSize(v) + return nil + case listingmedia.FieldChecksumSha256: + v, ok := value.(string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetChecksumSha256(v) + return nil + case listingmedia.FieldObjectReference: + v, ok := value.(string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetObjectReference(v) + return nil + case listingmedia.FieldState: + v, ok := value.(listingmedia.State) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetState(v) + return nil + case listingmedia.FieldCreatedAt: + v, ok := value.(time.Time) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetCreatedAt(v) + return nil + case listingmedia.FieldUpdatedAt: + v, ok := value.(time.Time) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetUpdatedAt(v) + return nil + } + return fmt.Errorf("unknown ListingMedia field %s", name) +} + +// AddedFields returns all numeric fields that were incremented/decremented during +// this mutation. +func (m *ListingMediaMutation) AddedFields() []string { + var fields []string + if m.addordinal != nil { + fields = append(fields, listingmedia.FieldOrdinal) + } + if m.addbyte_size != nil { + fields = append(fields, listingmedia.FieldByteSize) + } + 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 *ListingMediaMutation) AddedField(name string) (ent.Value, bool) { + switch name { + case listingmedia.FieldOrdinal: + return m.AddedOrdinal() + case listingmedia.FieldByteSize: + return m.AddedByteSize() + } + 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 *ListingMediaMutation) AddField(name string, value ent.Value) error { + switch name { + case listingmedia.FieldOrdinal: + v, ok := value.(int) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.AddOrdinal(v) + return nil + case listingmedia.FieldByteSize: + v, ok := value.(int64) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.AddByteSize(v) + return nil + } + return fmt.Errorf("unknown ListingMedia numeric field %s", name) +} + +// ClearedFields returns all nullable fields that were cleared during this +// mutation. +func (m *ListingMediaMutation) ClearedFields() []string { + return nil +} + +// FieldCleared returns a boolean indicating if a field with the given name was +// cleared in this mutation. +func (m *ListingMediaMutation) 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 *ListingMediaMutation) ClearField(name string) error { + return fmt.Errorf("unknown ListingMedia 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 *ListingMediaMutation) ResetField(name string) error { + switch name { + case listingmedia.FieldListingID: + m.ResetListingID() + return nil + case listingmedia.FieldOrdinal: + m.ResetOrdinal() + return nil + case listingmedia.FieldContentType: + m.ResetContentType() + return nil + case listingmedia.FieldByteSize: + m.ResetByteSize() + return nil + case listingmedia.FieldChecksumSha256: + m.ResetChecksumSha256() + return nil + case listingmedia.FieldObjectReference: + m.ResetObjectReference() + return nil + case listingmedia.FieldState: + m.ResetState() + return nil + case listingmedia.FieldCreatedAt: + m.ResetCreatedAt() + return nil + case listingmedia.FieldUpdatedAt: + m.ResetUpdatedAt() + return nil + } + return fmt.Errorf("unknown ListingMedia field %s", name) +} + +// AddedEdges returns all edge names that were set/added in this mutation. +func (m *ListingMediaMutation) AddedEdges() []string { + edges := make([]string, 0, 0) + return edges +} + +// AddedIDs returns all IDs (to other nodes) that were added for the given edge +// name in this mutation. +func (m *ListingMediaMutation) AddedIDs(name string) []ent.Value { + return nil +} + +// RemovedEdges returns all edge names that were removed in this mutation. +func (m *ListingMediaMutation) RemovedEdges() []string { + edges := make([]string, 0, 0) + return edges +} + +// RemovedIDs returns all IDs (to other nodes) that were removed for the edge with +// the given name in this mutation. +func (m *ListingMediaMutation) RemovedIDs(name string) []ent.Value { + return nil +} + +// ClearedEdges returns all edge names that were cleared in this mutation. +func (m *ListingMediaMutation) ClearedEdges() []string { + edges := make([]string, 0, 0) + return edges +} + +// EdgeCleared returns a boolean which indicates if the edge with the given name +// was cleared in this mutation. +func (m *ListingMediaMutation) EdgeCleared(name string) bool { + return false +} + +// ClearEdge clears the value of the edge with the given name. It returns an error +// if that edge is not defined in the schema. +func (m *ListingMediaMutation) ClearEdge(name string) error { + return fmt.Errorf("unknown ListingMedia 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 *ListingMediaMutation) ResetEdge(name string) error { + return fmt.Errorf("unknown ListingMedia edge %s", name) +} + +// LocalityMutation represents an operation that mutates the Locality nodes in the graph. +type LocalityMutation struct { + config + op Op + typ string + id *uuid.UUID + slug *string + name *string + source *string + source_element_id *string + source_version *string + source_retrieved_at *time.Time + latitude *float64 + addlatitude *float64 + longitude *float64 + addlongitude *float64 + active *bool + created_at *time.Time + updated_at *time.Time + clearedFields map[string]struct{} + parent_parish *uuid.UUID + clearedparent_parish bool + provider_profiles map[uuid.UUID]struct{} + removedprovider_profiles map[uuid.UUID]struct{} + clearedprovider_profiles bool + done bool + oldValue func(context.Context) (*Locality, error) + predicates []predicate.Locality +} + +var _ ent.Mutation = (*LocalityMutation)(nil) + +// localityOption allows management of the mutation configuration using functional options. +type localityOption func(*LocalityMutation) + +// newLocalityMutation creates new mutation for the Locality entity. +func newLocalityMutation(c config, op Op, opts ...localityOption) *LocalityMutation { + m := &LocalityMutation{ + config: c, + op: op, + typ: TypeLocality, + clearedFields: make(map[string]struct{}), + } + for _, opt := range opts { + opt(m) + } + return m +} + +// withLocalityID sets the ID field of the mutation. +func withLocalityID(id uuid.UUID) localityOption { + return func(m *LocalityMutation) { + var ( + err error + once sync.Once + value *Locality + ) + m.oldValue = func(ctx context.Context) (*Locality, error) { + once.Do(func() { + if m.done { + err = errors.New("querying old values post mutation is not allowed") + } else { + value, err = m.Client().Locality.Get(ctx, id) + } + }) + return value, err + } + m.id = &id + } +} + +// withLocality sets the old Locality of the mutation. +func withLocality(node *Locality) localityOption { + return func(m *LocalityMutation) { + m.oldValue = func(context.Context) (*Locality, 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 LocalityMutation) 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 LocalityMutation) 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 Locality entities. +func (m *LocalityMutation) 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 *LocalityMutation) 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 *LocalityMutation) 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().Locality.Query().Where(m.predicates...).IDs(ctx) + default: + return nil, fmt.Errorf("IDs is not allowed on %s operations", m.op) + } +} + +// SetSlug sets the "slug" field. +func (m *LocalityMutation) SetSlug(s string) { + m.slug = &s +} + +// Slug returns the value of the "slug" field in the mutation. +func (m *LocalityMutation) Slug() (r string, exists bool) { + v := m.slug + if v == nil { + return + } + return *v, true +} + +// OldSlug returns the old "slug" field's value of the Locality entity. +// If the Locality object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *LocalityMutation) OldSlug(ctx context.Context) (v string, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldSlug is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldSlug requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldSlug: %w", err) + } + return oldValue.Slug, nil +} + +// ResetSlug resets all changes to the "slug" field. +func (m *LocalityMutation) ResetSlug() { + m.slug = nil +} + +// SetName sets the "name" field. +func (m *LocalityMutation) SetName(s string) { + m.name = &s +} + +// Name returns the value of the "name" field in the mutation. +func (m *LocalityMutation) 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 Locality entity. +// If the Locality object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *LocalityMutation) 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 *LocalityMutation) ResetName() { + m.name = nil +} + +// SetParentParishID sets the "parent_parish_id" field. +func (m *LocalityMutation) SetParentParishID(u uuid.UUID) { + m.parent_parish = &u +} + +// ParentParishID returns the value of the "parent_parish_id" field in the mutation. +func (m *LocalityMutation) ParentParishID() (r uuid.UUID, exists bool) { + v := m.parent_parish + if v == nil { + return + } + return *v, true +} + +// OldParentParishID returns the old "parent_parish_id" field's value of the Locality entity. +// If the Locality object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *LocalityMutation) OldParentParishID(ctx context.Context) (v uuid.UUID, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldParentParishID is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldParentParishID requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldParentParishID: %w", err) + } + return oldValue.ParentParishID, nil +} + +// ResetParentParishID resets all changes to the "parent_parish_id" field. +func (m *LocalityMutation) ResetParentParishID() { + m.parent_parish = nil +} + +// SetSource sets the "source" field. +func (m *LocalityMutation) SetSource(s string) { + m.source = &s +} + +// Source returns the value of the "source" field in the mutation. +func (m *LocalityMutation) Source() (r string, exists bool) { + v := m.source + if v == nil { + return + } + return *v, true +} + +// OldSource returns the old "source" field's value of the Locality entity. +// If the Locality object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *LocalityMutation) OldSource(ctx context.Context) (v string, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldSource is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldSource requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldSource: %w", err) + } + return oldValue.Source, nil +} + +// ResetSource resets all changes to the "source" field. +func (m *LocalityMutation) ResetSource() { + m.source = nil +} + +// SetSourceElementID sets the "source_element_id" field. +func (m *LocalityMutation) SetSourceElementID(s string) { + m.source_element_id = &s +} + +// SourceElementID returns the value of the "source_element_id" field in the mutation. +func (m *LocalityMutation) SourceElementID() (r string, exists bool) { + v := m.source_element_id + if v == nil { + return + } + return *v, true +} + +// OldSourceElementID returns the old "source_element_id" field's value of the Locality entity. +// If the Locality object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *LocalityMutation) OldSourceElementID(ctx context.Context) (v string, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldSourceElementID is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldSourceElementID requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldSourceElementID: %w", err) + } + return oldValue.SourceElementID, nil +} + +// ResetSourceElementID resets all changes to the "source_element_id" field. +func (m *LocalityMutation) ResetSourceElementID() { + m.source_element_id = nil +} + +// SetSourceVersion sets the "source_version" field. +func (m *LocalityMutation) SetSourceVersion(s string) { + m.source_version = &s +} + +// SourceVersion returns the value of the "source_version" field in the mutation. +func (m *LocalityMutation) SourceVersion() (r string, exists bool) { + v := m.source_version + if v == nil { + return + } + return *v, true +} + +// OldSourceVersion returns the old "source_version" field's value of the Locality entity. +// If the Locality object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *LocalityMutation) OldSourceVersion(ctx context.Context) (v string, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldSourceVersion is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldSourceVersion requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldSourceVersion: %w", err) + } + return oldValue.SourceVersion, nil +} + +// ResetSourceVersion resets all changes to the "source_version" field. +func (m *LocalityMutation) ResetSourceVersion() { + m.source_version = nil +} + +// SetSourceRetrievedAt sets the "source_retrieved_at" field. +func (m *LocalityMutation) SetSourceRetrievedAt(t time.Time) { + m.source_retrieved_at = &t +} + +// SourceRetrievedAt returns the value of the "source_retrieved_at" field in the mutation. +func (m *LocalityMutation) SourceRetrievedAt() (r time.Time, exists bool) { + v := m.source_retrieved_at + if v == nil { + return + } + return *v, true +} + +// OldSourceRetrievedAt returns the old "source_retrieved_at" field's value of the Locality entity. +// If the Locality object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *LocalityMutation) OldSourceRetrievedAt(ctx context.Context) (v time.Time, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldSourceRetrievedAt is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldSourceRetrievedAt requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldSourceRetrievedAt: %w", err) + } + return oldValue.SourceRetrievedAt, nil +} + +// ResetSourceRetrievedAt resets all changes to the "source_retrieved_at" field. +func (m *LocalityMutation) ResetSourceRetrievedAt() { + m.source_retrieved_at = nil +} + +// SetLatitude sets the "latitude" field. +func (m *LocalityMutation) SetLatitude(f float64) { + m.latitude = &f + m.addlatitude = nil +} + +// Latitude returns the value of the "latitude" field in the mutation. +func (m *LocalityMutation) Latitude() (r float64, exists bool) { + v := m.latitude + if v == nil { + return + } + return *v, true +} + +// OldLatitude returns the old "latitude" field's value of the Locality entity. +// If the Locality object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *LocalityMutation) OldLatitude(ctx context.Context) (v float64, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldLatitude is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldLatitude requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldLatitude: %w", err) + } + return oldValue.Latitude, nil +} + +// AddLatitude adds f to the "latitude" field. +func (m *LocalityMutation) AddLatitude(f float64) { + if m.addlatitude != nil { + *m.addlatitude += f + } else { + m.addlatitude = &f + } +} + +// AddedLatitude returns the value that was added to the "latitude" field in this mutation. +func (m *LocalityMutation) AddedLatitude() (r float64, exists bool) { + v := m.addlatitude + if v == nil { + return + } + return *v, true +} + +// ResetLatitude resets all changes to the "latitude" field. +func (m *LocalityMutation) ResetLatitude() { + m.latitude = nil + m.addlatitude = nil +} + +// SetLongitude sets the "longitude" field. +func (m *LocalityMutation) SetLongitude(f float64) { + m.longitude = &f + m.addlongitude = nil +} + +// Longitude returns the value of the "longitude" field in the mutation. +func (m *LocalityMutation) Longitude() (r float64, exists bool) { + v := m.longitude + if v == nil { + return + } + return *v, true +} + +// OldLongitude returns the old "longitude" field's value of the Locality entity. +// If the Locality object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *LocalityMutation) OldLongitude(ctx context.Context) (v float64, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldLongitude is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldLongitude requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldLongitude: %w", err) + } + return oldValue.Longitude, nil +} + +// AddLongitude adds f to the "longitude" field. +func (m *LocalityMutation) AddLongitude(f float64) { + if m.addlongitude != nil { + *m.addlongitude += f + } else { + m.addlongitude = &f + } +} + +// AddedLongitude returns the value that was added to the "longitude" field in this mutation. +func (m *LocalityMutation) AddedLongitude() (r float64, exists bool) { + v := m.addlongitude + if v == nil { + return + } + return *v, true +} + +// ResetLongitude resets all changes to the "longitude" field. +func (m *LocalityMutation) ResetLongitude() { + m.longitude = nil + m.addlongitude = nil +} + +// SetActive sets the "active" field. +func (m *LocalityMutation) SetActive(b bool) { + m.active = &b +} + +// Active returns the value of the "active" field in the mutation. +func (m *LocalityMutation) Active() (r bool, exists bool) { + v := m.active + if v == nil { + return + } + return *v, true +} + +// OldActive returns the old "active" field's value of the Locality entity. +// If the Locality object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *LocalityMutation) OldActive(ctx context.Context) (v bool, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldActive is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldActive requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldActive: %w", err) + } + return oldValue.Active, nil +} + +// ResetActive resets all changes to the "active" field. +func (m *LocalityMutation) ResetActive() { + m.active = nil +} + +// SetCreatedAt sets the "created_at" field. +func (m *LocalityMutation) SetCreatedAt(t time.Time) { + m.created_at = &t +} + +// CreatedAt returns the value of the "created_at" field in the mutation. +func (m *LocalityMutation) CreatedAt() (r time.Time, exists bool) { + v := m.created_at + if v == nil { + return + } + return *v, true +} + +// OldCreatedAt returns the old "created_at" field's value of the Locality entity. +// If the Locality object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *LocalityMutation) OldCreatedAt(ctx context.Context) (v time.Time, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldCreatedAt is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldCreatedAt requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldCreatedAt: %w", err) + } + return oldValue.CreatedAt, nil +} + +// ResetCreatedAt resets all changes to the "created_at" field. +func (m *LocalityMutation) ResetCreatedAt() { + m.created_at = nil +} + +// SetUpdatedAt sets the "updated_at" field. +func (m *LocalityMutation) SetUpdatedAt(t time.Time) { + m.updated_at = &t +} + +// UpdatedAt returns the value of the "updated_at" field in the mutation. +func (m *LocalityMutation) UpdatedAt() (r time.Time, exists bool) { + v := m.updated_at + if v == nil { + return + } + return *v, true +} + +// OldUpdatedAt returns the old "updated_at" field's value of the Locality entity. +// If the Locality object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *LocalityMutation) OldUpdatedAt(ctx context.Context) (v time.Time, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldUpdatedAt is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldUpdatedAt requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldUpdatedAt: %w", err) + } + return oldValue.UpdatedAt, nil +} + +// ResetUpdatedAt resets all changes to the "updated_at" field. +func (m *LocalityMutation) ResetUpdatedAt() { + m.updated_at = nil +} + +// ClearParentParish clears the "parent_parish" edge to the AdministrativeArea entity. +func (m *LocalityMutation) ClearParentParish() { + m.clearedparent_parish = true + m.clearedFields[locality.FieldParentParishID] = struct{}{} +} + +// ParentParishCleared reports if the "parent_parish" edge to the AdministrativeArea entity was cleared. +func (m *LocalityMutation) ParentParishCleared() bool { + return m.clearedparent_parish +} + +// ParentParishIDs returns the "parent_parish" edge IDs in the mutation. +// Note that IDs always returns len(IDs) <= 1 for unique edges, and you should use +// ParentParishID instead. It exists only for internal usage by the builders. +func (m *LocalityMutation) ParentParishIDs() (ids []uuid.UUID) { + if id := m.parent_parish; id != nil { + ids = append(ids, *id) + } + return +} + +// ResetParentParish resets all changes to the "parent_parish" edge. +func (m *LocalityMutation) ResetParentParish() { + m.parent_parish = nil + m.clearedparent_parish = false +} + +// AddProviderProfileIDs adds the "provider_profiles" edge to the ProviderProfile entity by ids. +func (m *LocalityMutation) AddProviderProfileIDs(ids ...uuid.UUID) { + if m.provider_profiles == nil { + m.provider_profiles = make(map[uuid.UUID]struct{}) + } + for i := range ids { + m.provider_profiles[ids[i]] = struct{}{} + } +} + +// ClearProviderProfiles clears the "provider_profiles" edge to the ProviderProfile entity. +func (m *LocalityMutation) ClearProviderProfiles() { + m.clearedprovider_profiles = true +} + +// ProviderProfilesCleared reports if the "provider_profiles" edge to the ProviderProfile entity was cleared. +func (m *LocalityMutation) ProviderProfilesCleared() bool { + return m.clearedprovider_profiles +} + +// RemoveProviderProfileIDs removes the "provider_profiles" edge to the ProviderProfile entity by IDs. +func (m *LocalityMutation) RemoveProviderProfileIDs(ids ...uuid.UUID) { + if m.removedprovider_profiles == nil { + m.removedprovider_profiles = make(map[uuid.UUID]struct{}) + } + for i := range ids { + delete(m.provider_profiles, ids[i]) + m.removedprovider_profiles[ids[i]] = struct{}{} + } +} + +// RemovedProviderProfiles returns the removed IDs of the "provider_profiles" edge to the ProviderProfile entity. +func (m *LocalityMutation) RemovedProviderProfilesIDs() (ids []uuid.UUID) { + for id := range m.removedprovider_profiles { + ids = append(ids, id) + } + return +} + +// ProviderProfilesIDs returns the "provider_profiles" edge IDs in the mutation. +func (m *LocalityMutation) ProviderProfilesIDs() (ids []uuid.UUID) { + for id := range m.provider_profiles { + ids = append(ids, id) + } + return +} + +// ResetProviderProfiles resets all changes to the "provider_profiles" edge. +func (m *LocalityMutation) ResetProviderProfiles() { + m.provider_profiles = nil + m.clearedprovider_profiles = false + m.removedprovider_profiles = nil +} + +// Where appends a list predicates to the LocalityMutation builder. +func (m *LocalityMutation) Where(ps ...predicate.Locality) { + m.predicates = append(m.predicates, ps...) +} + +// WhereP appends storage-level predicates to the LocalityMutation builder. Using this method, +// users can use type-assertion to append predicates that do not depend on any generated package. +func (m *LocalityMutation) WhereP(ps ...func(*sql.Selector)) { + p := make([]predicate.Locality, len(ps)) + for i := range ps { + p[i] = ps[i] + } + m.Where(p...) +} + +// Op returns the operation name. +func (m *LocalityMutation) Op() Op { + return m.op +} + +// SetOp allows setting the mutation operation. +func (m *LocalityMutation) SetOp(op Op) { + m.op = op +} + +// Type returns the node type of this mutation (Locality). +func (m *LocalityMutation) 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 *LocalityMutation) Fields() []string { + fields := make([]string, 0, 12) + if m.slug != nil { + fields = append(fields, locality.FieldSlug) + } + if m.name != nil { + fields = append(fields, locality.FieldName) + } + if m.parent_parish != nil { + fields = append(fields, locality.FieldParentParishID) + } + if m.source != nil { + fields = append(fields, locality.FieldSource) + } + if m.source_element_id != nil { + fields = append(fields, locality.FieldSourceElementID) + } + if m.source_version != nil { + fields = append(fields, locality.FieldSourceVersion) + } + if m.source_retrieved_at != nil { + fields = append(fields, locality.FieldSourceRetrievedAt) + } + if m.latitude != nil { + fields = append(fields, locality.FieldLatitude) + } + if m.longitude != nil { + fields = append(fields, locality.FieldLongitude) + } + if m.active != nil { + fields = append(fields, locality.FieldActive) + } + if m.created_at != nil { + fields = append(fields, locality.FieldCreatedAt) + } + if m.updated_at != nil { + fields = append(fields, locality.FieldUpdatedAt) + } + 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 *LocalityMutation) Field(name string) (ent.Value, bool) { + switch name { + case locality.FieldSlug: + return m.Slug() + case locality.FieldName: + return m.Name() + case locality.FieldParentParishID: + return m.ParentParishID() + case locality.FieldSource: + return m.Source() + case locality.FieldSourceElementID: + return m.SourceElementID() + case locality.FieldSourceVersion: + return m.SourceVersion() + case locality.FieldSourceRetrievedAt: + return m.SourceRetrievedAt() + case locality.FieldLatitude: + return m.Latitude() + case locality.FieldLongitude: + return m.Longitude() + case locality.FieldActive: + return m.Active() + case locality.FieldCreatedAt: + return m.CreatedAt() + case locality.FieldUpdatedAt: + return m.UpdatedAt() + } + 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 *LocalityMutation) OldField(ctx context.Context, name string) (ent.Value, error) { + switch name { + case locality.FieldSlug: + return m.OldSlug(ctx) + case locality.FieldName: + return m.OldName(ctx) + case locality.FieldParentParishID: + return m.OldParentParishID(ctx) + case locality.FieldSource: + return m.OldSource(ctx) + case locality.FieldSourceElementID: + return m.OldSourceElementID(ctx) + case locality.FieldSourceVersion: + return m.OldSourceVersion(ctx) + case locality.FieldSourceRetrievedAt: + return m.OldSourceRetrievedAt(ctx) + case locality.FieldLatitude: + return m.OldLatitude(ctx) + case locality.FieldLongitude: + return m.OldLongitude(ctx) + case locality.FieldActive: + return m.OldActive(ctx) + case locality.FieldCreatedAt: + return m.OldCreatedAt(ctx) + case locality.FieldUpdatedAt: + return m.OldUpdatedAt(ctx) + } + return nil, fmt.Errorf("unknown Locality 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 *LocalityMutation) SetField(name string, value ent.Value) error { + switch name { + case locality.FieldSlug: + v, ok := value.(string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetSlug(v) + return nil + case locality.FieldName: + v, ok := value.(string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetName(v) + return nil + case locality.FieldParentParishID: + v, ok := value.(uuid.UUID) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetParentParishID(v) + return nil + case locality.FieldSource: + v, ok := value.(string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetSource(v) + return nil + case locality.FieldSourceElementID: + v, ok := value.(string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetSourceElementID(v) + return nil + case locality.FieldSourceVersion: + v, ok := value.(string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetSourceVersion(v) + return nil + case locality.FieldSourceRetrievedAt: + v, ok := value.(time.Time) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetSourceRetrievedAt(v) + return nil + case locality.FieldLatitude: + v, ok := value.(float64) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetLatitude(v) + return nil + case locality.FieldLongitude: + v, ok := value.(float64) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetLongitude(v) + return nil + case locality.FieldActive: + v, ok := value.(bool) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetActive(v) + return nil + case locality.FieldCreatedAt: + v, ok := value.(time.Time) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetCreatedAt(v) + return nil + case locality.FieldUpdatedAt: + v, ok := value.(time.Time) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetUpdatedAt(v) + return nil + } + return fmt.Errorf("unknown Locality field %s", name) +} + +// AddedFields returns all numeric fields that were incremented/decremented during +// this mutation. +func (m *LocalityMutation) AddedFields() []string { + var fields []string + if m.addlatitude != nil { + fields = append(fields, locality.FieldLatitude) + } + if m.addlongitude != nil { + fields = append(fields, locality.FieldLongitude) + } + 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 *LocalityMutation) AddedField(name string) (ent.Value, bool) { + switch name { + case locality.FieldLatitude: + return m.AddedLatitude() + case locality.FieldLongitude: + return m.AddedLongitude() + } + 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 *LocalityMutation) AddField(name string, value ent.Value) error { + switch name { + case locality.FieldLatitude: + v, ok := value.(float64) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.AddLatitude(v) + return nil + case locality.FieldLongitude: + v, ok := value.(float64) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.AddLongitude(v) + return nil + } + return fmt.Errorf("unknown Locality numeric field %s", name) +} + +// ClearedFields returns all nullable fields that were cleared during this +// mutation. +func (m *LocalityMutation) ClearedFields() []string { + return nil +} + +// FieldCleared returns a boolean indicating if a field with the given name was +// cleared in this mutation. +func (m *LocalityMutation) 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 *LocalityMutation) ClearField(name string) error { + return fmt.Errorf("unknown Locality 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 *LocalityMutation) ResetField(name string) error { + switch name { + case locality.FieldSlug: + m.ResetSlug() + return nil + case locality.FieldName: + m.ResetName() + return nil + case locality.FieldParentParishID: + m.ResetParentParishID() + return nil + case locality.FieldSource: + m.ResetSource() + return nil + case locality.FieldSourceElementID: + m.ResetSourceElementID() + return nil + case locality.FieldSourceVersion: + m.ResetSourceVersion() + return nil + case locality.FieldSourceRetrievedAt: + m.ResetSourceRetrievedAt() + return nil + case locality.FieldLatitude: + m.ResetLatitude() + return nil + case locality.FieldLongitude: + m.ResetLongitude() + return nil + case locality.FieldActive: + m.ResetActive() + return nil + case locality.FieldCreatedAt: + m.ResetCreatedAt() + return nil + case locality.FieldUpdatedAt: + m.ResetUpdatedAt() + return nil + } + return fmt.Errorf("unknown Locality field %s", name) +} + +// AddedEdges returns all edge names that were set/added in this mutation. +func (m *LocalityMutation) AddedEdges() []string { + edges := make([]string, 0, 2) + if m.parent_parish != nil { + edges = append(edges, locality.EdgeParentParish) + } + if m.provider_profiles != nil { + edges = append(edges, locality.EdgeProviderProfiles) + } + return edges +} + +// AddedIDs returns all IDs (to other nodes) that were added for the given edge +// name in this mutation. +func (m *LocalityMutation) AddedIDs(name string) []ent.Value { + switch name { + case locality.EdgeParentParish: + if id := m.parent_parish; id != nil { + return []ent.Value{*id} + } + case locality.EdgeProviderProfiles: + ids := make([]ent.Value, 0, len(m.provider_profiles)) + for id := range m.provider_profiles { + ids = append(ids, id) + } + return ids + } + return nil +} + +// RemovedEdges returns all edge names that were removed in this mutation. +func (m *LocalityMutation) RemovedEdges() []string { + edges := make([]string, 0, 2) + if m.removedprovider_profiles != nil { + edges = append(edges, locality.EdgeProviderProfiles) + } + return edges +} + +// RemovedIDs returns all IDs (to other nodes) that were removed for the edge with +// the given name in this mutation. +func (m *LocalityMutation) RemovedIDs(name string) []ent.Value { + switch name { + case locality.EdgeProviderProfiles: + ids := make([]ent.Value, 0, len(m.removedprovider_profiles)) + for id := range m.removedprovider_profiles { + ids = append(ids, id) + } + return ids + } + return nil +} + +// ClearedEdges returns all edge names that were cleared in this mutation. +func (m *LocalityMutation) ClearedEdges() []string { + edges := make([]string, 0, 2) + if m.clearedparent_parish { + edges = append(edges, locality.EdgeParentParish) + } + if m.clearedprovider_profiles { + edges = append(edges, locality.EdgeProviderProfiles) + } + return edges +} + +// EdgeCleared returns a boolean which indicates if the edge with the given name +// was cleared in this mutation. +func (m *LocalityMutation) EdgeCleared(name string) bool { + switch name { + case locality.EdgeParentParish: + return m.clearedparent_parish + case locality.EdgeProviderProfiles: + return m.clearedprovider_profiles + } + 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 *LocalityMutation) ClearEdge(name string) error { + switch name { + case locality.EdgeParentParish: + m.ClearParentParish() + return nil + } + return fmt.Errorf("unknown Locality 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 *LocalityMutation) ResetEdge(name string) error { + switch name { + case locality.EdgeParentParish: + m.ResetParentParish() + return nil + case locality.EdgeProviderProfiles: + m.ResetProviderProfiles() + return nil + } + return fmt.Errorf("unknown Locality edge %s", name) +} + +// PlatformRoleMutation represents an operation that mutates the PlatformRole nodes in the graph. +type PlatformRoleMutation struct { + config + op Op + typ string + id *uuid.UUID + internal_user_id *uuid.UUID + role *string + granted_at *time.Time + clearedFields map[string]struct{} + done bool + oldValue func(context.Context) (*PlatformRole, error) + predicates []predicate.PlatformRole +} + +var _ ent.Mutation = (*PlatformRoleMutation)(nil) + +// platformroleOption allows management of the mutation configuration using functional options. +type platformroleOption func(*PlatformRoleMutation) + +// newPlatformRoleMutation creates new mutation for the PlatformRole entity. +func newPlatformRoleMutation(c config, op Op, opts ...platformroleOption) *PlatformRoleMutation { + m := &PlatformRoleMutation{ + config: c, + op: op, + typ: TypePlatformRole, + clearedFields: make(map[string]struct{}), + } + for _, opt := range opts { + opt(m) + } + return m +} + +// withPlatformRoleID sets the ID field of the mutation. +func withPlatformRoleID(id uuid.UUID) platformroleOption { + return func(m *PlatformRoleMutation) { + var ( + err error + once sync.Once + value *PlatformRole + ) + m.oldValue = func(ctx context.Context) (*PlatformRole, error) { + once.Do(func() { + if m.done { + err = errors.New("querying old values post mutation is not allowed") + } else { + value, err = m.Client().PlatformRole.Get(ctx, id) + } + }) + return value, err + } + m.id = &id + } +} + +// withPlatformRole sets the old PlatformRole of the mutation. +func withPlatformRole(node *PlatformRole) platformroleOption { + return func(m *PlatformRoleMutation) { + m.oldValue = func(context.Context) (*PlatformRole, 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 PlatformRoleMutation) 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 PlatformRoleMutation) 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 PlatformRole entities. +func (m *PlatformRoleMutation) 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 *PlatformRoleMutation) 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 *PlatformRoleMutation) 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().PlatformRole.Query().Where(m.predicates...).IDs(ctx) + default: + return nil, fmt.Errorf("IDs is not allowed on %s operations", m.op) + } +} + +// SetInternalUserID sets the "internal_user_id" field. +func (m *PlatformRoleMutation) SetInternalUserID(u uuid.UUID) { + m.internal_user_id = &u +} + +// InternalUserID returns the value of the "internal_user_id" field in the mutation. +func (m *PlatformRoleMutation) InternalUserID() (r uuid.UUID, exists bool) { + v := m.internal_user_id + if v == nil { + return + } + return *v, true +} + +// OldInternalUserID returns the old "internal_user_id" field's value of the PlatformRole entity. +// If the PlatformRole object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *PlatformRoleMutation) OldInternalUserID(ctx context.Context) (v uuid.UUID, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldInternalUserID is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldInternalUserID requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldInternalUserID: %w", err) + } + return oldValue.InternalUserID, nil +} + +// ResetInternalUserID resets all changes to the "internal_user_id" field. +func (m *PlatformRoleMutation) ResetInternalUserID() { + m.internal_user_id = nil +} + +// SetRole sets the "role" field. +func (m *PlatformRoleMutation) SetRole(s string) { + m.role = &s +} + +// Role returns the value of the "role" field in the mutation. +func (m *PlatformRoleMutation) Role() (r string, exists bool) { + v := m.role + if v == nil { + return + } + return *v, true +} + +// OldRole returns the old "role" field's value of the PlatformRole entity. +// If the PlatformRole object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *PlatformRoleMutation) OldRole(ctx context.Context) (v string, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldRole is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldRole requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldRole: %w", err) + } + return oldValue.Role, nil +} + +// ResetRole resets all changes to the "role" field. +func (m *PlatformRoleMutation) ResetRole() { + m.role = nil +} + +// SetGrantedAt sets the "granted_at" field. +func (m *PlatformRoleMutation) SetGrantedAt(t time.Time) { + m.granted_at = &t +} + +// GrantedAt returns the value of the "granted_at" field in the mutation. +func (m *PlatformRoleMutation) GrantedAt() (r time.Time, exists bool) { + v := m.granted_at + if v == nil { + return + } + return *v, true +} + +// OldGrantedAt returns the old "granted_at" field's value of the PlatformRole entity. +// If the PlatformRole object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *PlatformRoleMutation) OldGrantedAt(ctx context.Context) (v time.Time, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldGrantedAt is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldGrantedAt requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldGrantedAt: %w", err) + } + return oldValue.GrantedAt, nil +} + +// ResetGrantedAt resets all changes to the "granted_at" field. +func (m *PlatformRoleMutation) ResetGrantedAt() { + m.granted_at = nil +} + +// Where appends a list predicates to the PlatformRoleMutation builder. +func (m *PlatformRoleMutation) Where(ps ...predicate.PlatformRole) { + m.predicates = append(m.predicates, ps...) +} + +// WhereP appends storage-level predicates to the PlatformRoleMutation builder. Using this method, +// users can use type-assertion to append predicates that do not depend on any generated package. +func (m *PlatformRoleMutation) WhereP(ps ...func(*sql.Selector)) { + p := make([]predicate.PlatformRole, len(ps)) + for i := range ps { + p[i] = ps[i] + } + m.Where(p...) +} + +// Op returns the operation name. +func (m *PlatformRoleMutation) Op() Op { + return m.op +} + +// SetOp allows setting the mutation operation. +func (m *PlatformRoleMutation) SetOp(op Op) { + m.op = op +} + +// Type returns the node type of this mutation (PlatformRole). +func (m *PlatformRoleMutation) 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 *PlatformRoleMutation) Fields() []string { + fields := make([]string, 0, 3) + if m.internal_user_id != nil { + fields = append(fields, platformrole.FieldInternalUserID) + } + if m.role != nil { + fields = append(fields, platformrole.FieldRole) + } + if m.granted_at != nil { + fields = append(fields, platformrole.FieldGrantedAt) + } + 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 *PlatformRoleMutation) Field(name string) (ent.Value, bool) { + switch name { + case platformrole.FieldInternalUserID: + return m.InternalUserID() + case platformrole.FieldRole: + return m.Role() + case platformrole.FieldGrantedAt: + return m.GrantedAt() + } + 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 *PlatformRoleMutation) OldField(ctx context.Context, name string) (ent.Value, error) { + switch name { + case platformrole.FieldInternalUserID: + return m.OldInternalUserID(ctx) + case platformrole.FieldRole: + return m.OldRole(ctx) + case platformrole.FieldGrantedAt: + return m.OldGrantedAt(ctx) + } + return nil, fmt.Errorf("unknown PlatformRole 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 *PlatformRoleMutation) SetField(name string, value ent.Value) error { + switch name { + case platformrole.FieldInternalUserID: + v, ok := value.(uuid.UUID) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetInternalUserID(v) + return nil + case platformrole.FieldRole: + v, ok := value.(string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetRole(v) + return nil + case platformrole.FieldGrantedAt: + v, ok := value.(time.Time) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetGrantedAt(v) + return nil + } + return fmt.Errorf("unknown PlatformRole field %s", name) +} + +// AddedFields returns all numeric fields that were incremented/decremented during +// this mutation. +func (m *PlatformRoleMutation) 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 *PlatformRoleMutation) 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 *PlatformRoleMutation) AddField(name string, value ent.Value) error { + switch name { + } + return fmt.Errorf("unknown PlatformRole numeric field %s", name) +} + +// ClearedFields returns all nullable fields that were cleared during this +// mutation. +func (m *PlatformRoleMutation) ClearedFields() []string { + return nil +} + +// FieldCleared returns a boolean indicating if a field with the given name was +// cleared in this mutation. +func (m *PlatformRoleMutation) 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 *PlatformRoleMutation) ClearField(name string) error { + return fmt.Errorf("unknown PlatformRole 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 *PlatformRoleMutation) ResetField(name string) error { + switch name { + case platformrole.FieldInternalUserID: + m.ResetInternalUserID() + return nil + case platformrole.FieldRole: + m.ResetRole() + return nil + case platformrole.FieldGrantedAt: + m.ResetGrantedAt() + return nil + } + return fmt.Errorf("unknown PlatformRole field %s", name) +} + +// AddedEdges returns all edge names that were set/added in this mutation. +func (m *PlatformRoleMutation) AddedEdges() []string { + edges := make([]string, 0, 0) + return edges +} + +// AddedIDs returns all IDs (to other nodes) that were added for the given edge +// name in this mutation. +func (m *PlatformRoleMutation) AddedIDs(name string) []ent.Value { + return nil +} + +// RemovedEdges returns all edge names that were removed in this mutation. +func (m *PlatformRoleMutation) RemovedEdges() []string { + edges := make([]string, 0, 0) + return edges +} + +// RemovedIDs returns all IDs (to other nodes) that were removed for the edge with +// the given name in this mutation. +func (m *PlatformRoleMutation) RemovedIDs(name string) []ent.Value { + return nil +} + +// ClearedEdges returns all edge names that were cleared in this mutation. +func (m *PlatformRoleMutation) ClearedEdges() []string { + edges := make([]string, 0, 0) + return edges +} + +// EdgeCleared returns a boolean which indicates if the edge with the given name +// was cleared in this mutation. +func (m *PlatformRoleMutation) EdgeCleared(name string) bool { + return false +} + +// ClearEdge clears the value of the edge with the given name. It returns an error +// if that edge is not defined in the schema. +func (m *PlatformRoleMutation) ClearEdge(name string) error { + return fmt.Errorf("unknown PlatformRole 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 *PlatformRoleMutation) ResetEdge(name string) error { + return fmt.Errorf("unknown PlatformRole edge %s", name) +} + +// ProviderContactChannelMutation represents an operation that mutates the ProviderContactChannel nodes in the graph. +type ProviderContactChannelMutation struct { + config + op Op + typ string + id *uuid.UUID + internal_user_id *uuid.UUID + channel *providercontactchannel.Channel + ciphertext *[]byte + nonce *[]byte + key_version *string + enabled *bool + reveal_consent *bool + created_at *time.Time + updated_at *time.Time + clearedFields map[string]struct{} + done bool + oldValue func(context.Context) (*ProviderContactChannel, error) + predicates []predicate.ProviderContactChannel +} + +var _ ent.Mutation = (*ProviderContactChannelMutation)(nil) + +// providercontactchannelOption allows management of the mutation configuration using functional options. +type providercontactchannelOption func(*ProviderContactChannelMutation) + +// newProviderContactChannelMutation creates new mutation for the ProviderContactChannel entity. +func newProviderContactChannelMutation(c config, op Op, opts ...providercontactchannelOption) *ProviderContactChannelMutation { + m := &ProviderContactChannelMutation{ + config: c, + op: op, + typ: TypeProviderContactChannel, + clearedFields: make(map[string]struct{}), + } + for _, opt := range opts { + opt(m) + } + return m +} + +// withProviderContactChannelID sets the ID field of the mutation. +func withProviderContactChannelID(id uuid.UUID) providercontactchannelOption { + return func(m *ProviderContactChannelMutation) { + var ( + err error + once sync.Once + value *ProviderContactChannel + ) + m.oldValue = func(ctx context.Context) (*ProviderContactChannel, error) { + once.Do(func() { + if m.done { + err = errors.New("querying old values post mutation is not allowed") + } else { + value, err = m.Client().ProviderContactChannel.Get(ctx, id) + } + }) + return value, err + } + m.id = &id + } +} + +// withProviderContactChannel sets the old ProviderContactChannel of the mutation. +func withProviderContactChannel(node *ProviderContactChannel) providercontactchannelOption { + return func(m *ProviderContactChannelMutation) { + m.oldValue = func(context.Context) (*ProviderContactChannel, 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 ProviderContactChannelMutation) 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 ProviderContactChannelMutation) 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 ProviderContactChannel entities. +func (m *ProviderContactChannelMutation) 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 *ProviderContactChannelMutation) 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 *ProviderContactChannelMutation) 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().ProviderContactChannel.Query().Where(m.predicates...).IDs(ctx) + default: + return nil, fmt.Errorf("IDs is not allowed on %s operations", m.op) + } +} + +// SetInternalUserID sets the "internal_user_id" field. +func (m *ProviderContactChannelMutation) SetInternalUserID(u uuid.UUID) { + m.internal_user_id = &u +} + +// InternalUserID returns the value of the "internal_user_id" field in the mutation. +func (m *ProviderContactChannelMutation) InternalUserID() (r uuid.UUID, exists bool) { + v := m.internal_user_id + if v == nil { + return + } + return *v, true +} + +// OldInternalUserID returns the old "internal_user_id" field's value of the ProviderContactChannel entity. +// If the ProviderContactChannel object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *ProviderContactChannelMutation) OldInternalUserID(ctx context.Context) (v uuid.UUID, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldInternalUserID is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldInternalUserID requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldInternalUserID: %w", err) + } + return oldValue.InternalUserID, nil +} + +// ResetInternalUserID resets all changes to the "internal_user_id" field. +func (m *ProviderContactChannelMutation) ResetInternalUserID() { + m.internal_user_id = nil +} + +// SetChannel sets the "channel" field. +func (m *ProviderContactChannelMutation) SetChannel(pr providercontactchannel.Channel) { + m.channel = &pr +} + +// Channel returns the value of the "channel" field in the mutation. +func (m *ProviderContactChannelMutation) Channel() (r providercontactchannel.Channel, exists bool) { + v := m.channel + if v == nil { + return + } + return *v, true +} + +// OldChannel returns the old "channel" field's value of the ProviderContactChannel entity. +// If the ProviderContactChannel object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *ProviderContactChannelMutation) OldChannel(ctx context.Context) (v providercontactchannel.Channel, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldChannel is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldChannel requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldChannel: %w", err) + } + return oldValue.Channel, nil +} + +// ResetChannel resets all changes to the "channel" field. +func (m *ProviderContactChannelMutation) ResetChannel() { + m.channel = nil +} + +// SetCiphertext sets the "ciphertext" field. +func (m *ProviderContactChannelMutation) SetCiphertext(b []byte) { + m.ciphertext = &b +} + +// Ciphertext returns the value of the "ciphertext" field in the mutation. +func (m *ProviderContactChannelMutation) Ciphertext() (r []byte, exists bool) { + v := m.ciphertext + if v == nil { + return + } + return *v, true +} + +// OldCiphertext returns the old "ciphertext" field's value of the ProviderContactChannel entity. +// If the ProviderContactChannel object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *ProviderContactChannelMutation) OldCiphertext(ctx context.Context) (v []byte, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldCiphertext is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldCiphertext requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldCiphertext: %w", err) + } + return oldValue.Ciphertext, nil +} + +// ResetCiphertext resets all changes to the "ciphertext" field. +func (m *ProviderContactChannelMutation) ResetCiphertext() { + m.ciphertext = nil +} + +// SetNonce sets the "nonce" field. +func (m *ProviderContactChannelMutation) SetNonce(b []byte) { + m.nonce = &b +} + +// Nonce returns the value of the "nonce" field in the mutation. +func (m *ProviderContactChannelMutation) Nonce() (r []byte, exists bool) { + v := m.nonce + if v == nil { + return + } + return *v, true +} + +// OldNonce returns the old "nonce" field's value of the ProviderContactChannel entity. +// If the ProviderContactChannel object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *ProviderContactChannelMutation) OldNonce(ctx context.Context) (v []byte, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldNonce is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldNonce requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldNonce: %w", err) + } + return oldValue.Nonce, nil +} + +// ResetNonce resets all changes to the "nonce" field. +func (m *ProviderContactChannelMutation) ResetNonce() { + m.nonce = nil +} + +// SetKeyVersion sets the "key_version" field. +func (m *ProviderContactChannelMutation) SetKeyVersion(s string) { + m.key_version = &s +} + +// KeyVersion returns the value of the "key_version" field in the mutation. +func (m *ProviderContactChannelMutation) KeyVersion() (r string, exists bool) { + v := m.key_version + if v == nil { + return + } + return *v, true +} + +// OldKeyVersion returns the old "key_version" field's value of the ProviderContactChannel entity. +// If the ProviderContactChannel object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *ProviderContactChannelMutation) OldKeyVersion(ctx context.Context) (v string, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldKeyVersion is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldKeyVersion requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldKeyVersion: %w", err) + } + return oldValue.KeyVersion, nil +} + +// ResetKeyVersion resets all changes to the "key_version" field. +func (m *ProviderContactChannelMutation) ResetKeyVersion() { + m.key_version = nil +} + +// SetEnabled sets the "enabled" field. +func (m *ProviderContactChannelMutation) SetEnabled(b bool) { + m.enabled = &b +} + +// Enabled returns the value of the "enabled" field in the mutation. +func (m *ProviderContactChannelMutation) Enabled() (r bool, exists bool) { + v := m.enabled + if v == nil { + return + } + return *v, true +} + +// OldEnabled returns the old "enabled" field's value of the ProviderContactChannel entity. +// If the ProviderContactChannel object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *ProviderContactChannelMutation) OldEnabled(ctx context.Context) (v bool, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldEnabled is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldEnabled requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldEnabled: %w", err) + } + return oldValue.Enabled, nil +} + +// ResetEnabled resets all changes to the "enabled" field. +func (m *ProviderContactChannelMutation) ResetEnabled() { + m.enabled = nil +} + +// SetRevealConsent sets the "reveal_consent" field. +func (m *ProviderContactChannelMutation) SetRevealConsent(b bool) { + m.reveal_consent = &b +} + +// RevealConsent returns the value of the "reveal_consent" field in the mutation. +func (m *ProviderContactChannelMutation) RevealConsent() (r bool, exists bool) { + v := m.reveal_consent + if v == nil { + return + } + return *v, true +} + +// OldRevealConsent returns the old "reveal_consent" field's value of the ProviderContactChannel entity. +// If the ProviderContactChannel object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *ProviderContactChannelMutation) OldRevealConsent(ctx context.Context) (v bool, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldRevealConsent is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldRevealConsent requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldRevealConsent: %w", err) + } + return oldValue.RevealConsent, nil +} + +// ResetRevealConsent resets all changes to the "reveal_consent" field. +func (m *ProviderContactChannelMutation) ResetRevealConsent() { + m.reveal_consent = nil +} + +// SetCreatedAt sets the "created_at" field. +func (m *ProviderContactChannelMutation) SetCreatedAt(t time.Time) { + m.created_at = &t +} + +// CreatedAt returns the value of the "created_at" field in the mutation. +func (m *ProviderContactChannelMutation) CreatedAt() (r time.Time, exists bool) { + v := m.created_at + if v == nil { + return + } + return *v, true +} + +// OldCreatedAt returns the old "created_at" field's value of the ProviderContactChannel entity. +// If the ProviderContactChannel object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *ProviderContactChannelMutation) OldCreatedAt(ctx context.Context) (v time.Time, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldCreatedAt is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldCreatedAt requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldCreatedAt: %w", err) + } + return oldValue.CreatedAt, nil +} + +// ResetCreatedAt resets all changes to the "created_at" field. +func (m *ProviderContactChannelMutation) ResetCreatedAt() { + m.created_at = nil +} + +// SetUpdatedAt sets the "updated_at" field. +func (m *ProviderContactChannelMutation) SetUpdatedAt(t time.Time) { + m.updated_at = &t +} + +// UpdatedAt returns the value of the "updated_at" field in the mutation. +func (m *ProviderContactChannelMutation) UpdatedAt() (r time.Time, exists bool) { + v := m.updated_at + if v == nil { + return + } + return *v, true +} + +// OldUpdatedAt returns the old "updated_at" field's value of the ProviderContactChannel entity. +// If the ProviderContactChannel object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *ProviderContactChannelMutation) OldUpdatedAt(ctx context.Context) (v time.Time, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldUpdatedAt is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldUpdatedAt requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldUpdatedAt: %w", err) + } + return oldValue.UpdatedAt, nil +} + +// ResetUpdatedAt resets all changes to the "updated_at" field. +func (m *ProviderContactChannelMutation) ResetUpdatedAt() { + m.updated_at = nil +} + +// Where appends a list predicates to the ProviderContactChannelMutation builder. +func (m *ProviderContactChannelMutation) Where(ps ...predicate.ProviderContactChannel) { + m.predicates = append(m.predicates, ps...) +} + +// WhereP appends storage-level predicates to the ProviderContactChannelMutation builder. Using this method, +// users can use type-assertion to append predicates that do not depend on any generated package. +func (m *ProviderContactChannelMutation) WhereP(ps ...func(*sql.Selector)) { + p := make([]predicate.ProviderContactChannel, len(ps)) + for i := range ps { + p[i] = ps[i] + } + m.Where(p...) +} + +// Op returns the operation name. +func (m *ProviderContactChannelMutation) Op() Op { + return m.op +} + +// SetOp allows setting the mutation operation. +func (m *ProviderContactChannelMutation) SetOp(op Op) { + m.op = op +} + +// Type returns the node type of this mutation (ProviderContactChannel). +func (m *ProviderContactChannelMutation) 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 *ProviderContactChannelMutation) Fields() []string { + fields := make([]string, 0, 9) + if m.internal_user_id != nil { + fields = append(fields, providercontactchannel.FieldInternalUserID) + } + if m.channel != nil { + fields = append(fields, providercontactchannel.FieldChannel) + } + if m.ciphertext != nil { + fields = append(fields, providercontactchannel.FieldCiphertext) + } + if m.nonce != nil { + fields = append(fields, providercontactchannel.FieldNonce) + } + if m.key_version != nil { + fields = append(fields, providercontactchannel.FieldKeyVersion) + } + if m.enabled != nil { + fields = append(fields, providercontactchannel.FieldEnabled) + } + if m.reveal_consent != nil { + fields = append(fields, providercontactchannel.FieldRevealConsent) + } + if m.created_at != nil { + fields = append(fields, providercontactchannel.FieldCreatedAt) + } + if m.updated_at != nil { + fields = append(fields, providercontactchannel.FieldUpdatedAt) + } + 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 *ProviderContactChannelMutation) Field(name string) (ent.Value, bool) { + switch name { + case providercontactchannel.FieldInternalUserID: + return m.InternalUserID() + case providercontactchannel.FieldChannel: + return m.Channel() + case providercontactchannel.FieldCiphertext: + return m.Ciphertext() + case providercontactchannel.FieldNonce: + return m.Nonce() + case providercontactchannel.FieldKeyVersion: + return m.KeyVersion() + case providercontactchannel.FieldEnabled: + return m.Enabled() + case providercontactchannel.FieldRevealConsent: + return m.RevealConsent() + case providercontactchannel.FieldCreatedAt: + return m.CreatedAt() + case providercontactchannel.FieldUpdatedAt: + return m.UpdatedAt() + } + 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 *ProviderContactChannelMutation) OldField(ctx context.Context, name string) (ent.Value, error) { + switch name { + case providercontactchannel.FieldInternalUserID: + return m.OldInternalUserID(ctx) + case providercontactchannel.FieldChannel: + return m.OldChannel(ctx) + case providercontactchannel.FieldCiphertext: + return m.OldCiphertext(ctx) + case providercontactchannel.FieldNonce: + return m.OldNonce(ctx) + case providercontactchannel.FieldKeyVersion: + return m.OldKeyVersion(ctx) + case providercontactchannel.FieldEnabled: + return m.OldEnabled(ctx) + case providercontactchannel.FieldRevealConsent: + return m.OldRevealConsent(ctx) + case providercontactchannel.FieldCreatedAt: + return m.OldCreatedAt(ctx) + case providercontactchannel.FieldUpdatedAt: + return m.OldUpdatedAt(ctx) + } + return nil, fmt.Errorf("unknown ProviderContactChannel 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 *ProviderContactChannelMutation) SetField(name string, value ent.Value) error { + switch name { + case providercontactchannel.FieldInternalUserID: + v, ok := value.(uuid.UUID) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetInternalUserID(v) + return nil + case providercontactchannel.FieldChannel: + v, ok := value.(providercontactchannel.Channel) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetChannel(v) + return nil + case providercontactchannel.FieldCiphertext: + v, ok := value.([]byte) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetCiphertext(v) + return nil + case providercontactchannel.FieldNonce: + v, ok := value.([]byte) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetNonce(v) + return nil + case providercontactchannel.FieldKeyVersion: + v, ok := value.(string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetKeyVersion(v) + return nil + case providercontactchannel.FieldEnabled: + v, ok := value.(bool) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetEnabled(v) + return nil + case providercontactchannel.FieldRevealConsent: + v, ok := value.(bool) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetRevealConsent(v) + return nil + case providercontactchannel.FieldCreatedAt: + v, ok := value.(time.Time) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetCreatedAt(v) + return nil + case providercontactchannel.FieldUpdatedAt: + v, ok := value.(time.Time) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetUpdatedAt(v) + return nil + } + return fmt.Errorf("unknown ProviderContactChannel field %s", name) +} + +// AddedFields returns all numeric fields that were incremented/decremented during +// this mutation. +func (m *ProviderContactChannelMutation) 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 *ProviderContactChannelMutation) 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 *ProviderContactChannelMutation) AddField(name string, value ent.Value) error { + switch name { + } + return fmt.Errorf("unknown ProviderContactChannel numeric field %s", name) +} + +// ClearedFields returns all nullable fields that were cleared during this +// mutation. +func (m *ProviderContactChannelMutation) ClearedFields() []string { + return nil +} + +// FieldCleared returns a boolean indicating if a field with the given name was +// cleared in this mutation. +func (m *ProviderContactChannelMutation) 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 *ProviderContactChannelMutation) ClearField(name string) error { + return fmt.Errorf("unknown ProviderContactChannel 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 *ProviderContactChannelMutation) ResetField(name string) error { + switch name { + case providercontactchannel.FieldInternalUserID: + m.ResetInternalUserID() + return nil + case providercontactchannel.FieldChannel: + m.ResetChannel() + return nil + case providercontactchannel.FieldCiphertext: + m.ResetCiphertext() + return nil + case providercontactchannel.FieldNonce: + m.ResetNonce() + return nil + case providercontactchannel.FieldKeyVersion: + m.ResetKeyVersion() + return nil + case providercontactchannel.FieldEnabled: + m.ResetEnabled() + return nil + case providercontactchannel.FieldRevealConsent: + m.ResetRevealConsent() + return nil + case providercontactchannel.FieldCreatedAt: + m.ResetCreatedAt() + return nil + case providercontactchannel.FieldUpdatedAt: + m.ResetUpdatedAt() + return nil + } + return fmt.Errorf("unknown ProviderContactChannel field %s", name) +} + +// AddedEdges returns all edge names that were set/added in this mutation. +func (m *ProviderContactChannelMutation) AddedEdges() []string { + edges := make([]string, 0, 0) + return edges +} + +// AddedIDs returns all IDs (to other nodes) that were added for the given edge +// name in this mutation. +func (m *ProviderContactChannelMutation) AddedIDs(name string) []ent.Value { + return nil +} + +// RemovedEdges returns all edge names that were removed in this mutation. +func (m *ProviderContactChannelMutation) RemovedEdges() []string { + edges := make([]string, 0, 0) + return edges +} + +// RemovedIDs returns all IDs (to other nodes) that were removed for the edge with +// the given name in this mutation. +func (m *ProviderContactChannelMutation) RemovedIDs(name string) []ent.Value { + return nil +} + +// ClearedEdges returns all edge names that were cleared in this mutation. +func (m *ProviderContactChannelMutation) ClearedEdges() []string { + edges := make([]string, 0, 0) + return edges +} + +// EdgeCleared returns a boolean which indicates if the edge with the given name +// was cleared in this mutation. +func (m *ProviderContactChannelMutation) EdgeCleared(name string) bool { + return false +} + +// ClearEdge clears the value of the edge with the given name. It returns an error +// if that edge is not defined in the schema. +func (m *ProviderContactChannelMutation) ClearEdge(name string) error { + return fmt.Errorf("unknown ProviderContactChannel 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 *ProviderContactChannelMutation) ResetEdge(name string) error { + return fmt.Errorf("unknown ProviderContactChannel edge %s", name) +} + +// ProviderProfileMutation represents an operation that mutates the ProviderProfile nodes in the graph. +type ProviderProfileMutation struct { + config + op Op + typ string + id *uuid.UUID + display_name *string + provider_type *string + bio *string + primary_locality_id *uuid.UUID + max_travel_distance_km *int + addmax_travel_distance_km *int + travels_to_customer *bool + receives_customer *bool + remote_services *bool + created_at *time.Time + updated_at *time.Time + clearedFields map[string]struct{} + service_localities map[uuid.UUID]struct{} + removedservice_localities map[uuid.UUID]struct{} + clearedservice_localities bool + spoken_languages map[string]struct{} + removedspoken_languages map[string]struct{} + clearedspoken_languages bool + done bool + oldValue func(context.Context) (*ProviderProfile, error) + predicates []predicate.ProviderProfile +} + +var _ ent.Mutation = (*ProviderProfileMutation)(nil) + +// providerprofileOption allows management of the mutation configuration using functional options. +type providerprofileOption func(*ProviderProfileMutation) + +// newProviderProfileMutation creates new mutation for the ProviderProfile entity. +func newProviderProfileMutation(c config, op Op, opts ...providerprofileOption) *ProviderProfileMutation { + m := &ProviderProfileMutation{ + config: c, + op: op, + typ: TypeProviderProfile, + clearedFields: make(map[string]struct{}), + } + for _, opt := range opts { + opt(m) + } + return m +} + +// withProviderProfileID sets the ID field of the mutation. +func withProviderProfileID(id uuid.UUID) providerprofileOption { + return func(m *ProviderProfileMutation) { + var ( + err error + once sync.Once + value *ProviderProfile + ) + m.oldValue = func(ctx context.Context) (*ProviderProfile, error) { + once.Do(func() { + if m.done { + err = errors.New("querying old values post mutation is not allowed") + } else { + value, err = m.Client().ProviderProfile.Get(ctx, id) + } + }) + return value, err + } + m.id = &id + } +} + +// withProviderProfile sets the old ProviderProfile of the mutation. +func withProviderProfile(node *ProviderProfile) providerprofileOption { + return func(m *ProviderProfileMutation) { + m.oldValue = func(context.Context) (*ProviderProfile, 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 ProviderProfileMutation) 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 ProviderProfileMutation) 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 ProviderProfile entities. +func (m *ProviderProfileMutation) 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 *ProviderProfileMutation) 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 *ProviderProfileMutation) 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().ProviderProfile.Query().Where(m.predicates...).IDs(ctx) + default: + return nil, fmt.Errorf("IDs is not allowed on %s operations", m.op) + } +} + +// SetDisplayName sets the "display_name" field. +func (m *ProviderProfileMutation) SetDisplayName(s string) { + m.display_name = &s +} + +// DisplayName returns the value of the "display_name" field in the mutation. +func (m *ProviderProfileMutation) DisplayName() (r string, exists bool) { + v := m.display_name + if v == nil { + return + } + return *v, true +} + +// OldDisplayName returns the old "display_name" field's value of the ProviderProfile entity. +// If the ProviderProfile object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *ProviderProfileMutation) OldDisplayName(ctx context.Context) (v string, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldDisplayName is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldDisplayName requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldDisplayName: %w", err) + } + return oldValue.DisplayName, nil +} + +// ResetDisplayName resets all changes to the "display_name" field. +func (m *ProviderProfileMutation) ResetDisplayName() { + m.display_name = nil +} + +// SetProviderType sets the "provider_type" field. +func (m *ProviderProfileMutation) SetProviderType(s string) { + m.provider_type = &s +} + +// ProviderType returns the value of the "provider_type" field in the mutation. +func (m *ProviderProfileMutation) ProviderType() (r string, exists bool) { + v := m.provider_type + if v == nil { + return + } + return *v, true +} + +// OldProviderType returns the old "provider_type" field's value of the ProviderProfile entity. +// If the ProviderProfile object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *ProviderProfileMutation) OldProviderType(ctx context.Context) (v string, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldProviderType is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldProviderType requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldProviderType: %w", err) + } + return oldValue.ProviderType, nil +} + +// ResetProviderType resets all changes to the "provider_type" field. +func (m *ProviderProfileMutation) ResetProviderType() { + m.provider_type = nil +} + +// SetBio sets the "bio" field. +func (m *ProviderProfileMutation) SetBio(s string) { + m.bio = &s +} + +// Bio returns the value of the "bio" field in the mutation. +func (m *ProviderProfileMutation) Bio() (r string, exists bool) { + v := m.bio + if v == nil { + return + } + return *v, true +} + +// OldBio returns the old "bio" field's value of the ProviderProfile entity. +// If the ProviderProfile object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *ProviderProfileMutation) OldBio(ctx context.Context) (v string, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldBio is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldBio requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldBio: %w", err) + } + return oldValue.Bio, nil +} + +// ResetBio resets all changes to the "bio" field. +func (m *ProviderProfileMutation) ResetBio() { + m.bio = nil +} + +// SetPrimaryLocalityID sets the "primary_locality_id" field. +func (m *ProviderProfileMutation) SetPrimaryLocalityID(u uuid.UUID) { + m.primary_locality_id = &u +} + +// PrimaryLocalityID returns the value of the "primary_locality_id" field in the mutation. +func (m *ProviderProfileMutation) PrimaryLocalityID() (r uuid.UUID, exists bool) { + v := m.primary_locality_id + if v == nil { + return + } + return *v, true +} + +// OldPrimaryLocalityID returns the old "primary_locality_id" field's value of the ProviderProfile entity. +// If the ProviderProfile object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *ProviderProfileMutation) OldPrimaryLocalityID(ctx context.Context) (v uuid.UUID, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldPrimaryLocalityID is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldPrimaryLocalityID requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldPrimaryLocalityID: %w", err) + } + return oldValue.PrimaryLocalityID, nil +} + +// ResetPrimaryLocalityID resets all changes to the "primary_locality_id" field. +func (m *ProviderProfileMutation) ResetPrimaryLocalityID() { + m.primary_locality_id = nil +} + +// SetMaxTravelDistanceKm sets the "max_travel_distance_km" field. +func (m *ProviderProfileMutation) SetMaxTravelDistanceKm(i int) { + m.max_travel_distance_km = &i + m.addmax_travel_distance_km = nil +} + +// MaxTravelDistanceKm returns the value of the "max_travel_distance_km" field in the mutation. +func (m *ProviderProfileMutation) MaxTravelDistanceKm() (r int, exists bool) { + v := m.max_travel_distance_km + if v == nil { + return + } + return *v, true +} + +// OldMaxTravelDistanceKm returns the old "max_travel_distance_km" field's value of the ProviderProfile entity. +// If the ProviderProfile object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *ProviderProfileMutation) OldMaxTravelDistanceKm(ctx context.Context) (v int, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldMaxTravelDistanceKm is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldMaxTravelDistanceKm requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldMaxTravelDistanceKm: %w", err) + } + return oldValue.MaxTravelDistanceKm, nil +} + +// AddMaxTravelDistanceKm adds i to the "max_travel_distance_km" field. +func (m *ProviderProfileMutation) AddMaxTravelDistanceKm(i int) { + if m.addmax_travel_distance_km != nil { + *m.addmax_travel_distance_km += i + } else { + m.addmax_travel_distance_km = &i + } +} + +// AddedMaxTravelDistanceKm returns the value that was added to the "max_travel_distance_km" field in this mutation. +func (m *ProviderProfileMutation) AddedMaxTravelDistanceKm() (r int, exists bool) { + v := m.addmax_travel_distance_km + if v == nil { + return + } + return *v, true +} + +// ResetMaxTravelDistanceKm resets all changes to the "max_travel_distance_km" field. +func (m *ProviderProfileMutation) ResetMaxTravelDistanceKm() { + m.max_travel_distance_km = nil + m.addmax_travel_distance_km = nil +} + +// SetTravelsToCustomer sets the "travels_to_customer" field. +func (m *ProviderProfileMutation) SetTravelsToCustomer(b bool) { + m.travels_to_customer = &b +} + +// TravelsToCustomer returns the value of the "travels_to_customer" field in the mutation. +func (m *ProviderProfileMutation) TravelsToCustomer() (r bool, exists bool) { + v := m.travels_to_customer + if v == nil { + return + } + return *v, true +} + +// OldTravelsToCustomer returns the old "travels_to_customer" field's value of the ProviderProfile entity. +// If the ProviderProfile object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *ProviderProfileMutation) OldTravelsToCustomer(ctx context.Context) (v bool, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldTravelsToCustomer is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldTravelsToCustomer requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldTravelsToCustomer: %w", err) + } + return oldValue.TravelsToCustomer, nil +} + +// ResetTravelsToCustomer resets all changes to the "travels_to_customer" field. +func (m *ProviderProfileMutation) ResetTravelsToCustomer() { + m.travels_to_customer = nil +} + +// SetReceivesCustomer sets the "receives_customer" field. +func (m *ProviderProfileMutation) SetReceivesCustomer(b bool) { + m.receives_customer = &b +} + +// ReceivesCustomer returns the value of the "receives_customer" field in the mutation. +func (m *ProviderProfileMutation) ReceivesCustomer() (r bool, exists bool) { + v := m.receives_customer + if v == nil { + return + } + return *v, true +} + +// OldReceivesCustomer returns the old "receives_customer" field's value of the ProviderProfile entity. +// If the ProviderProfile object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *ProviderProfileMutation) OldReceivesCustomer(ctx context.Context) (v bool, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldReceivesCustomer is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldReceivesCustomer requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldReceivesCustomer: %w", err) + } + return oldValue.ReceivesCustomer, nil +} + +// ResetReceivesCustomer resets all changes to the "receives_customer" field. +func (m *ProviderProfileMutation) ResetReceivesCustomer() { + m.receives_customer = nil +} + +// SetRemoteServices sets the "remote_services" field. +func (m *ProviderProfileMutation) SetRemoteServices(b bool) { + m.remote_services = &b +} + +// RemoteServices returns the value of the "remote_services" field in the mutation. +func (m *ProviderProfileMutation) RemoteServices() (r bool, exists bool) { + v := m.remote_services + if v == nil { + return + } + return *v, true +} + +// OldRemoteServices returns the old "remote_services" field's value of the ProviderProfile entity. +// If the ProviderProfile object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *ProviderProfileMutation) OldRemoteServices(ctx context.Context) (v bool, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldRemoteServices is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldRemoteServices requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldRemoteServices: %w", err) + } + return oldValue.RemoteServices, nil +} + +// ResetRemoteServices resets all changes to the "remote_services" field. +func (m *ProviderProfileMutation) ResetRemoteServices() { + m.remote_services = nil +} + +// SetCreatedAt sets the "created_at" field. +func (m *ProviderProfileMutation) SetCreatedAt(t time.Time) { + m.created_at = &t +} + +// CreatedAt returns the value of the "created_at" field in the mutation. +func (m *ProviderProfileMutation) CreatedAt() (r time.Time, exists bool) { + v := m.created_at + if v == nil { + return + } + return *v, true +} + +// OldCreatedAt returns the old "created_at" field's value of the ProviderProfile entity. +// If the ProviderProfile object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *ProviderProfileMutation) OldCreatedAt(ctx context.Context) (v time.Time, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldCreatedAt is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldCreatedAt requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldCreatedAt: %w", err) + } + return oldValue.CreatedAt, nil +} + +// ResetCreatedAt resets all changes to the "created_at" field. +func (m *ProviderProfileMutation) ResetCreatedAt() { + m.created_at = nil +} + +// SetUpdatedAt sets the "updated_at" field. +func (m *ProviderProfileMutation) SetUpdatedAt(t time.Time) { + m.updated_at = &t +} + +// UpdatedAt returns the value of the "updated_at" field in the mutation. +func (m *ProviderProfileMutation) UpdatedAt() (r time.Time, exists bool) { + v := m.updated_at + if v == nil { + return + } + return *v, true +} + +// OldUpdatedAt returns the old "updated_at" field's value of the ProviderProfile entity. +// If the ProviderProfile object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *ProviderProfileMutation) OldUpdatedAt(ctx context.Context) (v time.Time, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldUpdatedAt is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldUpdatedAt requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldUpdatedAt: %w", err) + } + return oldValue.UpdatedAt, nil +} + +// ResetUpdatedAt resets all changes to the "updated_at" field. +func (m *ProviderProfileMutation) ResetUpdatedAt() { + m.updated_at = nil +} + +// AddServiceLocalityIDs adds the "service_localities" edge to the Locality entity by ids. +func (m *ProviderProfileMutation) AddServiceLocalityIDs(ids ...uuid.UUID) { + if m.service_localities == nil { + m.service_localities = make(map[uuid.UUID]struct{}) + } + for i := range ids { + m.service_localities[ids[i]] = struct{}{} + } +} + +// ClearServiceLocalities clears the "service_localities" edge to the Locality entity. +func (m *ProviderProfileMutation) ClearServiceLocalities() { + m.clearedservice_localities = true +} + +// ServiceLocalitiesCleared reports if the "service_localities" edge to the Locality entity was cleared. +func (m *ProviderProfileMutation) ServiceLocalitiesCleared() bool { + return m.clearedservice_localities +} + +// RemoveServiceLocalityIDs removes the "service_localities" edge to the Locality entity by IDs. +func (m *ProviderProfileMutation) RemoveServiceLocalityIDs(ids ...uuid.UUID) { + if m.removedservice_localities == nil { + m.removedservice_localities = make(map[uuid.UUID]struct{}) + } + for i := range ids { + delete(m.service_localities, ids[i]) + m.removedservice_localities[ids[i]] = struct{}{} + } +} + +// RemovedServiceLocalities returns the removed IDs of the "service_localities" edge to the Locality entity. +func (m *ProviderProfileMutation) RemovedServiceLocalitiesIDs() (ids []uuid.UUID) { + for id := range m.removedservice_localities { + ids = append(ids, id) + } + return +} + +// ServiceLocalitiesIDs returns the "service_localities" edge IDs in the mutation. +func (m *ProviderProfileMutation) ServiceLocalitiesIDs() (ids []uuid.UUID) { + for id := range m.service_localities { + ids = append(ids, id) + } + return +} + +// ResetServiceLocalities resets all changes to the "service_localities" edge. +func (m *ProviderProfileMutation) ResetServiceLocalities() { + m.service_localities = nil + m.clearedservice_localities = false + m.removedservice_localities = nil +} + +// AddSpokenLanguageIDs adds the "spoken_languages" edge to the SpokenLanguage entity by ids. +func (m *ProviderProfileMutation) AddSpokenLanguageIDs(ids ...string) { + if m.spoken_languages == nil { + m.spoken_languages = make(map[string]struct{}) + } + for i := range ids { + m.spoken_languages[ids[i]] = struct{}{} + } +} + +// ClearSpokenLanguages clears the "spoken_languages" edge to the SpokenLanguage entity. +func (m *ProviderProfileMutation) ClearSpokenLanguages() { + m.clearedspoken_languages = true +} + +// SpokenLanguagesCleared reports if the "spoken_languages" edge to the SpokenLanguage entity was cleared. +func (m *ProviderProfileMutation) SpokenLanguagesCleared() bool { + return m.clearedspoken_languages +} + +// RemoveSpokenLanguageIDs removes the "spoken_languages" edge to the SpokenLanguage entity by IDs. +func (m *ProviderProfileMutation) RemoveSpokenLanguageIDs(ids ...string) { + if m.removedspoken_languages == nil { + m.removedspoken_languages = make(map[string]struct{}) + } + for i := range ids { + delete(m.spoken_languages, ids[i]) + m.removedspoken_languages[ids[i]] = struct{}{} + } +} + +// RemovedSpokenLanguages returns the removed IDs of the "spoken_languages" edge to the SpokenLanguage entity. +func (m *ProviderProfileMutation) RemovedSpokenLanguagesIDs() (ids []string) { + for id := range m.removedspoken_languages { + ids = append(ids, id) + } + return +} + +// SpokenLanguagesIDs returns the "spoken_languages" edge IDs in the mutation. +func (m *ProviderProfileMutation) SpokenLanguagesIDs() (ids []string) { + for id := range m.spoken_languages { + ids = append(ids, id) + } + return +} + +// ResetSpokenLanguages resets all changes to the "spoken_languages" edge. +func (m *ProviderProfileMutation) ResetSpokenLanguages() { + m.spoken_languages = nil + m.clearedspoken_languages = false + m.removedspoken_languages = nil +} + +// Where appends a list predicates to the ProviderProfileMutation builder. +func (m *ProviderProfileMutation) Where(ps ...predicate.ProviderProfile) { + m.predicates = append(m.predicates, ps...) +} + +// WhereP appends storage-level predicates to the ProviderProfileMutation builder. Using this method, +// users can use type-assertion to append predicates that do not depend on any generated package. +func (m *ProviderProfileMutation) WhereP(ps ...func(*sql.Selector)) { + p := make([]predicate.ProviderProfile, len(ps)) + for i := range ps { + p[i] = ps[i] + } + m.Where(p...) +} + +// Op returns the operation name. +func (m *ProviderProfileMutation) Op() Op { + return m.op +} + +// SetOp allows setting the mutation operation. +func (m *ProviderProfileMutation) SetOp(op Op) { + m.op = op +} + +// Type returns the node type of this mutation (ProviderProfile). +func (m *ProviderProfileMutation) 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 *ProviderProfileMutation) Fields() []string { + fields := make([]string, 0, 10) + if m.display_name != nil { + fields = append(fields, providerprofile.FieldDisplayName) + } + if m.provider_type != nil { + fields = append(fields, providerprofile.FieldProviderType) + } + if m.bio != nil { + fields = append(fields, providerprofile.FieldBio) + } + if m.primary_locality_id != nil { + fields = append(fields, providerprofile.FieldPrimaryLocalityID) + } + if m.max_travel_distance_km != nil { + fields = append(fields, providerprofile.FieldMaxTravelDistanceKm) + } + if m.travels_to_customer != nil { + fields = append(fields, providerprofile.FieldTravelsToCustomer) + } + if m.receives_customer != nil { + fields = append(fields, providerprofile.FieldReceivesCustomer) + } + if m.remote_services != nil { + fields = append(fields, providerprofile.FieldRemoteServices) + } + if m.created_at != nil { + fields = append(fields, providerprofile.FieldCreatedAt) + } + if m.updated_at != nil { + fields = append(fields, providerprofile.FieldUpdatedAt) + } + 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 *ProviderProfileMutation) Field(name string) (ent.Value, bool) { + switch name { + case providerprofile.FieldDisplayName: + return m.DisplayName() + case providerprofile.FieldProviderType: + return m.ProviderType() + case providerprofile.FieldBio: + return m.Bio() + case providerprofile.FieldPrimaryLocalityID: + return m.PrimaryLocalityID() + case providerprofile.FieldMaxTravelDistanceKm: + return m.MaxTravelDistanceKm() + case providerprofile.FieldTravelsToCustomer: + return m.TravelsToCustomer() + case providerprofile.FieldReceivesCustomer: + return m.ReceivesCustomer() + case providerprofile.FieldRemoteServices: + return m.RemoteServices() + case providerprofile.FieldCreatedAt: + return m.CreatedAt() + case providerprofile.FieldUpdatedAt: + return m.UpdatedAt() + } + 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 *ProviderProfileMutation) OldField(ctx context.Context, name string) (ent.Value, error) { + switch name { + case providerprofile.FieldDisplayName: + return m.OldDisplayName(ctx) + case providerprofile.FieldProviderType: + return m.OldProviderType(ctx) + case providerprofile.FieldBio: + return m.OldBio(ctx) + case providerprofile.FieldPrimaryLocalityID: + return m.OldPrimaryLocalityID(ctx) + case providerprofile.FieldMaxTravelDistanceKm: + return m.OldMaxTravelDistanceKm(ctx) + case providerprofile.FieldTravelsToCustomer: + return m.OldTravelsToCustomer(ctx) + case providerprofile.FieldReceivesCustomer: + return m.OldReceivesCustomer(ctx) + case providerprofile.FieldRemoteServices: + return m.OldRemoteServices(ctx) + case providerprofile.FieldCreatedAt: + return m.OldCreatedAt(ctx) + case providerprofile.FieldUpdatedAt: + return m.OldUpdatedAt(ctx) + } + return nil, fmt.Errorf("unknown ProviderProfile 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 *ProviderProfileMutation) SetField(name string, value ent.Value) error { + switch name { + case providerprofile.FieldDisplayName: + v, ok := value.(string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetDisplayName(v) + return nil + case providerprofile.FieldProviderType: + v, ok := value.(string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetProviderType(v) + return nil + case providerprofile.FieldBio: + v, ok := value.(string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetBio(v) + return nil + case providerprofile.FieldPrimaryLocalityID: + v, ok := value.(uuid.UUID) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetPrimaryLocalityID(v) + return nil + case providerprofile.FieldMaxTravelDistanceKm: + v, ok := value.(int) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetMaxTravelDistanceKm(v) + return nil + case providerprofile.FieldTravelsToCustomer: + v, ok := value.(bool) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetTravelsToCustomer(v) + return nil + case providerprofile.FieldReceivesCustomer: + v, ok := value.(bool) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetReceivesCustomer(v) + return nil + case providerprofile.FieldRemoteServices: + v, ok := value.(bool) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetRemoteServices(v) + return nil + case providerprofile.FieldCreatedAt: + v, ok := value.(time.Time) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetCreatedAt(v) + return nil + case providerprofile.FieldUpdatedAt: + v, ok := value.(time.Time) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetUpdatedAt(v) + return nil + } + return fmt.Errorf("unknown ProviderProfile field %s", name) +} + +// AddedFields returns all numeric fields that were incremented/decremented during +// this mutation. +func (m *ProviderProfileMutation) AddedFields() []string { + var fields []string + if m.addmax_travel_distance_km != nil { + fields = append(fields, providerprofile.FieldMaxTravelDistanceKm) + } + 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 *ProviderProfileMutation) AddedField(name string) (ent.Value, bool) { + switch name { + case providerprofile.FieldMaxTravelDistanceKm: + return m.AddedMaxTravelDistanceKm() + } + 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 *ProviderProfileMutation) AddField(name string, value ent.Value) error { + switch name { + case providerprofile.FieldMaxTravelDistanceKm: + v, ok := value.(int) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.AddMaxTravelDistanceKm(v) + return nil + } + return fmt.Errorf("unknown ProviderProfile numeric field %s", name) +} + +// ClearedFields returns all nullable fields that were cleared during this +// mutation. +func (m *ProviderProfileMutation) ClearedFields() []string { + return nil +} + +// FieldCleared returns a boolean indicating if a field with the given name was +// cleared in this mutation. +func (m *ProviderProfileMutation) 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 *ProviderProfileMutation) ClearField(name string) error { + return fmt.Errorf("unknown ProviderProfile 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 *ProviderProfileMutation) ResetField(name string) error { + switch name { + case providerprofile.FieldDisplayName: + m.ResetDisplayName() + return nil + case providerprofile.FieldProviderType: + m.ResetProviderType() + return nil + case providerprofile.FieldBio: + m.ResetBio() + return nil + case providerprofile.FieldPrimaryLocalityID: + m.ResetPrimaryLocalityID() + return nil + case providerprofile.FieldMaxTravelDistanceKm: + m.ResetMaxTravelDistanceKm() + return nil + case providerprofile.FieldTravelsToCustomer: + m.ResetTravelsToCustomer() + return nil + case providerprofile.FieldReceivesCustomer: + m.ResetReceivesCustomer() + return nil + case providerprofile.FieldRemoteServices: + m.ResetRemoteServices() + return nil + case providerprofile.FieldCreatedAt: + m.ResetCreatedAt() + return nil + case providerprofile.FieldUpdatedAt: + m.ResetUpdatedAt() + return nil + } + return fmt.Errorf("unknown ProviderProfile field %s", name) +} + +// AddedEdges returns all edge names that were set/added in this mutation. +func (m *ProviderProfileMutation) AddedEdges() []string { + edges := make([]string, 0, 2) + if m.service_localities != nil { + edges = append(edges, providerprofile.EdgeServiceLocalities) + } + if m.spoken_languages != nil { + edges = append(edges, providerprofile.EdgeSpokenLanguages) + } + return edges +} + +// AddedIDs returns all IDs (to other nodes) that were added for the given edge +// name in this mutation. +func (m *ProviderProfileMutation) AddedIDs(name string) []ent.Value { + switch name { + case providerprofile.EdgeServiceLocalities: + ids := make([]ent.Value, 0, len(m.service_localities)) + for id := range m.service_localities { + ids = append(ids, id) + } + return ids + case providerprofile.EdgeSpokenLanguages: + ids := make([]ent.Value, 0, len(m.spoken_languages)) + for id := range m.spoken_languages { + ids = append(ids, id) + } + return ids + } + return nil +} + +// RemovedEdges returns all edge names that were removed in this mutation. +func (m *ProviderProfileMutation) RemovedEdges() []string { + edges := make([]string, 0, 2) + if m.removedservice_localities != nil { + edges = append(edges, providerprofile.EdgeServiceLocalities) + } + if m.removedspoken_languages != nil { + edges = append(edges, providerprofile.EdgeSpokenLanguages) + } + return edges +} + +// RemovedIDs returns all IDs (to other nodes) that were removed for the edge with +// the given name in this mutation. +func (m *ProviderProfileMutation) RemovedIDs(name string) []ent.Value { + switch name { + case providerprofile.EdgeServiceLocalities: + ids := make([]ent.Value, 0, len(m.removedservice_localities)) + for id := range m.removedservice_localities { + ids = append(ids, id) + } + return ids + case providerprofile.EdgeSpokenLanguages: + ids := make([]ent.Value, 0, len(m.removedspoken_languages)) + for id := range m.removedspoken_languages { + ids = append(ids, id) + } + return ids + } + return nil +} + +// ClearedEdges returns all edge names that were cleared in this mutation. +func (m *ProviderProfileMutation) ClearedEdges() []string { + edges := make([]string, 0, 2) + if m.clearedservice_localities { + edges = append(edges, providerprofile.EdgeServiceLocalities) + } + if m.clearedspoken_languages { + edges = append(edges, providerprofile.EdgeSpokenLanguages) + } + return edges +} + +// EdgeCleared returns a boolean which indicates if the edge with the given name +// was cleared in this mutation. +func (m *ProviderProfileMutation) EdgeCleared(name string) bool { + switch name { + case providerprofile.EdgeServiceLocalities: + return m.clearedservice_localities + case providerprofile.EdgeSpokenLanguages: + return m.clearedspoken_languages + } + 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 *ProviderProfileMutation) ClearEdge(name string) error { + switch name { + } + return fmt.Errorf("unknown ProviderProfile 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 *ProviderProfileMutation) ResetEdge(name string) error { + switch name { + case providerprofile.EdgeServiceLocalities: + m.ResetServiceLocalities() + return nil + case providerprofile.EdgeSpokenLanguages: + m.ResetSpokenLanguages() + return nil + } + return fmt.Errorf("unknown ProviderProfile edge %s", name) +} + +// ProviderServiceLocalityMutation represents an operation that mutates the ProviderServiceLocality nodes in the graph. +type ProviderServiceLocalityMutation struct { + config + op Op + typ string + clearedFields map[string]struct{} + profile *uuid.UUID + clearedprofile bool + locality *uuid.UUID + clearedlocality bool + done bool + oldValue func(context.Context) (*ProviderServiceLocality, error) + predicates []predicate.ProviderServiceLocality +} + +var _ ent.Mutation = (*ProviderServiceLocalityMutation)(nil) + +// providerservicelocalityOption allows management of the mutation configuration using functional options. +type providerservicelocalityOption func(*ProviderServiceLocalityMutation) + +// newProviderServiceLocalityMutation creates new mutation for the ProviderServiceLocality entity. +func newProviderServiceLocalityMutation(c config, op Op, opts ...providerservicelocalityOption) *ProviderServiceLocalityMutation { + m := &ProviderServiceLocalityMutation{ + config: c, + op: op, + typ: TypeProviderServiceLocality, + clearedFields: make(map[string]struct{}), + } + for _, opt := range opts { + opt(m) + } + return m +} + +// 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 ProviderServiceLocalityMutation) 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 ProviderServiceLocalityMutation) 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 +} + +// SetInternalUserID sets the "internal_user_id" field. +func (m *ProviderServiceLocalityMutation) SetInternalUserID(u uuid.UUID) { + m.profile = &u +} + +// InternalUserID returns the value of the "internal_user_id" field in the mutation. +func (m *ProviderServiceLocalityMutation) InternalUserID() (r uuid.UUID, exists bool) { + v := m.profile + if v == nil { + return + } + return *v, true +} + +// ResetInternalUserID resets all changes to the "internal_user_id" field. +func (m *ProviderServiceLocalityMutation) ResetInternalUserID() { + m.profile = nil +} + +// SetLocalityID sets the "locality_id" field. +func (m *ProviderServiceLocalityMutation) SetLocalityID(u uuid.UUID) { + m.locality = &u +} + +// LocalityID returns the value of the "locality_id" field in the mutation. +func (m *ProviderServiceLocalityMutation) LocalityID() (r uuid.UUID, exists bool) { + v := m.locality + if v == nil { + return + } + return *v, true +} + +// ResetLocalityID resets all changes to the "locality_id" field. +func (m *ProviderServiceLocalityMutation) ResetLocalityID() { + m.locality = nil +} + +// SetProfileID sets the "profile" edge to the ProviderProfile entity by id. +func (m *ProviderServiceLocalityMutation) SetProfileID(id uuid.UUID) { + m.profile = &id +} + +// ClearProfile clears the "profile" edge to the ProviderProfile entity. +func (m *ProviderServiceLocalityMutation) ClearProfile() { + m.clearedprofile = true + m.clearedFields[providerservicelocality.FieldInternalUserID] = struct{}{} +} + +// ProfileCleared reports if the "profile" edge to the ProviderProfile entity was cleared. +func (m *ProviderServiceLocalityMutation) ProfileCleared() bool { + return m.clearedprofile +} + +// ProfileID returns the "profile" edge ID in the mutation. +func (m *ProviderServiceLocalityMutation) ProfileID() (id uuid.UUID, exists bool) { + if m.profile != nil { + return *m.profile, true + } + return +} + +// ProfileIDs returns the "profile" edge IDs in the mutation. +// Note that IDs always returns len(IDs) <= 1 for unique edges, and you should use +// ProfileID instead. It exists only for internal usage by the builders. +func (m *ProviderServiceLocalityMutation) ProfileIDs() (ids []uuid.UUID) { + if id := m.profile; id != nil { + ids = append(ids, *id) + } + return +} + +// ResetProfile resets all changes to the "profile" edge. +func (m *ProviderServiceLocalityMutation) ResetProfile() { + m.profile = nil + m.clearedprofile = false +} + +// ClearLocality clears the "locality" edge to the Locality entity. +func (m *ProviderServiceLocalityMutation) ClearLocality() { + m.clearedlocality = true + m.clearedFields[providerservicelocality.FieldLocalityID] = struct{}{} +} + +// LocalityCleared reports if the "locality" edge to the Locality entity was cleared. +func (m *ProviderServiceLocalityMutation) LocalityCleared() bool { + return m.clearedlocality +} + +// LocalityIDs returns the "locality" edge IDs in the mutation. +// Note that IDs always returns len(IDs) <= 1 for unique edges, and you should use +// LocalityID instead. It exists only for internal usage by the builders. +func (m *ProviderServiceLocalityMutation) LocalityIDs() (ids []uuid.UUID) { + if id := m.locality; id != nil { + ids = append(ids, *id) + } + return +} + +// ResetLocality resets all changes to the "locality" edge. +func (m *ProviderServiceLocalityMutation) ResetLocality() { + m.locality = nil + m.clearedlocality = false +} + +// Where appends a list predicates to the ProviderServiceLocalityMutation builder. +func (m *ProviderServiceLocalityMutation) Where(ps ...predicate.ProviderServiceLocality) { + m.predicates = append(m.predicates, ps...) +} + +// WhereP appends storage-level predicates to the ProviderServiceLocalityMutation builder. Using this method, +// users can use type-assertion to append predicates that do not depend on any generated package. +func (m *ProviderServiceLocalityMutation) WhereP(ps ...func(*sql.Selector)) { + p := make([]predicate.ProviderServiceLocality, len(ps)) + for i := range ps { + p[i] = ps[i] + } + m.Where(p...) +} + +// Op returns the operation name. +func (m *ProviderServiceLocalityMutation) Op() Op { + return m.op +} + +// SetOp allows setting the mutation operation. +func (m *ProviderServiceLocalityMutation) SetOp(op Op) { + m.op = op +} + +// Type returns the node type of this mutation (ProviderServiceLocality). +func (m *ProviderServiceLocalityMutation) 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 *ProviderServiceLocalityMutation) Fields() []string { + fields := make([]string, 0, 2) + if m.profile != nil { + fields = append(fields, providerservicelocality.FieldInternalUserID) + } + if m.locality != nil { + fields = append(fields, providerservicelocality.FieldLocalityID) + } + 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 *ProviderServiceLocalityMutation) Field(name string) (ent.Value, bool) { + switch name { + case providerservicelocality.FieldInternalUserID: + return m.InternalUserID() + case providerservicelocality.FieldLocalityID: + return m.LocalityID() + } + 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 *ProviderServiceLocalityMutation) OldField(ctx context.Context, name string) (ent.Value, error) { + return nil, errors.New("edge schema ProviderServiceLocality does not support getting old values") +} + +// 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 *ProviderServiceLocalityMutation) SetField(name string, value ent.Value) error { + switch name { + case providerservicelocality.FieldInternalUserID: + v, ok := value.(uuid.UUID) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetInternalUserID(v) + return nil + case providerservicelocality.FieldLocalityID: + v, ok := value.(uuid.UUID) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetLocalityID(v) + return nil + } + return fmt.Errorf("unknown ProviderServiceLocality field %s", name) +} + +// AddedFields returns all numeric fields that were incremented/decremented during +// this mutation. +func (m *ProviderServiceLocalityMutation) 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 *ProviderServiceLocalityMutation) 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 *ProviderServiceLocalityMutation) AddField(name string, value ent.Value) error { + switch name { + } + return fmt.Errorf("unknown ProviderServiceLocality numeric field %s", name) +} + +// ClearedFields returns all nullable fields that were cleared during this +// mutation. +func (m *ProviderServiceLocalityMutation) ClearedFields() []string { + return nil +} + +// FieldCleared returns a boolean indicating if a field with the given name was +// cleared in this mutation. +func (m *ProviderServiceLocalityMutation) 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 *ProviderServiceLocalityMutation) ClearField(name string) error { + return fmt.Errorf("unknown ProviderServiceLocality 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 *ProviderServiceLocalityMutation) ResetField(name string) error { + switch name { + case providerservicelocality.FieldInternalUserID: + m.ResetInternalUserID() + return nil + case providerservicelocality.FieldLocalityID: + m.ResetLocalityID() + return nil + } + return fmt.Errorf("unknown ProviderServiceLocality field %s", name) +} + +// AddedEdges returns all edge names that were set/added in this mutation. +func (m *ProviderServiceLocalityMutation) AddedEdges() []string { + edges := make([]string, 0, 2) + if m.profile != nil { + edges = append(edges, providerservicelocality.EdgeProfile) + } + if m.locality != nil { + edges = append(edges, providerservicelocality.EdgeLocality) + } + return edges +} + +// AddedIDs returns all IDs (to other nodes) that were added for the given edge +// name in this mutation. +func (m *ProviderServiceLocalityMutation) AddedIDs(name string) []ent.Value { + switch name { + case providerservicelocality.EdgeProfile: + if id := m.profile; id != nil { + return []ent.Value{*id} + } + case providerservicelocality.EdgeLocality: + if id := m.locality; id != nil { + return []ent.Value{*id} + } + } + return nil +} + +// RemovedEdges returns all edge names that were removed in this mutation. +func (m *ProviderServiceLocalityMutation) 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 *ProviderServiceLocalityMutation) RemovedIDs(name string) []ent.Value { + return nil +} + +// ClearedEdges returns all edge names that were cleared in this mutation. +func (m *ProviderServiceLocalityMutation) ClearedEdges() []string { + edges := make([]string, 0, 2) + if m.clearedprofile { + edges = append(edges, providerservicelocality.EdgeProfile) + } + if m.clearedlocality { + edges = append(edges, providerservicelocality.EdgeLocality) + } + return edges +} + +// EdgeCleared returns a boolean which indicates if the edge with the given name +// was cleared in this mutation. +func (m *ProviderServiceLocalityMutation) EdgeCleared(name string) bool { + switch name { + case providerservicelocality.EdgeProfile: + return m.clearedprofile + case providerservicelocality.EdgeLocality: + return m.clearedlocality + } + 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 *ProviderServiceLocalityMutation) ClearEdge(name string) error { + switch name { + case providerservicelocality.EdgeProfile: + m.ClearProfile() + return nil + case providerservicelocality.EdgeLocality: + m.ClearLocality() + return nil + } + return fmt.Errorf("unknown ProviderServiceLocality 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 *ProviderServiceLocalityMutation) ResetEdge(name string) error { + switch name { + case providerservicelocality.EdgeProfile: + m.ResetProfile() + return nil + case providerservicelocality.EdgeLocality: + m.ResetLocality() + return nil + } + return fmt.Errorf("unknown ProviderServiceLocality edge %s", name) +} + +// ProviderSpokenLanguageMutation represents an operation that mutates the ProviderSpokenLanguage nodes in the graph. +type ProviderSpokenLanguageMutation struct { + config + op Op + typ string + clearedFields map[string]struct{} + profile *uuid.UUID + clearedprofile bool + language *string + clearedlanguage bool + done bool + oldValue func(context.Context) (*ProviderSpokenLanguage, error) + predicates []predicate.ProviderSpokenLanguage +} + +var _ ent.Mutation = (*ProviderSpokenLanguageMutation)(nil) + +// providerspokenlanguageOption allows management of the mutation configuration using functional options. +type providerspokenlanguageOption func(*ProviderSpokenLanguageMutation) + +// newProviderSpokenLanguageMutation creates new mutation for the ProviderSpokenLanguage entity. +func newProviderSpokenLanguageMutation(c config, op Op, opts ...providerspokenlanguageOption) *ProviderSpokenLanguageMutation { + m := &ProviderSpokenLanguageMutation{ + config: c, + op: op, + typ: TypeProviderSpokenLanguage, + clearedFields: make(map[string]struct{}), + } + for _, opt := range opts { + opt(m) + } + return m +} + +// 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 ProviderSpokenLanguageMutation) 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 ProviderSpokenLanguageMutation) 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 +} + +// SetInternalUserID sets the "internal_user_id" field. +func (m *ProviderSpokenLanguageMutation) SetInternalUserID(u uuid.UUID) { + m.profile = &u +} + +// InternalUserID returns the value of the "internal_user_id" field in the mutation. +func (m *ProviderSpokenLanguageMutation) InternalUserID() (r uuid.UUID, exists bool) { + v := m.profile + if v == nil { + return + } + return *v, true +} + +// ResetInternalUserID resets all changes to the "internal_user_id" field. +func (m *ProviderSpokenLanguageMutation) ResetInternalUserID() { + m.profile = nil +} + +// SetLanguageCode sets the "language_code" field. +func (m *ProviderSpokenLanguageMutation) SetLanguageCode(s string) { + m.language = &s +} + +// LanguageCode returns the value of the "language_code" field in the mutation. +func (m *ProviderSpokenLanguageMutation) LanguageCode() (r string, exists bool) { + v := m.language + if v == nil { + return + } + return *v, true +} + +// ResetLanguageCode resets all changes to the "language_code" field. +func (m *ProviderSpokenLanguageMutation) ResetLanguageCode() { + m.language = nil +} + +// SetProfileID sets the "profile" edge to the ProviderProfile entity by id. +func (m *ProviderSpokenLanguageMutation) SetProfileID(id uuid.UUID) { + m.profile = &id +} + +// ClearProfile clears the "profile" edge to the ProviderProfile entity. +func (m *ProviderSpokenLanguageMutation) ClearProfile() { + m.clearedprofile = true + m.clearedFields[providerspokenlanguage.FieldInternalUserID] = struct{}{} +} + +// ProfileCleared reports if the "profile" edge to the ProviderProfile entity was cleared. +func (m *ProviderSpokenLanguageMutation) ProfileCleared() bool { + return m.clearedprofile +} + +// ProfileID returns the "profile" edge ID in the mutation. +func (m *ProviderSpokenLanguageMutation) ProfileID() (id uuid.UUID, exists bool) { + if m.profile != nil { + return *m.profile, true + } + return +} + +// ProfileIDs returns the "profile" edge IDs in the mutation. +// Note that IDs always returns len(IDs) <= 1 for unique edges, and you should use +// ProfileID instead. It exists only for internal usage by the builders. +func (m *ProviderSpokenLanguageMutation) ProfileIDs() (ids []uuid.UUID) { + if id := m.profile; id != nil { + ids = append(ids, *id) + } + return +} + +// ResetProfile resets all changes to the "profile" edge. +func (m *ProviderSpokenLanguageMutation) ResetProfile() { + m.profile = nil + m.clearedprofile = false +} + +// SetLanguageID sets the "language" edge to the SpokenLanguage entity by id. +func (m *ProviderSpokenLanguageMutation) SetLanguageID(id string) { + m.language = &id +} + +// ClearLanguage clears the "language" edge to the SpokenLanguage entity. +func (m *ProviderSpokenLanguageMutation) ClearLanguage() { + m.clearedlanguage = true + m.clearedFields[providerspokenlanguage.FieldLanguageCode] = struct{}{} +} + +// LanguageCleared reports if the "language" edge to the SpokenLanguage entity was cleared. +func (m *ProviderSpokenLanguageMutation) LanguageCleared() bool { + return m.clearedlanguage +} + +// LanguageID returns the "language" edge ID in the mutation. +func (m *ProviderSpokenLanguageMutation) LanguageID() (id string, exists bool) { + if m.language != nil { + return *m.language, true + } + return +} + +// LanguageIDs returns the "language" edge IDs in the mutation. +// Note that IDs always returns len(IDs) <= 1 for unique edges, and you should use +// LanguageID instead. It exists only for internal usage by the builders. +func (m *ProviderSpokenLanguageMutation) LanguageIDs() (ids []string) { + if id := m.language; id != nil { + ids = append(ids, *id) + } + return +} + +// ResetLanguage resets all changes to the "language" edge. +func (m *ProviderSpokenLanguageMutation) ResetLanguage() { + m.language = nil + m.clearedlanguage = false +} + +// Where appends a list predicates to the ProviderSpokenLanguageMutation builder. +func (m *ProviderSpokenLanguageMutation) Where(ps ...predicate.ProviderSpokenLanguage) { + m.predicates = append(m.predicates, ps...) +} + +// WhereP appends storage-level predicates to the ProviderSpokenLanguageMutation builder. Using this method, +// users can use type-assertion to append predicates that do not depend on any generated package. +func (m *ProviderSpokenLanguageMutation) WhereP(ps ...func(*sql.Selector)) { + p := make([]predicate.ProviderSpokenLanguage, len(ps)) + for i := range ps { + p[i] = ps[i] + } + m.Where(p...) +} + +// Op returns the operation name. +func (m *ProviderSpokenLanguageMutation) Op() Op { + return m.op +} + +// SetOp allows setting the mutation operation. +func (m *ProviderSpokenLanguageMutation) SetOp(op Op) { + m.op = op +} + +// Type returns the node type of this mutation (ProviderSpokenLanguage). +func (m *ProviderSpokenLanguageMutation) 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 *ProviderSpokenLanguageMutation) Fields() []string { + fields := make([]string, 0, 2) + if m.profile != nil { + fields = append(fields, providerspokenlanguage.FieldInternalUserID) + } + if m.language != nil { + fields = append(fields, providerspokenlanguage.FieldLanguageCode) + } + 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 *ProviderSpokenLanguageMutation) Field(name string) (ent.Value, bool) { + switch name { + case providerspokenlanguage.FieldInternalUserID: + return m.InternalUserID() + case providerspokenlanguage.FieldLanguageCode: + return m.LanguageCode() + } + 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 *ProviderSpokenLanguageMutation) OldField(ctx context.Context, name string) (ent.Value, error) { + return nil, errors.New("edge schema ProviderSpokenLanguage does not support getting old values") +} + +// 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 *ProviderSpokenLanguageMutation) SetField(name string, value ent.Value) error { + switch name { + case providerspokenlanguage.FieldInternalUserID: + v, ok := value.(uuid.UUID) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetInternalUserID(v) + return nil + case providerspokenlanguage.FieldLanguageCode: + v, ok := value.(string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetLanguageCode(v) + return nil + } + return fmt.Errorf("unknown ProviderSpokenLanguage field %s", name) +} + +// AddedFields returns all numeric fields that were incremented/decremented during +// this mutation. +func (m *ProviderSpokenLanguageMutation) 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 *ProviderSpokenLanguageMutation) 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 *ProviderSpokenLanguageMutation) AddField(name string, value ent.Value) error { + switch name { + } + return fmt.Errorf("unknown ProviderSpokenLanguage numeric field %s", name) +} + +// ClearedFields returns all nullable fields that were cleared during this +// mutation. +func (m *ProviderSpokenLanguageMutation) ClearedFields() []string { + return nil +} + +// FieldCleared returns a boolean indicating if a field with the given name was +// cleared in this mutation. +func (m *ProviderSpokenLanguageMutation) 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 *ProviderSpokenLanguageMutation) ClearField(name string) error { + return fmt.Errorf("unknown ProviderSpokenLanguage 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 *ProviderSpokenLanguageMutation) ResetField(name string) error { + switch name { + case providerspokenlanguage.FieldInternalUserID: + m.ResetInternalUserID() + return nil + case providerspokenlanguage.FieldLanguageCode: + m.ResetLanguageCode() + return nil + } + return fmt.Errorf("unknown ProviderSpokenLanguage field %s", name) +} + +// AddedEdges returns all edge names that were set/added in this mutation. +func (m *ProviderSpokenLanguageMutation) AddedEdges() []string { + edges := make([]string, 0, 2) + if m.profile != nil { + edges = append(edges, providerspokenlanguage.EdgeProfile) + } + if m.language != nil { + edges = append(edges, providerspokenlanguage.EdgeLanguage) + } + return edges +} + +// AddedIDs returns all IDs (to other nodes) that were added for the given edge +// name in this mutation. +func (m *ProviderSpokenLanguageMutation) AddedIDs(name string) []ent.Value { + switch name { + case providerspokenlanguage.EdgeProfile: + if id := m.profile; id != nil { + return []ent.Value{*id} + } + case providerspokenlanguage.EdgeLanguage: + if id := m.language; id != nil { + return []ent.Value{*id} + } + } + return nil +} + +// RemovedEdges returns all edge names that were removed in this mutation. +func (m *ProviderSpokenLanguageMutation) 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 *ProviderSpokenLanguageMutation) RemovedIDs(name string) []ent.Value { + return nil +} + +// ClearedEdges returns all edge names that were cleared in this mutation. +func (m *ProviderSpokenLanguageMutation) ClearedEdges() []string { + edges := make([]string, 0, 2) + if m.clearedprofile { + edges = append(edges, providerspokenlanguage.EdgeProfile) + } + if m.clearedlanguage { + edges = append(edges, providerspokenlanguage.EdgeLanguage) + } + return edges +} + +// EdgeCleared returns a boolean which indicates if the edge with the given name +// was cleared in this mutation. +func (m *ProviderSpokenLanguageMutation) EdgeCleared(name string) bool { + switch name { + case providerspokenlanguage.EdgeProfile: + return m.clearedprofile + case providerspokenlanguage.EdgeLanguage: + return m.clearedlanguage + } + 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 *ProviderSpokenLanguageMutation) ClearEdge(name string) error { + switch name { + case providerspokenlanguage.EdgeProfile: + m.ClearProfile() + return nil + case providerspokenlanguage.EdgeLanguage: + m.ClearLanguage() + return nil + } + return fmt.Errorf("unknown ProviderSpokenLanguage 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 *ProviderSpokenLanguageMutation) ResetEdge(name string) error { + switch name { + case providerspokenlanguage.EdgeProfile: + m.ResetProfile() + return nil + case providerspokenlanguage.EdgeLanguage: + m.ResetLanguage() + return nil + } + return fmt.Errorf("unknown ProviderSpokenLanguage edge %s", name) +} + +// ServiceCategoryMutation represents an operation that mutates the ServiceCategory nodes in the graph. +type ServiceCategoryMutation struct { + config + op Op + typ string + id *uuid.UUID + slug *string + active *bool + sort_order *int + addsort_order *int + created_at *time.Time + updated_at *time.Time + clearedFields map[string]struct{} + children map[uuid.UUID]struct{} + removedchildren map[uuid.UUID]struct{} + clearedchildren bool + parent *uuid.UUID + clearedparent bool + localized_in map[string]struct{} + removedlocalized_in map[string]struct{} + clearedlocalized_in bool + done bool + oldValue func(context.Context) (*ServiceCategory, error) + predicates []predicate.ServiceCategory +} + +var _ ent.Mutation = (*ServiceCategoryMutation)(nil) + +// servicecategoryOption allows management of the mutation configuration using functional options. +type servicecategoryOption func(*ServiceCategoryMutation) + +// newServiceCategoryMutation creates new mutation for the ServiceCategory entity. +func newServiceCategoryMutation(c config, op Op, opts ...servicecategoryOption) *ServiceCategoryMutation { + m := &ServiceCategoryMutation{ + config: c, + op: op, + typ: TypeServiceCategory, + clearedFields: make(map[string]struct{}), + } + for _, opt := range opts { + opt(m) + } + return m +} + +// withServiceCategoryID sets the ID field of the mutation. +func withServiceCategoryID(id uuid.UUID) servicecategoryOption { + return func(m *ServiceCategoryMutation) { + var ( + err error + once sync.Once + value *ServiceCategory + ) + m.oldValue = func(ctx context.Context) (*ServiceCategory, error) { + once.Do(func() { + if m.done { + err = errors.New("querying old values post mutation is not allowed") + } else { + value, err = m.Client().ServiceCategory.Get(ctx, id) + } + }) + return value, err + } + m.id = &id + } +} + +// withServiceCategory sets the old ServiceCategory of the mutation. +func withServiceCategory(node *ServiceCategory) servicecategoryOption { + return func(m *ServiceCategoryMutation) { + m.oldValue = func(context.Context) (*ServiceCategory, 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 ServiceCategoryMutation) 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 ServiceCategoryMutation) 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 ServiceCategory entities. +func (m *ServiceCategoryMutation) 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 *ServiceCategoryMutation) 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 *ServiceCategoryMutation) 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().ServiceCategory.Query().Where(m.predicates...).IDs(ctx) + default: + return nil, fmt.Errorf("IDs is not allowed on %s operations", m.op) + } +} + +// SetParentID sets the "parent_id" field. +func (m *ServiceCategoryMutation) SetParentID(u uuid.UUID) { + m.parent = &u +} + +// ParentID returns the value of the "parent_id" field in the mutation. +func (m *ServiceCategoryMutation) ParentID() (r uuid.UUID, exists bool) { + v := m.parent + if v == nil { + return + } + return *v, true +} + +// OldParentID returns the old "parent_id" field's value of the ServiceCategory entity. +// If the ServiceCategory object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *ServiceCategoryMutation) OldParentID(ctx context.Context) (v *uuid.UUID, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldParentID is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldParentID requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldParentID: %w", err) + } + return oldValue.ParentID, nil +} + +// ClearParentID clears the value of the "parent_id" field. +func (m *ServiceCategoryMutation) ClearParentID() { + m.parent = nil + m.clearedFields[servicecategory.FieldParentID] = struct{}{} +} + +// ParentIDCleared returns if the "parent_id" field was cleared in this mutation. +func (m *ServiceCategoryMutation) ParentIDCleared() bool { + _, ok := m.clearedFields[servicecategory.FieldParentID] + return ok +} + +// ResetParentID resets all changes to the "parent_id" field. +func (m *ServiceCategoryMutation) ResetParentID() { + m.parent = nil + delete(m.clearedFields, servicecategory.FieldParentID) +} + +// SetSlug sets the "slug" field. +func (m *ServiceCategoryMutation) SetSlug(s string) { + m.slug = &s +} + +// Slug returns the value of the "slug" field in the mutation. +func (m *ServiceCategoryMutation) Slug() (r string, exists bool) { + v := m.slug + if v == nil { + return + } + return *v, true +} + +// OldSlug returns the old "slug" field's value of the ServiceCategory entity. +// If the ServiceCategory object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *ServiceCategoryMutation) OldSlug(ctx context.Context) (v string, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldSlug is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldSlug requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldSlug: %w", err) + } + return oldValue.Slug, nil +} + +// ResetSlug resets all changes to the "slug" field. +func (m *ServiceCategoryMutation) ResetSlug() { + m.slug = nil +} + +// SetActive sets the "active" field. +func (m *ServiceCategoryMutation) SetActive(b bool) { + m.active = &b +} + +// Active returns the value of the "active" field in the mutation. +func (m *ServiceCategoryMutation) Active() (r bool, exists bool) { + v := m.active + if v == nil { + return + } + return *v, true +} + +// OldActive returns the old "active" field's value of the ServiceCategory entity. +// If the ServiceCategory object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *ServiceCategoryMutation) OldActive(ctx context.Context) (v bool, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldActive is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldActive requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldActive: %w", err) + } + return oldValue.Active, nil +} + +// ResetActive resets all changes to the "active" field. +func (m *ServiceCategoryMutation) ResetActive() { + m.active = nil +} + +// SetSortOrder sets the "sort_order" field. +func (m *ServiceCategoryMutation) SetSortOrder(i int) { + m.sort_order = &i + m.addsort_order = nil +} + +// SortOrder returns the value of the "sort_order" field in the mutation. +func (m *ServiceCategoryMutation) SortOrder() (r int, exists bool) { + v := m.sort_order + if v == nil { + return + } + return *v, true +} + +// OldSortOrder returns the old "sort_order" field's value of the ServiceCategory entity. +// If the ServiceCategory object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *ServiceCategoryMutation) OldSortOrder(ctx context.Context) (v int, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldSortOrder is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldSortOrder requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldSortOrder: %w", err) + } + return oldValue.SortOrder, nil +} + +// AddSortOrder adds i to the "sort_order" field. +func (m *ServiceCategoryMutation) AddSortOrder(i int) { + if m.addsort_order != nil { + *m.addsort_order += i + } else { + m.addsort_order = &i + } +} + +// AddedSortOrder returns the value that was added to the "sort_order" field in this mutation. +func (m *ServiceCategoryMutation) AddedSortOrder() (r int, exists bool) { + v := m.addsort_order + if v == nil { + return + } + return *v, true +} + +// ResetSortOrder resets all changes to the "sort_order" field. +func (m *ServiceCategoryMutation) ResetSortOrder() { + m.sort_order = nil + m.addsort_order = nil +} + +// SetCreatedAt sets the "created_at" field. +func (m *ServiceCategoryMutation) SetCreatedAt(t time.Time) { + m.created_at = &t +} + +// CreatedAt returns the value of the "created_at" field in the mutation. +func (m *ServiceCategoryMutation) CreatedAt() (r time.Time, exists bool) { + v := m.created_at + if v == nil { + return + } + return *v, true +} + +// OldCreatedAt returns the old "created_at" field's value of the ServiceCategory entity. +// If the ServiceCategory object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *ServiceCategoryMutation) OldCreatedAt(ctx context.Context) (v time.Time, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldCreatedAt is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldCreatedAt requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldCreatedAt: %w", err) + } + return oldValue.CreatedAt, nil +} + +// ResetCreatedAt resets all changes to the "created_at" field. +func (m *ServiceCategoryMutation) ResetCreatedAt() { + m.created_at = nil +} + +// SetUpdatedAt sets the "updated_at" field. +func (m *ServiceCategoryMutation) SetUpdatedAt(t time.Time) { + m.updated_at = &t +} + +// UpdatedAt returns the value of the "updated_at" field in the mutation. +func (m *ServiceCategoryMutation) UpdatedAt() (r time.Time, exists bool) { + v := m.updated_at + if v == nil { + return + } + return *v, true +} + +// OldUpdatedAt returns the old "updated_at" field's value of the ServiceCategory entity. +// If the ServiceCategory object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *ServiceCategoryMutation) OldUpdatedAt(ctx context.Context) (v time.Time, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldUpdatedAt is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldUpdatedAt requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldUpdatedAt: %w", err) + } + return oldValue.UpdatedAt, nil +} + +// ResetUpdatedAt resets all changes to the "updated_at" field. +func (m *ServiceCategoryMutation) ResetUpdatedAt() { + m.updated_at = nil +} + +// AddChildIDs adds the "children" edge to the ServiceCategory entity by ids. +func (m *ServiceCategoryMutation) AddChildIDs(ids ...uuid.UUID) { + if m.children == nil { + m.children = make(map[uuid.UUID]struct{}) + } + for i := range ids { + m.children[ids[i]] = struct{}{} + } +} + +// ClearChildren clears the "children" edge to the ServiceCategory entity. +func (m *ServiceCategoryMutation) ClearChildren() { + m.clearedchildren = true +} + +// ChildrenCleared reports if the "children" edge to the ServiceCategory entity was cleared. +func (m *ServiceCategoryMutation) ChildrenCleared() bool { + return m.clearedchildren +} + +// RemoveChildIDs removes the "children" edge to the ServiceCategory entity by IDs. +func (m *ServiceCategoryMutation) RemoveChildIDs(ids ...uuid.UUID) { + if m.removedchildren == nil { + m.removedchildren = make(map[uuid.UUID]struct{}) + } + for i := range ids { + delete(m.children, ids[i]) + m.removedchildren[ids[i]] = struct{}{} + } +} + +// RemovedChildren returns the removed IDs of the "children" edge to the ServiceCategory entity. +func (m *ServiceCategoryMutation) RemovedChildrenIDs() (ids []uuid.UUID) { + for id := range m.removedchildren { + ids = append(ids, id) + } + return +} + +// ChildrenIDs returns the "children" edge IDs in the mutation. +func (m *ServiceCategoryMutation) ChildrenIDs() (ids []uuid.UUID) { + for id := range m.children { + ids = append(ids, id) + } + return +} + +// ResetChildren resets all changes to the "children" edge. +func (m *ServiceCategoryMutation) ResetChildren() { + m.children = nil + m.clearedchildren = false + m.removedchildren = nil +} + +// ClearParent clears the "parent" edge to the ServiceCategory entity. +func (m *ServiceCategoryMutation) ClearParent() { + m.clearedparent = true + m.clearedFields[servicecategory.FieldParentID] = struct{}{} +} + +// ParentCleared reports if the "parent" edge to the ServiceCategory entity was cleared. +func (m *ServiceCategoryMutation) ParentCleared() bool { + return m.ParentIDCleared() || m.clearedparent +} + +// ParentIDs returns the "parent" edge IDs in the mutation. +// Note that IDs always returns len(IDs) <= 1 for unique edges, and you should use +// ParentID instead. It exists only for internal usage by the builders. +func (m *ServiceCategoryMutation) ParentIDs() (ids []uuid.UUID) { + if id := m.parent; id != nil { + ids = append(ids, *id) + } + return +} + +// ResetParent resets all changes to the "parent" edge. +func (m *ServiceCategoryMutation) ResetParent() { + m.parent = nil + m.clearedparent = false +} + +// AddLocalizedInIDs adds the "localized_in" edge to the SupportedLocale entity by ids. +func (m *ServiceCategoryMutation) AddLocalizedInIDs(ids ...string) { + if m.localized_in == nil { + m.localized_in = make(map[string]struct{}) + } + for i := range ids { + m.localized_in[ids[i]] = struct{}{} + } +} + +// ClearLocalizedIn clears the "localized_in" edge to the SupportedLocale entity. +func (m *ServiceCategoryMutation) ClearLocalizedIn() { + m.clearedlocalized_in = true +} + +// LocalizedInCleared reports if the "localized_in" edge to the SupportedLocale entity was cleared. +func (m *ServiceCategoryMutation) LocalizedInCleared() bool { + return m.clearedlocalized_in +} + +// RemoveLocalizedInIDs removes the "localized_in" edge to the SupportedLocale entity by IDs. +func (m *ServiceCategoryMutation) RemoveLocalizedInIDs(ids ...string) { + if m.removedlocalized_in == nil { + m.removedlocalized_in = make(map[string]struct{}) + } + for i := range ids { + delete(m.localized_in, ids[i]) + m.removedlocalized_in[ids[i]] = struct{}{} + } +} + +// RemovedLocalizedIn returns the removed IDs of the "localized_in" edge to the SupportedLocale entity. +func (m *ServiceCategoryMutation) RemovedLocalizedInIDs() (ids []string) { + for id := range m.removedlocalized_in { + ids = append(ids, id) + } + return +} + +// LocalizedInIDs returns the "localized_in" edge IDs in the mutation. +func (m *ServiceCategoryMutation) LocalizedInIDs() (ids []string) { + for id := range m.localized_in { + ids = append(ids, id) + } + return +} + +// ResetLocalizedIn resets all changes to the "localized_in" edge. +func (m *ServiceCategoryMutation) ResetLocalizedIn() { + m.localized_in = nil + m.clearedlocalized_in = false + m.removedlocalized_in = nil +} + +// Where appends a list predicates to the ServiceCategoryMutation builder. +func (m *ServiceCategoryMutation) Where(ps ...predicate.ServiceCategory) { + m.predicates = append(m.predicates, ps...) +} + +// WhereP appends storage-level predicates to the ServiceCategoryMutation builder. Using this method, +// users can use type-assertion to append predicates that do not depend on any generated package. +func (m *ServiceCategoryMutation) WhereP(ps ...func(*sql.Selector)) { + p := make([]predicate.ServiceCategory, len(ps)) + for i := range ps { + p[i] = ps[i] + } + m.Where(p...) +} + +// Op returns the operation name. +func (m *ServiceCategoryMutation) Op() Op { + return m.op +} + +// SetOp allows setting the mutation operation. +func (m *ServiceCategoryMutation) SetOp(op Op) { + m.op = op +} + +// Type returns the node type of this mutation (ServiceCategory). +func (m *ServiceCategoryMutation) 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 *ServiceCategoryMutation) Fields() []string { + fields := make([]string, 0, 6) + if m.parent != nil { + fields = append(fields, servicecategory.FieldParentID) + } + if m.slug != nil { + fields = append(fields, servicecategory.FieldSlug) + } + if m.active != nil { + fields = append(fields, servicecategory.FieldActive) + } + if m.sort_order != nil { + fields = append(fields, servicecategory.FieldSortOrder) + } + if m.created_at != nil { + fields = append(fields, servicecategory.FieldCreatedAt) + } + if m.updated_at != nil { + fields = append(fields, servicecategory.FieldUpdatedAt) + } + 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 *ServiceCategoryMutation) Field(name string) (ent.Value, bool) { + switch name { + case servicecategory.FieldParentID: + return m.ParentID() + case servicecategory.FieldSlug: + return m.Slug() + case servicecategory.FieldActive: + return m.Active() + case servicecategory.FieldSortOrder: + return m.SortOrder() + case servicecategory.FieldCreatedAt: + return m.CreatedAt() + case servicecategory.FieldUpdatedAt: + return m.UpdatedAt() + } + 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 *ServiceCategoryMutation) OldField(ctx context.Context, name string) (ent.Value, error) { + switch name { + case servicecategory.FieldParentID: + return m.OldParentID(ctx) + case servicecategory.FieldSlug: + return m.OldSlug(ctx) + case servicecategory.FieldActive: + return m.OldActive(ctx) + case servicecategory.FieldSortOrder: + return m.OldSortOrder(ctx) + case servicecategory.FieldCreatedAt: + return m.OldCreatedAt(ctx) + case servicecategory.FieldUpdatedAt: + return m.OldUpdatedAt(ctx) + } + return nil, fmt.Errorf("unknown ServiceCategory 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 *ServiceCategoryMutation) SetField(name string, value ent.Value) error { + switch name { + case servicecategory.FieldParentID: + v, ok := value.(uuid.UUID) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetParentID(v) + return nil + case servicecategory.FieldSlug: + v, ok := value.(string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetSlug(v) + return nil + case servicecategory.FieldActive: + v, ok := value.(bool) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetActive(v) + return nil + case servicecategory.FieldSortOrder: + v, ok := value.(int) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetSortOrder(v) + return nil + case servicecategory.FieldCreatedAt: + v, ok := value.(time.Time) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetCreatedAt(v) + return nil + case servicecategory.FieldUpdatedAt: + v, ok := value.(time.Time) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetUpdatedAt(v) + return nil + } + return fmt.Errorf("unknown ServiceCategory field %s", name) +} + +// AddedFields returns all numeric fields that were incremented/decremented during +// this mutation. +func (m *ServiceCategoryMutation) AddedFields() []string { + var fields []string + if m.addsort_order != nil { + fields = append(fields, servicecategory.FieldSortOrder) + } + 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 *ServiceCategoryMutation) AddedField(name string) (ent.Value, bool) { + switch name { + case servicecategory.FieldSortOrder: + return m.AddedSortOrder() + } + 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 *ServiceCategoryMutation) AddField(name string, value ent.Value) error { + switch name { + case servicecategory.FieldSortOrder: + v, ok := value.(int) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.AddSortOrder(v) + return nil + } + return fmt.Errorf("unknown ServiceCategory numeric field %s", name) +} + +// ClearedFields returns all nullable fields that were cleared during this +// mutation. +func (m *ServiceCategoryMutation) ClearedFields() []string { + var fields []string + if m.FieldCleared(servicecategory.FieldParentID) { + fields = append(fields, servicecategory.FieldParentID) + } + return fields +} + +// FieldCleared returns a boolean indicating if a field with the given name was +// cleared in this mutation. +func (m *ServiceCategoryMutation) 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 *ServiceCategoryMutation) ClearField(name string) error { + switch name { + case servicecategory.FieldParentID: + m.ClearParentID() + return nil + } + return fmt.Errorf("unknown ServiceCategory 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 *ServiceCategoryMutation) ResetField(name string) error { + switch name { + case servicecategory.FieldParentID: + m.ResetParentID() + return nil + case servicecategory.FieldSlug: + m.ResetSlug() + return nil + case servicecategory.FieldActive: + m.ResetActive() + return nil + case servicecategory.FieldSortOrder: + m.ResetSortOrder() + return nil + case servicecategory.FieldCreatedAt: + m.ResetCreatedAt() + return nil + case servicecategory.FieldUpdatedAt: + m.ResetUpdatedAt() + return nil + } + return fmt.Errorf("unknown ServiceCategory field %s", name) +} + +// AddedEdges returns all edge names that were set/added in this mutation. +func (m *ServiceCategoryMutation) AddedEdges() []string { + edges := make([]string, 0, 3) + if m.children != nil { + edges = append(edges, servicecategory.EdgeChildren) + } + if m.parent != nil { + edges = append(edges, servicecategory.EdgeParent) + } + if m.localized_in != nil { + edges = append(edges, servicecategory.EdgeLocalizedIn) + } + return edges +} + +// AddedIDs returns all IDs (to other nodes) that were added for the given edge +// name in this mutation. +func (m *ServiceCategoryMutation) AddedIDs(name string) []ent.Value { + switch name { + case servicecategory.EdgeChildren: + ids := make([]ent.Value, 0, len(m.children)) + for id := range m.children { + ids = append(ids, id) + } + return ids + case servicecategory.EdgeParent: + if id := m.parent; id != nil { + return []ent.Value{*id} + } + case servicecategory.EdgeLocalizedIn: + ids := make([]ent.Value, 0, len(m.localized_in)) + for id := range m.localized_in { + ids = append(ids, id) + } + return ids + } + return nil +} + +// RemovedEdges returns all edge names that were removed in this mutation. +func (m *ServiceCategoryMutation) RemovedEdges() []string { + edges := make([]string, 0, 3) + if m.removedchildren != nil { + edges = append(edges, servicecategory.EdgeChildren) + } + if m.removedlocalized_in != nil { + edges = append(edges, servicecategory.EdgeLocalizedIn) + } + return edges +} + +// RemovedIDs returns all IDs (to other nodes) that were removed for the edge with +// the given name in this mutation. +func (m *ServiceCategoryMutation) RemovedIDs(name string) []ent.Value { + switch name { + case servicecategory.EdgeChildren: + ids := make([]ent.Value, 0, len(m.removedchildren)) + for id := range m.removedchildren { + ids = append(ids, id) + } + return ids + case servicecategory.EdgeLocalizedIn: + ids := make([]ent.Value, 0, len(m.removedlocalized_in)) + for id := range m.removedlocalized_in { + ids = append(ids, id) + } + return ids + } + return nil +} + +// ClearedEdges returns all edge names that were cleared in this mutation. +func (m *ServiceCategoryMutation) ClearedEdges() []string { + edges := make([]string, 0, 3) + if m.clearedchildren { + edges = append(edges, servicecategory.EdgeChildren) + } + if m.clearedparent { + edges = append(edges, servicecategory.EdgeParent) + } + if m.clearedlocalized_in { + edges = append(edges, servicecategory.EdgeLocalizedIn) + } + return edges +} + +// EdgeCleared returns a boolean which indicates if the edge with the given name +// was cleared in this mutation. +func (m *ServiceCategoryMutation) EdgeCleared(name string) bool { + switch name { + case servicecategory.EdgeChildren: + return m.clearedchildren + case servicecategory.EdgeParent: + return m.clearedparent + case servicecategory.EdgeLocalizedIn: + return m.clearedlocalized_in + } + 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 *ServiceCategoryMutation) ClearEdge(name string) error { + switch name { + case servicecategory.EdgeParent: + m.ClearParent() + return nil + } + return fmt.Errorf("unknown ServiceCategory 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 *ServiceCategoryMutation) ResetEdge(name string) error { + switch name { + case servicecategory.EdgeChildren: + m.ResetChildren() + return nil + case servicecategory.EdgeParent: + m.ResetParent() + return nil + case servicecategory.EdgeLocalizedIn: + m.ResetLocalizedIn() + return nil + } + return fmt.Errorf("unknown ServiceCategory edge %s", name) +} + +// ServiceCategoryTranslationMutation represents an operation that mutates the ServiceCategoryTranslation nodes in the graph. +type ServiceCategoryTranslationMutation struct { + config + op Op + typ string + name *string + description *string + clearedFields map[string]struct{} + category *uuid.UUID + clearedcategory bool + locale_record *string + clearedlocale_record bool + done bool + oldValue func(context.Context) (*ServiceCategoryTranslation, error) + predicates []predicate.ServiceCategoryTranslation +} + +var _ ent.Mutation = (*ServiceCategoryTranslationMutation)(nil) + +// servicecategorytranslationOption allows management of the mutation configuration using functional options. +type servicecategorytranslationOption func(*ServiceCategoryTranslationMutation) + +// newServiceCategoryTranslationMutation creates new mutation for the ServiceCategoryTranslation entity. +func newServiceCategoryTranslationMutation(c config, op Op, opts ...servicecategorytranslationOption) *ServiceCategoryTranslationMutation { + m := &ServiceCategoryTranslationMutation{ + config: c, + op: op, + typ: TypeServiceCategoryTranslation, + clearedFields: make(map[string]struct{}), + } + for _, opt := range opts { + opt(m) + } + return m +} + +// 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 ServiceCategoryTranslationMutation) 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 ServiceCategoryTranslationMutation) 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 +} + +// SetCategoryID sets the "category_id" field. +func (m *ServiceCategoryTranslationMutation) SetCategoryID(u uuid.UUID) { + m.category = &u +} + +// CategoryID returns the value of the "category_id" field in the mutation. +func (m *ServiceCategoryTranslationMutation) CategoryID() (r uuid.UUID, exists bool) { + v := m.category + if v == nil { + return + } + return *v, true +} + +// ResetCategoryID resets all changes to the "category_id" field. +func (m *ServiceCategoryTranslationMutation) ResetCategoryID() { + m.category = nil +} + +// SetLocale sets the "locale" field. +func (m *ServiceCategoryTranslationMutation) SetLocale(s string) { + m.locale_record = &s +} + +// Locale returns the value of the "locale" field in the mutation. +func (m *ServiceCategoryTranslationMutation) Locale() (r string, exists bool) { + v := m.locale_record + if v == nil { + return + } + return *v, true +} + +// ResetLocale resets all changes to the "locale" field. +func (m *ServiceCategoryTranslationMutation) ResetLocale() { + m.locale_record = nil +} + +// SetName sets the "name" field. +func (m *ServiceCategoryTranslationMutation) SetName(s string) { + m.name = &s +} + +// Name returns the value of the "name" field in the mutation. +func (m *ServiceCategoryTranslationMutation) Name() (r string, exists bool) { + v := m.name + if v == nil { + return + } + return *v, true +} + +// ResetName resets all changes to the "name" field. +func (m *ServiceCategoryTranslationMutation) ResetName() { + m.name = nil +} + +// SetDescription sets the "description" field. +func (m *ServiceCategoryTranslationMutation) SetDescription(s string) { + m.description = &s +} + +// Description returns the value of the "description" field in the mutation. +func (m *ServiceCategoryTranslationMutation) Description() (r string, exists bool) { + v := m.description + if v == nil { + return + } + return *v, true +} + +// ClearDescription clears the value of the "description" field. +func (m *ServiceCategoryTranslationMutation) ClearDescription() { + m.description = nil + m.clearedFields[servicecategorytranslation.FieldDescription] = struct{}{} +} + +// DescriptionCleared returns if the "description" field was cleared in this mutation. +func (m *ServiceCategoryTranslationMutation) DescriptionCleared() bool { + _, ok := m.clearedFields[servicecategorytranslation.FieldDescription] + return ok +} + +// ResetDescription resets all changes to the "description" field. +func (m *ServiceCategoryTranslationMutation) ResetDescription() { + m.description = nil + delete(m.clearedFields, servicecategorytranslation.FieldDescription) +} + +// ClearCategory clears the "category" edge to the ServiceCategory entity. +func (m *ServiceCategoryTranslationMutation) ClearCategory() { + m.clearedcategory = true + m.clearedFields[servicecategorytranslation.FieldCategoryID] = struct{}{} +} + +// CategoryCleared reports if the "category" edge to the ServiceCategory entity was cleared. +func (m *ServiceCategoryTranslationMutation) CategoryCleared() bool { + return m.clearedcategory +} + +// CategoryIDs returns the "category" edge IDs in the mutation. +// Note that IDs always returns len(IDs) <= 1 for unique edges, and you should use +// CategoryID instead. It exists only for internal usage by the builders. +func (m *ServiceCategoryTranslationMutation) CategoryIDs() (ids []uuid.UUID) { + if id := m.category; id != nil { + ids = append(ids, *id) + } + return +} + +// ResetCategory resets all changes to the "category" edge. +func (m *ServiceCategoryTranslationMutation) ResetCategory() { + m.category = nil + m.clearedcategory = false +} + +// SetLocaleRecordID sets the "locale_record" edge to the SupportedLocale entity by id. +func (m *ServiceCategoryTranslationMutation) SetLocaleRecordID(id string) { + m.locale_record = &id +} + +// ClearLocaleRecord clears the "locale_record" edge to the SupportedLocale entity. +func (m *ServiceCategoryTranslationMutation) ClearLocaleRecord() { + m.clearedlocale_record = true + m.clearedFields[servicecategorytranslation.FieldLocale] = struct{}{} +} + +// LocaleRecordCleared reports if the "locale_record" edge to the SupportedLocale entity was cleared. +func (m *ServiceCategoryTranslationMutation) LocaleRecordCleared() bool { + return m.clearedlocale_record +} + +// LocaleRecordID returns the "locale_record" edge ID in the mutation. +func (m *ServiceCategoryTranslationMutation) LocaleRecordID() (id string, exists bool) { + if m.locale_record != nil { + return *m.locale_record, true + } + return +} + +// LocaleRecordIDs returns the "locale_record" edge IDs in the mutation. +// Note that IDs always returns len(IDs) <= 1 for unique edges, and you should use +// LocaleRecordID instead. It exists only for internal usage by the builders. +func (m *ServiceCategoryTranslationMutation) LocaleRecordIDs() (ids []string) { + if id := m.locale_record; id != nil { + ids = append(ids, *id) + } + return +} + +// ResetLocaleRecord resets all changes to the "locale_record" edge. +func (m *ServiceCategoryTranslationMutation) ResetLocaleRecord() { + m.locale_record = nil + m.clearedlocale_record = false +} + +// Where appends a list predicates to the ServiceCategoryTranslationMutation builder. +func (m *ServiceCategoryTranslationMutation) Where(ps ...predicate.ServiceCategoryTranslation) { + m.predicates = append(m.predicates, ps...) +} + +// WhereP appends storage-level predicates to the ServiceCategoryTranslationMutation builder. Using this method, +// users can use type-assertion to append predicates that do not depend on any generated package. +func (m *ServiceCategoryTranslationMutation) WhereP(ps ...func(*sql.Selector)) { + p := make([]predicate.ServiceCategoryTranslation, len(ps)) + for i := range ps { + p[i] = ps[i] + } + m.Where(p...) +} + +// Op returns the operation name. +func (m *ServiceCategoryTranslationMutation) Op() Op { + return m.op +} + +// SetOp allows setting the mutation operation. +func (m *ServiceCategoryTranslationMutation) SetOp(op Op) { + m.op = op +} + +// Type returns the node type of this mutation (ServiceCategoryTranslation). +func (m *ServiceCategoryTranslationMutation) 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 *ServiceCategoryTranslationMutation) Fields() []string { + fields := make([]string, 0, 4) + if m.category != nil { + fields = append(fields, servicecategorytranslation.FieldCategoryID) + } + if m.locale_record != nil { + fields = append(fields, servicecategorytranslation.FieldLocale) + } + if m.name != nil { + fields = append(fields, servicecategorytranslation.FieldName) + } + if m.description != nil { + fields = append(fields, servicecategorytranslation.FieldDescription) + } + 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 *ServiceCategoryTranslationMutation) Field(name string) (ent.Value, bool) { + switch name { + case servicecategorytranslation.FieldCategoryID: + return m.CategoryID() + case servicecategorytranslation.FieldLocale: + return m.Locale() + case servicecategorytranslation.FieldName: + return m.Name() + case servicecategorytranslation.FieldDescription: + return m.Description() + } + 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 *ServiceCategoryTranslationMutation) OldField(ctx context.Context, name string) (ent.Value, error) { + return nil, errors.New("edge schema ServiceCategoryTranslation does not support getting old values") +} + +// 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 *ServiceCategoryTranslationMutation) SetField(name string, value ent.Value) error { + switch name { + case servicecategorytranslation.FieldCategoryID: + v, ok := value.(uuid.UUID) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetCategoryID(v) + return nil + case servicecategorytranslation.FieldLocale: + v, ok := value.(string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetLocale(v) + return nil + case servicecategorytranslation.FieldName: + v, ok := value.(string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetName(v) + return nil + case servicecategorytranslation.FieldDescription: + v, ok := value.(string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetDescription(v) + return nil + } + return fmt.Errorf("unknown ServiceCategoryTranslation field %s", name) +} + +// AddedFields returns all numeric fields that were incremented/decremented during +// this mutation. +func (m *ServiceCategoryTranslationMutation) 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 *ServiceCategoryTranslationMutation) 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 *ServiceCategoryTranslationMutation) AddField(name string, value ent.Value) error { + switch name { + } + return fmt.Errorf("unknown ServiceCategoryTranslation numeric field %s", name) +} + +// ClearedFields returns all nullable fields that were cleared during this +// mutation. +func (m *ServiceCategoryTranslationMutation) ClearedFields() []string { + var fields []string + if m.FieldCleared(servicecategorytranslation.FieldDescription) { + fields = append(fields, servicecategorytranslation.FieldDescription) + } + return fields +} + +// FieldCleared returns a boolean indicating if a field with the given name was +// cleared in this mutation. +func (m *ServiceCategoryTranslationMutation) 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 *ServiceCategoryTranslationMutation) ClearField(name string) error { + switch name { + case servicecategorytranslation.FieldDescription: + m.ClearDescription() + return nil + } + return fmt.Errorf("unknown ServiceCategoryTranslation 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 *ServiceCategoryTranslationMutation) ResetField(name string) error { + switch name { + case servicecategorytranslation.FieldCategoryID: + m.ResetCategoryID() + return nil + case servicecategorytranslation.FieldLocale: + m.ResetLocale() + return nil + case servicecategorytranslation.FieldName: + m.ResetName() + return nil + case servicecategorytranslation.FieldDescription: + m.ResetDescription() + return nil + } + return fmt.Errorf("unknown ServiceCategoryTranslation field %s", name) +} + +// AddedEdges returns all edge names that were set/added in this mutation. +func (m *ServiceCategoryTranslationMutation) AddedEdges() []string { + edges := make([]string, 0, 2) + if m.category != nil { + edges = append(edges, servicecategorytranslation.EdgeCategory) + } + if m.locale_record != nil { + edges = append(edges, servicecategorytranslation.EdgeLocaleRecord) + } + return edges +} + +// AddedIDs returns all IDs (to other nodes) that were added for the given edge +// name in this mutation. +func (m *ServiceCategoryTranslationMutation) AddedIDs(name string) []ent.Value { + switch name { + case servicecategorytranslation.EdgeCategory: + if id := m.category; id != nil { + return []ent.Value{*id} + } + case servicecategorytranslation.EdgeLocaleRecord: + if id := m.locale_record; id != nil { + return []ent.Value{*id} + } + } + return nil +} + +// RemovedEdges returns all edge names that were removed in this mutation. +func (m *ServiceCategoryTranslationMutation) 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 *ServiceCategoryTranslationMutation) RemovedIDs(name string) []ent.Value { + return nil +} + +// ClearedEdges returns all edge names that were cleared in this mutation. +func (m *ServiceCategoryTranslationMutation) ClearedEdges() []string { + edges := make([]string, 0, 2) + if m.clearedcategory { + edges = append(edges, servicecategorytranslation.EdgeCategory) + } + if m.clearedlocale_record { + edges = append(edges, servicecategorytranslation.EdgeLocaleRecord) + } + return edges +} + +// EdgeCleared returns a boolean which indicates if the edge with the given name +// was cleared in this mutation. +func (m *ServiceCategoryTranslationMutation) EdgeCleared(name string) bool { + switch name { + case servicecategorytranslation.EdgeCategory: + return m.clearedcategory + case servicecategorytranslation.EdgeLocaleRecord: + return m.clearedlocale_record + } + 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 *ServiceCategoryTranslationMutation) ClearEdge(name string) error { + switch name { + case servicecategorytranslation.EdgeCategory: + m.ClearCategory() + return nil + case servicecategorytranslation.EdgeLocaleRecord: + m.ClearLocaleRecord() + return nil + } + return fmt.Errorf("unknown ServiceCategoryTranslation 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 *ServiceCategoryTranslationMutation) ResetEdge(name string) error { + switch name { + case servicecategorytranslation.EdgeCategory: + m.ResetCategory() + return nil + case servicecategorytranslation.EdgeLocaleRecord: + m.ResetLocaleRecord() + return nil + } + return fmt.Errorf("unknown ServiceCategoryTranslation edge %s", name) +} + +// SpokenLanguageMutation represents an operation that mutates the SpokenLanguage nodes in the graph. +type SpokenLanguageMutation struct { + config + op Op + typ string + id *string + active *bool + sort_order *int + addsort_order *int + clearedFields map[string]struct{} + localized_in map[string]struct{} + removedlocalized_in map[string]struct{} + clearedlocalized_in bool + provider_profiles map[uuid.UUID]struct{} + removedprovider_profiles map[uuid.UUID]struct{} + clearedprovider_profiles bool + done bool + oldValue func(context.Context) (*SpokenLanguage, error) + predicates []predicate.SpokenLanguage +} + +var _ ent.Mutation = (*SpokenLanguageMutation)(nil) + +// spokenlanguageOption allows management of the mutation configuration using functional options. +type spokenlanguageOption func(*SpokenLanguageMutation) + +// newSpokenLanguageMutation creates new mutation for the SpokenLanguage entity. +func newSpokenLanguageMutation(c config, op Op, opts ...spokenlanguageOption) *SpokenLanguageMutation { + m := &SpokenLanguageMutation{ + config: c, + op: op, + typ: TypeSpokenLanguage, + clearedFields: make(map[string]struct{}), + } + for _, opt := range opts { + opt(m) + } + return m +} + +// withSpokenLanguageID sets the ID field of the mutation. +func withSpokenLanguageID(id string) spokenlanguageOption { + return func(m *SpokenLanguageMutation) { + var ( + err error + once sync.Once + value *SpokenLanguage + ) + m.oldValue = func(ctx context.Context) (*SpokenLanguage, error) { + once.Do(func() { + if m.done { + err = errors.New("querying old values post mutation is not allowed") + } else { + value, err = m.Client().SpokenLanguage.Get(ctx, id) + } + }) + return value, err + } + m.id = &id + } +} + +// withSpokenLanguage sets the old SpokenLanguage of the mutation. +func withSpokenLanguage(node *SpokenLanguage) spokenlanguageOption { + return func(m *SpokenLanguageMutation) { + m.oldValue = func(context.Context) (*SpokenLanguage, 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 SpokenLanguageMutation) 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 SpokenLanguageMutation) 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 SpokenLanguage entities. +func (m *SpokenLanguageMutation) SetID(id string) { + 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 *SpokenLanguageMutation) ID() (id string, 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 *SpokenLanguageMutation) IDs(ctx context.Context) ([]string, error) { + switch { + case m.op.Is(OpUpdateOne | OpDeleteOne): + id, exists := m.ID() + if exists { + return []string{id}, nil + } + fallthrough + case m.op.Is(OpUpdate | OpDelete): + return m.Client().SpokenLanguage.Query().Where(m.predicates...).IDs(ctx) + default: + return nil, fmt.Errorf("IDs is not allowed on %s operations", m.op) + } +} + +// SetActive sets the "active" field. +func (m *SpokenLanguageMutation) SetActive(b bool) { + m.active = &b +} + +// Active returns the value of the "active" field in the mutation. +func (m *SpokenLanguageMutation) Active() (r bool, exists bool) { + v := m.active + if v == nil { + return + } + return *v, true +} + +// OldActive returns the old "active" field's value of the SpokenLanguage entity. +// If the SpokenLanguage object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *SpokenLanguageMutation) OldActive(ctx context.Context) (v bool, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldActive is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldActive requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldActive: %w", err) + } + return oldValue.Active, nil +} + +// ResetActive resets all changes to the "active" field. +func (m *SpokenLanguageMutation) ResetActive() { + m.active = nil +} + +// SetSortOrder sets the "sort_order" field. +func (m *SpokenLanguageMutation) SetSortOrder(i int) { + m.sort_order = &i + m.addsort_order = nil +} + +// SortOrder returns the value of the "sort_order" field in the mutation. +func (m *SpokenLanguageMutation) SortOrder() (r int, exists bool) { + v := m.sort_order + if v == nil { + return + } + return *v, true +} + +// OldSortOrder returns the old "sort_order" field's value of the SpokenLanguage entity. +// If the SpokenLanguage object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *SpokenLanguageMutation) OldSortOrder(ctx context.Context) (v int, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldSortOrder is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldSortOrder requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldSortOrder: %w", err) + } + return oldValue.SortOrder, nil +} + +// AddSortOrder adds i to the "sort_order" field. +func (m *SpokenLanguageMutation) AddSortOrder(i int) { + if m.addsort_order != nil { + *m.addsort_order += i + } else { + m.addsort_order = &i + } +} + +// AddedSortOrder returns the value that was added to the "sort_order" field in this mutation. +func (m *SpokenLanguageMutation) AddedSortOrder() (r int, exists bool) { + v := m.addsort_order + if v == nil { + return + } + return *v, true +} + +// ResetSortOrder resets all changes to the "sort_order" field. +func (m *SpokenLanguageMutation) ResetSortOrder() { + m.sort_order = nil + m.addsort_order = nil +} + +// AddLocalizedInIDs adds the "localized_in" edge to the SupportedLocale entity by ids. +func (m *SpokenLanguageMutation) AddLocalizedInIDs(ids ...string) { + if m.localized_in == nil { + m.localized_in = make(map[string]struct{}) + } + for i := range ids { + m.localized_in[ids[i]] = struct{}{} + } +} + +// ClearLocalizedIn clears the "localized_in" edge to the SupportedLocale entity. +func (m *SpokenLanguageMutation) ClearLocalizedIn() { + m.clearedlocalized_in = true +} + +// LocalizedInCleared reports if the "localized_in" edge to the SupportedLocale entity was cleared. +func (m *SpokenLanguageMutation) LocalizedInCleared() bool { + return m.clearedlocalized_in +} + +// RemoveLocalizedInIDs removes the "localized_in" edge to the SupportedLocale entity by IDs. +func (m *SpokenLanguageMutation) RemoveLocalizedInIDs(ids ...string) { + if m.removedlocalized_in == nil { + m.removedlocalized_in = make(map[string]struct{}) + } + for i := range ids { + delete(m.localized_in, ids[i]) + m.removedlocalized_in[ids[i]] = struct{}{} + } +} + +// RemovedLocalizedIn returns the removed IDs of the "localized_in" edge to the SupportedLocale entity. +func (m *SpokenLanguageMutation) RemovedLocalizedInIDs() (ids []string) { + for id := range m.removedlocalized_in { + ids = append(ids, id) + } + return +} + +// LocalizedInIDs returns the "localized_in" edge IDs in the mutation. +func (m *SpokenLanguageMutation) LocalizedInIDs() (ids []string) { + for id := range m.localized_in { + ids = append(ids, id) + } + return +} + +// ResetLocalizedIn resets all changes to the "localized_in" edge. +func (m *SpokenLanguageMutation) ResetLocalizedIn() { + m.localized_in = nil + m.clearedlocalized_in = false + m.removedlocalized_in = nil +} + +// AddProviderProfileIDs adds the "provider_profiles" edge to the ProviderProfile entity by ids. +func (m *SpokenLanguageMutation) AddProviderProfileIDs(ids ...uuid.UUID) { + if m.provider_profiles == nil { + m.provider_profiles = make(map[uuid.UUID]struct{}) + } + for i := range ids { + m.provider_profiles[ids[i]] = struct{}{} + } +} + +// ClearProviderProfiles clears the "provider_profiles" edge to the ProviderProfile entity. +func (m *SpokenLanguageMutation) ClearProviderProfiles() { + m.clearedprovider_profiles = true +} + +// ProviderProfilesCleared reports if the "provider_profiles" edge to the ProviderProfile entity was cleared. +func (m *SpokenLanguageMutation) ProviderProfilesCleared() bool { + return m.clearedprovider_profiles +} + +// RemoveProviderProfileIDs removes the "provider_profiles" edge to the ProviderProfile entity by IDs. +func (m *SpokenLanguageMutation) RemoveProviderProfileIDs(ids ...uuid.UUID) { + if m.removedprovider_profiles == nil { + m.removedprovider_profiles = make(map[uuid.UUID]struct{}) + } + for i := range ids { + delete(m.provider_profiles, ids[i]) + m.removedprovider_profiles[ids[i]] = struct{}{} + } +} + +// RemovedProviderProfiles returns the removed IDs of the "provider_profiles" edge to the ProviderProfile entity. +func (m *SpokenLanguageMutation) RemovedProviderProfilesIDs() (ids []uuid.UUID) { + for id := range m.removedprovider_profiles { + ids = append(ids, id) + } + return +} + +// ProviderProfilesIDs returns the "provider_profiles" edge IDs in the mutation. +func (m *SpokenLanguageMutation) ProviderProfilesIDs() (ids []uuid.UUID) { + for id := range m.provider_profiles { + ids = append(ids, id) + } + return +} + +// ResetProviderProfiles resets all changes to the "provider_profiles" edge. +func (m *SpokenLanguageMutation) ResetProviderProfiles() { + m.provider_profiles = nil + m.clearedprovider_profiles = false + m.removedprovider_profiles = nil +} + +// Where appends a list predicates to the SpokenLanguageMutation builder. +func (m *SpokenLanguageMutation) Where(ps ...predicate.SpokenLanguage) { + m.predicates = append(m.predicates, ps...) +} + +// WhereP appends storage-level predicates to the SpokenLanguageMutation builder. Using this method, +// users can use type-assertion to append predicates that do not depend on any generated package. +func (m *SpokenLanguageMutation) WhereP(ps ...func(*sql.Selector)) { + p := make([]predicate.SpokenLanguage, len(ps)) + for i := range ps { + p[i] = ps[i] + } + m.Where(p...) +} + +// Op returns the operation name. +func (m *SpokenLanguageMutation) Op() Op { + return m.op +} + +// SetOp allows setting the mutation operation. +func (m *SpokenLanguageMutation) SetOp(op Op) { + m.op = op +} + +// Type returns the node type of this mutation (SpokenLanguage). +func (m *SpokenLanguageMutation) 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 *SpokenLanguageMutation) Fields() []string { + fields := make([]string, 0, 2) + if m.active != nil { + fields = append(fields, spokenlanguage.FieldActive) + } + if m.sort_order != nil { + fields = append(fields, spokenlanguage.FieldSortOrder) + } + 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 *SpokenLanguageMutation) Field(name string) (ent.Value, bool) { + switch name { + case spokenlanguage.FieldActive: + return m.Active() + case spokenlanguage.FieldSortOrder: + return m.SortOrder() + } + 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 *SpokenLanguageMutation) OldField(ctx context.Context, name string) (ent.Value, error) { + switch name { + case spokenlanguage.FieldActive: + return m.OldActive(ctx) + case spokenlanguage.FieldSortOrder: + return m.OldSortOrder(ctx) + } + return nil, fmt.Errorf("unknown SpokenLanguage 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 *SpokenLanguageMutation) SetField(name string, value ent.Value) error { + switch name { + case spokenlanguage.FieldActive: + v, ok := value.(bool) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetActive(v) + return nil + case spokenlanguage.FieldSortOrder: + v, ok := value.(int) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetSortOrder(v) + return nil + } + return fmt.Errorf("unknown SpokenLanguage field %s", name) +} + +// AddedFields returns all numeric fields that were incremented/decremented during +// this mutation. +func (m *SpokenLanguageMutation) AddedFields() []string { + var fields []string + if m.addsort_order != nil { + fields = append(fields, spokenlanguage.FieldSortOrder) + } + 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 *SpokenLanguageMutation) AddedField(name string) (ent.Value, bool) { + switch name { + case spokenlanguage.FieldSortOrder: + return m.AddedSortOrder() + } + 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 *SpokenLanguageMutation) AddField(name string, value ent.Value) error { + switch name { + case spokenlanguage.FieldSortOrder: + v, ok := value.(int) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.AddSortOrder(v) + return nil + } + return fmt.Errorf("unknown SpokenLanguage numeric field %s", name) +} + +// ClearedFields returns all nullable fields that were cleared during this +// mutation. +func (m *SpokenLanguageMutation) ClearedFields() []string { + return nil +} + +// FieldCleared returns a boolean indicating if a field with the given name was +// cleared in this mutation. +func (m *SpokenLanguageMutation) 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 *SpokenLanguageMutation) ClearField(name string) error { + return fmt.Errorf("unknown SpokenLanguage 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 *SpokenLanguageMutation) ResetField(name string) error { + switch name { + case spokenlanguage.FieldActive: + m.ResetActive() + return nil + case spokenlanguage.FieldSortOrder: + m.ResetSortOrder() + return nil + } + return fmt.Errorf("unknown SpokenLanguage field %s", name) +} + +// AddedEdges returns all edge names that were set/added in this mutation. +func (m *SpokenLanguageMutation) AddedEdges() []string { + edges := make([]string, 0, 2) + if m.localized_in != nil { + edges = append(edges, spokenlanguage.EdgeLocalizedIn) + } + if m.provider_profiles != nil { + edges = append(edges, spokenlanguage.EdgeProviderProfiles) + } + return edges +} + +// AddedIDs returns all IDs (to other nodes) that were added for the given edge +// name in this mutation. +func (m *SpokenLanguageMutation) AddedIDs(name string) []ent.Value { + switch name { + case spokenlanguage.EdgeLocalizedIn: + ids := make([]ent.Value, 0, len(m.localized_in)) + for id := range m.localized_in { + ids = append(ids, id) + } + return ids + case spokenlanguage.EdgeProviderProfiles: + ids := make([]ent.Value, 0, len(m.provider_profiles)) + for id := range m.provider_profiles { + ids = append(ids, id) + } + return ids + } + return nil +} + +// RemovedEdges returns all edge names that were removed in this mutation. +func (m *SpokenLanguageMutation) RemovedEdges() []string { + edges := make([]string, 0, 2) + if m.removedlocalized_in != nil { + edges = append(edges, spokenlanguage.EdgeLocalizedIn) + } + if m.removedprovider_profiles != nil { + edges = append(edges, spokenlanguage.EdgeProviderProfiles) + } + return edges +} + +// RemovedIDs returns all IDs (to other nodes) that were removed for the edge with +// the given name in this mutation. +func (m *SpokenLanguageMutation) RemovedIDs(name string) []ent.Value { + switch name { + case spokenlanguage.EdgeLocalizedIn: + ids := make([]ent.Value, 0, len(m.removedlocalized_in)) + for id := range m.removedlocalized_in { + ids = append(ids, id) + } + return ids + case spokenlanguage.EdgeProviderProfiles: + ids := make([]ent.Value, 0, len(m.removedprovider_profiles)) + for id := range m.removedprovider_profiles { + ids = append(ids, id) + } + return ids + } + return nil +} + +// ClearedEdges returns all edge names that were cleared in this mutation. +func (m *SpokenLanguageMutation) ClearedEdges() []string { + edges := make([]string, 0, 2) + if m.clearedlocalized_in { + edges = append(edges, spokenlanguage.EdgeLocalizedIn) + } + if m.clearedprovider_profiles { + edges = append(edges, spokenlanguage.EdgeProviderProfiles) + } + return edges +} + +// EdgeCleared returns a boolean which indicates if the edge with the given name +// was cleared in this mutation. +func (m *SpokenLanguageMutation) EdgeCleared(name string) bool { + switch name { + case spokenlanguage.EdgeLocalizedIn: + return m.clearedlocalized_in + case spokenlanguage.EdgeProviderProfiles: + return m.clearedprovider_profiles + } + 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 *SpokenLanguageMutation) ClearEdge(name string) error { + switch name { + } + return fmt.Errorf("unknown SpokenLanguage 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 *SpokenLanguageMutation) ResetEdge(name string) error { + switch name { + case spokenlanguage.EdgeLocalizedIn: + m.ResetLocalizedIn() + return nil + case spokenlanguage.EdgeProviderProfiles: + m.ResetProviderProfiles() + return nil + } + return fmt.Errorf("unknown SpokenLanguage edge %s", name) +} + +// SpokenLanguageTranslationMutation represents an operation that mutates the SpokenLanguageTranslation nodes in the graph. +type SpokenLanguageTranslationMutation struct { + config + op Op + typ string + name *string + clearedFields map[string]struct{} + language *string + clearedlanguage bool + locale_record *string + clearedlocale_record bool + done bool + oldValue func(context.Context) (*SpokenLanguageTranslation, error) + predicates []predicate.SpokenLanguageTranslation +} + +var _ ent.Mutation = (*SpokenLanguageTranslationMutation)(nil) + +// spokenlanguagetranslationOption allows management of the mutation configuration using functional options. +type spokenlanguagetranslationOption func(*SpokenLanguageTranslationMutation) + +// newSpokenLanguageTranslationMutation creates new mutation for the SpokenLanguageTranslation entity. +func newSpokenLanguageTranslationMutation(c config, op Op, opts ...spokenlanguagetranslationOption) *SpokenLanguageTranslationMutation { + m := &SpokenLanguageTranslationMutation{ + config: c, + op: op, + typ: TypeSpokenLanguageTranslation, + clearedFields: make(map[string]struct{}), + } + for _, opt := range opts { + opt(m) + } + return m +} + +// 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 SpokenLanguageTranslationMutation) 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 SpokenLanguageTranslationMutation) 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 +} + +// SetLanguageCode sets the "language_code" field. +func (m *SpokenLanguageTranslationMutation) SetLanguageCode(s string) { + m.language = &s +} + +// LanguageCode returns the value of the "language_code" field in the mutation. +func (m *SpokenLanguageTranslationMutation) LanguageCode() (r string, exists bool) { + v := m.language + if v == nil { + return + } + return *v, true +} + +// ResetLanguageCode resets all changes to the "language_code" field. +func (m *SpokenLanguageTranslationMutation) ResetLanguageCode() { + m.language = nil +} + +// SetLocale sets the "locale" field. +func (m *SpokenLanguageTranslationMutation) SetLocale(s string) { + m.locale_record = &s +} + +// Locale returns the value of the "locale" field in the mutation. +func (m *SpokenLanguageTranslationMutation) Locale() (r string, exists bool) { + v := m.locale_record + if v == nil { + return + } + return *v, true +} + +// ResetLocale resets all changes to the "locale" field. +func (m *SpokenLanguageTranslationMutation) ResetLocale() { + m.locale_record = nil +} + +// SetName sets the "name" field. +func (m *SpokenLanguageTranslationMutation) SetName(s string) { + m.name = &s +} + +// Name returns the value of the "name" field in the mutation. +func (m *SpokenLanguageTranslationMutation) Name() (r string, exists bool) { + v := m.name + if v == nil { + return + } + return *v, true +} + +// ResetName resets all changes to the "name" field. +func (m *SpokenLanguageTranslationMutation) ResetName() { + m.name = nil +} + +// SetLanguageID sets the "language" edge to the SpokenLanguage entity by id. +func (m *SpokenLanguageTranslationMutation) SetLanguageID(id string) { + m.language = &id +} + +// ClearLanguage clears the "language" edge to the SpokenLanguage entity. +func (m *SpokenLanguageTranslationMutation) ClearLanguage() { + m.clearedlanguage = true + m.clearedFields[spokenlanguagetranslation.FieldLanguageCode] = struct{}{} +} + +// LanguageCleared reports if the "language" edge to the SpokenLanguage entity was cleared. +func (m *SpokenLanguageTranslationMutation) LanguageCleared() bool { + return m.clearedlanguage +} + +// LanguageID returns the "language" edge ID in the mutation. +func (m *SpokenLanguageTranslationMutation) LanguageID() (id string, exists bool) { + if m.language != nil { + return *m.language, true + } + return +} + +// LanguageIDs returns the "language" edge IDs in the mutation. +// Note that IDs always returns len(IDs) <= 1 for unique edges, and you should use +// LanguageID instead. It exists only for internal usage by the builders. +func (m *SpokenLanguageTranslationMutation) LanguageIDs() (ids []string) { + if id := m.language; id != nil { + ids = append(ids, *id) + } + return +} + +// ResetLanguage resets all changes to the "language" edge. +func (m *SpokenLanguageTranslationMutation) ResetLanguage() { + m.language = nil + m.clearedlanguage = false +} + +// SetLocaleRecordID sets the "locale_record" edge to the SupportedLocale entity by id. +func (m *SpokenLanguageTranslationMutation) SetLocaleRecordID(id string) { + m.locale_record = &id +} + +// ClearLocaleRecord clears the "locale_record" edge to the SupportedLocale entity. +func (m *SpokenLanguageTranslationMutation) ClearLocaleRecord() { + m.clearedlocale_record = true + m.clearedFields[spokenlanguagetranslation.FieldLocale] = struct{}{} +} + +// LocaleRecordCleared reports if the "locale_record" edge to the SupportedLocale entity was cleared. +func (m *SpokenLanguageTranslationMutation) LocaleRecordCleared() bool { + return m.clearedlocale_record +} + +// LocaleRecordID returns the "locale_record" edge ID in the mutation. +func (m *SpokenLanguageTranslationMutation) LocaleRecordID() (id string, exists bool) { + if m.locale_record != nil { + return *m.locale_record, true + } + return +} + +// LocaleRecordIDs returns the "locale_record" edge IDs in the mutation. +// Note that IDs always returns len(IDs) <= 1 for unique edges, and you should use +// LocaleRecordID instead. It exists only for internal usage by the builders. +func (m *SpokenLanguageTranslationMutation) LocaleRecordIDs() (ids []string) { + if id := m.locale_record; id != nil { + ids = append(ids, *id) + } + return +} + +// ResetLocaleRecord resets all changes to the "locale_record" edge. +func (m *SpokenLanguageTranslationMutation) ResetLocaleRecord() { + m.locale_record = nil + m.clearedlocale_record = false +} + +// Where appends a list predicates to the SpokenLanguageTranslationMutation builder. +func (m *SpokenLanguageTranslationMutation) Where(ps ...predicate.SpokenLanguageTranslation) { + m.predicates = append(m.predicates, ps...) +} + +// WhereP appends storage-level predicates to the SpokenLanguageTranslationMutation builder. Using this method, +// users can use type-assertion to append predicates that do not depend on any generated package. +func (m *SpokenLanguageTranslationMutation) WhereP(ps ...func(*sql.Selector)) { + p := make([]predicate.SpokenLanguageTranslation, len(ps)) + for i := range ps { + p[i] = ps[i] + } + m.Where(p...) +} + +// Op returns the operation name. +func (m *SpokenLanguageTranslationMutation) Op() Op { + return m.op +} + +// SetOp allows setting the mutation operation. +func (m *SpokenLanguageTranslationMutation) SetOp(op Op) { + m.op = op +} + +// Type returns the node type of this mutation (SpokenLanguageTranslation). +func (m *SpokenLanguageTranslationMutation) 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 *SpokenLanguageTranslationMutation) Fields() []string { + fields := make([]string, 0, 3) + if m.language != nil { + fields = append(fields, spokenlanguagetranslation.FieldLanguageCode) + } + if m.locale_record != nil { + fields = append(fields, spokenlanguagetranslation.FieldLocale) + } + if m.name != nil { + fields = append(fields, spokenlanguagetranslation.FieldName) + } + 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 *SpokenLanguageTranslationMutation) Field(name string) (ent.Value, bool) { + switch name { + case spokenlanguagetranslation.FieldLanguageCode: + return m.LanguageCode() + case spokenlanguagetranslation.FieldLocale: + return m.Locale() + case spokenlanguagetranslation.FieldName: + return m.Name() + } + 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 *SpokenLanguageTranslationMutation) OldField(ctx context.Context, name string) (ent.Value, error) { + return nil, errors.New("edge schema SpokenLanguageTranslation does not support getting old values") +} + +// 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 *SpokenLanguageTranslationMutation) SetField(name string, value ent.Value) error { + switch name { + case spokenlanguagetranslation.FieldLanguageCode: + v, ok := value.(string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetLanguageCode(v) + return nil + case spokenlanguagetranslation.FieldLocale: + v, ok := value.(string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetLocale(v) + return nil + case spokenlanguagetranslation.FieldName: + v, ok := value.(string) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetName(v) + return nil + } + return fmt.Errorf("unknown SpokenLanguageTranslation field %s", name) +} + +// AddedFields returns all numeric fields that were incremented/decremented during +// this mutation. +func (m *SpokenLanguageTranslationMutation) 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 *SpokenLanguageTranslationMutation) 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 *SpokenLanguageTranslationMutation) AddField(name string, value ent.Value) error { + switch name { + } + return fmt.Errorf("unknown SpokenLanguageTranslation numeric field %s", name) +} + +// ClearedFields returns all nullable fields that were cleared during this +// mutation. +func (m *SpokenLanguageTranslationMutation) ClearedFields() []string { + return nil +} + +// FieldCleared returns a boolean indicating if a field with the given name was +// cleared in this mutation. +func (m *SpokenLanguageTranslationMutation) 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 *SpokenLanguageTranslationMutation) ClearField(name string) error { + return fmt.Errorf("unknown SpokenLanguageTranslation 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 *SpokenLanguageTranslationMutation) ResetField(name string) error { + switch name { + case spokenlanguagetranslation.FieldLanguageCode: + m.ResetLanguageCode() + return nil + case spokenlanguagetranslation.FieldLocale: + m.ResetLocale() + return nil + case spokenlanguagetranslation.FieldName: + m.ResetName() + return nil + } + return fmt.Errorf("unknown SpokenLanguageTranslation field %s", name) +} + +// AddedEdges returns all edge names that were set/added in this mutation. +func (m *SpokenLanguageTranslationMutation) AddedEdges() []string { + edges := make([]string, 0, 2) + if m.language != nil { + edges = append(edges, spokenlanguagetranslation.EdgeLanguage) + } + if m.locale_record != nil { + edges = append(edges, spokenlanguagetranslation.EdgeLocaleRecord) + } + return edges +} + +// AddedIDs returns all IDs (to other nodes) that were added for the given edge +// name in this mutation. +func (m *SpokenLanguageTranslationMutation) AddedIDs(name string) []ent.Value { + switch name { + case spokenlanguagetranslation.EdgeLanguage: + if id := m.language; id != nil { + return []ent.Value{*id} + } + case spokenlanguagetranslation.EdgeLocaleRecord: + if id := m.locale_record; id != nil { + return []ent.Value{*id} + } + } + return nil +} + +// RemovedEdges returns all edge names that were removed in this mutation. +func (m *SpokenLanguageTranslationMutation) 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 *SpokenLanguageTranslationMutation) RemovedIDs(name string) []ent.Value { + return nil +} + +// ClearedEdges returns all edge names that were cleared in this mutation. +func (m *SpokenLanguageTranslationMutation) ClearedEdges() []string { + edges := make([]string, 0, 2) + if m.clearedlanguage { + edges = append(edges, spokenlanguagetranslation.EdgeLanguage) + } + if m.clearedlocale_record { + edges = append(edges, spokenlanguagetranslation.EdgeLocaleRecord) + } + return edges +} + +// EdgeCleared returns a boolean which indicates if the edge with the given name +// was cleared in this mutation. +func (m *SpokenLanguageTranslationMutation) EdgeCleared(name string) bool { + switch name { + case spokenlanguagetranslation.EdgeLanguage: + return m.clearedlanguage + case spokenlanguagetranslation.EdgeLocaleRecord: + return m.clearedlocale_record + } + 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 *SpokenLanguageTranslationMutation) ClearEdge(name string) error { + switch name { + case spokenlanguagetranslation.EdgeLanguage: + m.ClearLanguage() + return nil + case spokenlanguagetranslation.EdgeLocaleRecord: + m.ClearLocaleRecord() + return nil + } + return fmt.Errorf("unknown SpokenLanguageTranslation 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 *SpokenLanguageTranslationMutation) ResetEdge(name string) error { + switch name { + case spokenlanguagetranslation.EdgeLanguage: + m.ResetLanguage() + return nil + case spokenlanguagetranslation.EdgeLocaleRecord: + m.ResetLocaleRecord() + return nil + } + return fmt.Errorf("unknown SpokenLanguageTranslation edge %s", name) +} + +// SupportedLocaleMutation represents an operation that mutates the SupportedLocale nodes in the graph. +type SupportedLocaleMutation struct { + config + op Op + typ string + id *string + active *bool + sort_order *int + addsort_order *int + clearedFields map[string]struct{} + translated_categories map[uuid.UUID]struct{} + removedtranslated_categories map[uuid.UUID]struct{} + clearedtranslated_categories bool + translated_languages map[string]struct{} + removedtranslated_languages map[string]struct{} + clearedtranslated_languages bool + done bool + oldValue func(context.Context) (*SupportedLocale, error) + predicates []predicate.SupportedLocale +} + +var _ ent.Mutation = (*SupportedLocaleMutation)(nil) + +// supportedlocaleOption allows management of the mutation configuration using functional options. +type supportedlocaleOption func(*SupportedLocaleMutation) + +// newSupportedLocaleMutation creates new mutation for the SupportedLocale entity. +func newSupportedLocaleMutation(c config, op Op, opts ...supportedlocaleOption) *SupportedLocaleMutation { + m := &SupportedLocaleMutation{ + config: c, + op: op, + typ: TypeSupportedLocale, + clearedFields: make(map[string]struct{}), + } + for _, opt := range opts { + opt(m) + } + return m +} + +// withSupportedLocaleID sets the ID field of the mutation. +func withSupportedLocaleID(id string) supportedlocaleOption { + return func(m *SupportedLocaleMutation) { + var ( + err error + once sync.Once + value *SupportedLocale + ) + m.oldValue = func(ctx context.Context) (*SupportedLocale, error) { + once.Do(func() { + if m.done { + err = errors.New("querying old values post mutation is not allowed") + } else { + value, err = m.Client().SupportedLocale.Get(ctx, id) + } + }) + return value, err + } + m.id = &id + } +} + +// withSupportedLocale sets the old SupportedLocale of the mutation. +func withSupportedLocale(node *SupportedLocale) supportedlocaleOption { + return func(m *SupportedLocaleMutation) { + m.oldValue = func(context.Context) (*SupportedLocale, 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 SupportedLocaleMutation) 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 SupportedLocaleMutation) 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 SupportedLocale entities. +func (m *SupportedLocaleMutation) SetID(id string) { + 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 *SupportedLocaleMutation) ID() (id string, 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 *SupportedLocaleMutation) IDs(ctx context.Context) ([]string, error) { + switch { + case m.op.Is(OpUpdateOne | OpDeleteOne): + id, exists := m.ID() + if exists { + return []string{id}, nil + } + fallthrough + case m.op.Is(OpUpdate | OpDelete): + return m.Client().SupportedLocale.Query().Where(m.predicates...).IDs(ctx) + default: + return nil, fmt.Errorf("IDs is not allowed on %s operations", m.op) + } +} + +// SetActive sets the "active" field. +func (m *SupportedLocaleMutation) SetActive(b bool) { + m.active = &b +} + +// Active returns the value of the "active" field in the mutation. +func (m *SupportedLocaleMutation) Active() (r bool, exists bool) { + v := m.active + if v == nil { + return + } + return *v, true +} + +// OldActive returns the old "active" field's value of the SupportedLocale entity. +// If the SupportedLocale object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *SupportedLocaleMutation) OldActive(ctx context.Context) (v bool, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldActive is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldActive requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldActive: %w", err) + } + return oldValue.Active, nil +} + +// ResetActive resets all changes to the "active" field. +func (m *SupportedLocaleMutation) ResetActive() { + m.active = nil +} + +// SetSortOrder sets the "sort_order" field. +func (m *SupportedLocaleMutation) SetSortOrder(i int) { + m.sort_order = &i + m.addsort_order = nil +} + +// SortOrder returns the value of the "sort_order" field in the mutation. +func (m *SupportedLocaleMutation) SortOrder() (r int, exists bool) { + v := m.sort_order + if v == nil { + return + } + return *v, true +} + +// OldSortOrder returns the old "sort_order" field's value of the SupportedLocale entity. +// If the SupportedLocale object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *SupportedLocaleMutation) OldSortOrder(ctx context.Context) (v int, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldSortOrder is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldSortOrder requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldSortOrder: %w", err) + } + return oldValue.SortOrder, nil +} + +// AddSortOrder adds i to the "sort_order" field. +func (m *SupportedLocaleMutation) AddSortOrder(i int) { + if m.addsort_order != nil { + *m.addsort_order += i + } else { + m.addsort_order = &i + } +} + +// AddedSortOrder returns the value that was added to the "sort_order" field in this mutation. +func (m *SupportedLocaleMutation) AddedSortOrder() (r int, exists bool) { + v := m.addsort_order + if v == nil { + return + } + return *v, true +} + +// ResetSortOrder resets all changes to the "sort_order" field. +func (m *SupportedLocaleMutation) ResetSortOrder() { + m.sort_order = nil + m.addsort_order = nil +} + +// AddTranslatedCategoryIDs adds the "translated_categories" edge to the ServiceCategory entity by ids. +func (m *SupportedLocaleMutation) AddTranslatedCategoryIDs(ids ...uuid.UUID) { + if m.translated_categories == nil { + m.translated_categories = make(map[uuid.UUID]struct{}) + } + for i := range ids { + m.translated_categories[ids[i]] = struct{}{} + } +} + +// ClearTranslatedCategories clears the "translated_categories" edge to the ServiceCategory entity. +func (m *SupportedLocaleMutation) ClearTranslatedCategories() { + m.clearedtranslated_categories = true +} + +// TranslatedCategoriesCleared reports if the "translated_categories" edge to the ServiceCategory entity was cleared. +func (m *SupportedLocaleMutation) TranslatedCategoriesCleared() bool { + return m.clearedtranslated_categories +} + +// RemoveTranslatedCategoryIDs removes the "translated_categories" edge to the ServiceCategory entity by IDs. +func (m *SupportedLocaleMutation) RemoveTranslatedCategoryIDs(ids ...uuid.UUID) { + if m.removedtranslated_categories == nil { + m.removedtranslated_categories = make(map[uuid.UUID]struct{}) + } + for i := range ids { + delete(m.translated_categories, ids[i]) + m.removedtranslated_categories[ids[i]] = struct{}{} + } +} + +// RemovedTranslatedCategories returns the removed IDs of the "translated_categories" edge to the ServiceCategory entity. +func (m *SupportedLocaleMutation) RemovedTranslatedCategoriesIDs() (ids []uuid.UUID) { + for id := range m.removedtranslated_categories { + ids = append(ids, id) + } + return +} + +// TranslatedCategoriesIDs returns the "translated_categories" edge IDs in the mutation. +func (m *SupportedLocaleMutation) TranslatedCategoriesIDs() (ids []uuid.UUID) { + for id := range m.translated_categories { + ids = append(ids, id) + } + return +} + +// ResetTranslatedCategories resets all changes to the "translated_categories" edge. +func (m *SupportedLocaleMutation) ResetTranslatedCategories() { + m.translated_categories = nil + m.clearedtranslated_categories = false + m.removedtranslated_categories = nil +} + +// AddTranslatedLanguageIDs adds the "translated_languages" edge to the SpokenLanguage entity by ids. +func (m *SupportedLocaleMutation) AddTranslatedLanguageIDs(ids ...string) { + if m.translated_languages == nil { + m.translated_languages = make(map[string]struct{}) + } + for i := range ids { + m.translated_languages[ids[i]] = struct{}{} + } +} + +// ClearTranslatedLanguages clears the "translated_languages" edge to the SpokenLanguage entity. +func (m *SupportedLocaleMutation) ClearTranslatedLanguages() { + m.clearedtranslated_languages = true +} + +// TranslatedLanguagesCleared reports if the "translated_languages" edge to the SpokenLanguage entity was cleared. +func (m *SupportedLocaleMutation) TranslatedLanguagesCleared() bool { + return m.clearedtranslated_languages +} + +// RemoveTranslatedLanguageIDs removes the "translated_languages" edge to the SpokenLanguage entity by IDs. +func (m *SupportedLocaleMutation) RemoveTranslatedLanguageIDs(ids ...string) { + if m.removedtranslated_languages == nil { + m.removedtranslated_languages = make(map[string]struct{}) + } + for i := range ids { + delete(m.translated_languages, ids[i]) + m.removedtranslated_languages[ids[i]] = struct{}{} + } +} + +// RemovedTranslatedLanguages returns the removed IDs of the "translated_languages" edge to the SpokenLanguage entity. +func (m *SupportedLocaleMutation) RemovedTranslatedLanguagesIDs() (ids []string) { + for id := range m.removedtranslated_languages { + ids = append(ids, id) + } + return +} + +// TranslatedLanguagesIDs returns the "translated_languages" edge IDs in the mutation. +func (m *SupportedLocaleMutation) TranslatedLanguagesIDs() (ids []string) { + for id := range m.translated_languages { + ids = append(ids, id) + } + return +} + +// ResetTranslatedLanguages resets all changes to the "translated_languages" edge. +func (m *SupportedLocaleMutation) ResetTranslatedLanguages() { + m.translated_languages = nil + m.clearedtranslated_languages = false + m.removedtranslated_languages = nil +} + +// Where appends a list predicates to the SupportedLocaleMutation builder. +func (m *SupportedLocaleMutation) Where(ps ...predicate.SupportedLocale) { + m.predicates = append(m.predicates, ps...) +} + +// WhereP appends storage-level predicates to the SupportedLocaleMutation builder. Using this method, +// users can use type-assertion to append predicates that do not depend on any generated package. +func (m *SupportedLocaleMutation) WhereP(ps ...func(*sql.Selector)) { + p := make([]predicate.SupportedLocale, len(ps)) + for i := range ps { + p[i] = ps[i] + } + m.Where(p...) +} + +// Op returns the operation name. +func (m *SupportedLocaleMutation) Op() Op { + return m.op +} + +// SetOp allows setting the mutation operation. +func (m *SupportedLocaleMutation) SetOp(op Op) { + m.op = op +} + +// Type returns the node type of this mutation (SupportedLocale). +func (m *SupportedLocaleMutation) 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 *SupportedLocaleMutation) Fields() []string { + fields := make([]string, 0, 2) + if m.active != nil { + fields = append(fields, supportedlocale.FieldActive) + } + if m.sort_order != nil { + fields = append(fields, supportedlocale.FieldSortOrder) + } + 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 *SupportedLocaleMutation) Field(name string) (ent.Value, bool) { + switch name { + case supportedlocale.FieldActive: + return m.Active() + case supportedlocale.FieldSortOrder: + return m.SortOrder() + } + 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 *SupportedLocaleMutation) OldField(ctx context.Context, name string) (ent.Value, error) { + switch name { + case supportedlocale.FieldActive: + return m.OldActive(ctx) + case supportedlocale.FieldSortOrder: + return m.OldSortOrder(ctx) + } + return nil, fmt.Errorf("unknown SupportedLocale 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 *SupportedLocaleMutation) SetField(name string, value ent.Value) error { + switch name { + case supportedlocale.FieldActive: + v, ok := value.(bool) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetActive(v) + return nil + case supportedlocale.FieldSortOrder: + v, ok := value.(int) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetSortOrder(v) + return nil + } + return fmt.Errorf("unknown SupportedLocale field %s", name) +} + +// AddedFields returns all numeric fields that were incremented/decremented during +// this mutation. +func (m *SupportedLocaleMutation) AddedFields() []string { + var fields []string + if m.addsort_order != nil { + fields = append(fields, supportedlocale.FieldSortOrder) + } + 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 *SupportedLocaleMutation) AddedField(name string) (ent.Value, bool) { + switch name { + case supportedlocale.FieldSortOrder: + return m.AddedSortOrder() + } + 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 *SupportedLocaleMutation) AddField(name string, value ent.Value) error { + switch name { + case supportedlocale.FieldSortOrder: + v, ok := value.(int) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.AddSortOrder(v) + return nil + } + return fmt.Errorf("unknown SupportedLocale numeric field %s", name) +} + +// ClearedFields returns all nullable fields that were cleared during this +// mutation. +func (m *SupportedLocaleMutation) ClearedFields() []string { + return nil +} + +// FieldCleared returns a boolean indicating if a field with the given name was +// cleared in this mutation. +func (m *SupportedLocaleMutation) 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 *SupportedLocaleMutation) ClearField(name string) error { + return fmt.Errorf("unknown SupportedLocale 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 *SupportedLocaleMutation) ResetField(name string) error { + switch name { + case supportedlocale.FieldActive: + m.ResetActive() + return nil + case supportedlocale.FieldSortOrder: + m.ResetSortOrder() + return nil + } + return fmt.Errorf("unknown SupportedLocale field %s", name) +} + +// AddedEdges returns all edge names that were set/added in this mutation. +func (m *SupportedLocaleMutation) AddedEdges() []string { + edges := make([]string, 0, 2) + if m.translated_categories != nil { + edges = append(edges, supportedlocale.EdgeTranslatedCategories) + } + if m.translated_languages != nil { + edges = append(edges, supportedlocale.EdgeTranslatedLanguages) + } + return edges +} + +// AddedIDs returns all IDs (to other nodes) that were added for the given edge +// name in this mutation. +func (m *SupportedLocaleMutation) AddedIDs(name string) []ent.Value { + switch name { + case supportedlocale.EdgeTranslatedCategories: + ids := make([]ent.Value, 0, len(m.translated_categories)) + for id := range m.translated_categories { + ids = append(ids, id) + } + return ids + case supportedlocale.EdgeTranslatedLanguages: + ids := make([]ent.Value, 0, len(m.translated_languages)) + for id := range m.translated_languages { + ids = append(ids, id) + } + return ids + } + return nil +} + +// RemovedEdges returns all edge names that were removed in this mutation. +func (m *SupportedLocaleMutation) RemovedEdges() []string { + edges := make([]string, 0, 2) + if m.removedtranslated_categories != nil { + edges = append(edges, supportedlocale.EdgeTranslatedCategories) + } + if m.removedtranslated_languages != nil { + edges = append(edges, supportedlocale.EdgeTranslatedLanguages) + } + return edges +} + +// RemovedIDs returns all IDs (to other nodes) that were removed for the edge with +// the given name in this mutation. +func (m *SupportedLocaleMutation) RemovedIDs(name string) []ent.Value { + switch name { + case supportedlocale.EdgeTranslatedCategories: + ids := make([]ent.Value, 0, len(m.removedtranslated_categories)) + for id := range m.removedtranslated_categories { + ids = append(ids, id) + } + return ids + case supportedlocale.EdgeTranslatedLanguages: + ids := make([]ent.Value, 0, len(m.removedtranslated_languages)) + for id := range m.removedtranslated_languages { + ids = append(ids, id) + } + return ids + } + return nil +} + +// ClearedEdges returns all edge names that were cleared in this mutation. +func (m *SupportedLocaleMutation) ClearedEdges() []string { + edges := make([]string, 0, 2) + if m.clearedtranslated_categories { + edges = append(edges, supportedlocale.EdgeTranslatedCategories) + } + if m.clearedtranslated_languages { + edges = append(edges, supportedlocale.EdgeTranslatedLanguages) + } + return edges +} + +// EdgeCleared returns a boolean which indicates if the edge with the given name +// was cleared in this mutation. +func (m *SupportedLocaleMutation) EdgeCleared(name string) bool { + switch name { + case supportedlocale.EdgeTranslatedCategories: + return m.clearedtranslated_categories + case supportedlocale.EdgeTranslatedLanguages: + return m.clearedtranslated_languages + } + 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 *SupportedLocaleMutation) ClearEdge(name string) error { + switch name { + } + return fmt.Errorf("unknown SupportedLocale 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 *SupportedLocaleMutation) ResetEdge(name string) error { + switch name { + case supportedlocale.EdgeTranslatedCategories: + m.ResetTranslatedCategories() + return nil + case supportedlocale.EdgeTranslatedLanguages: + m.ResetTranslatedLanguages() + return nil + } + return fmt.Errorf("unknown SupportedLocale edge %s", name) +} + +// UserAccountMutation represents an operation that mutates the UserAccount nodes in the graph. +type UserAccountMutation struct { + config + op Op + typ string + id *uuid.UUID + provider_enabled *bool + onboarding_completed_at *time.Time + created_at *time.Time + updated_at *time.Time + clearedFields map[string]struct{} + done bool + oldValue func(context.Context) (*UserAccount, error) + predicates []predicate.UserAccount +} + +var _ ent.Mutation = (*UserAccountMutation)(nil) + +// useraccountOption allows management of the mutation configuration using functional options. +type useraccountOption func(*UserAccountMutation) + +// newUserAccountMutation creates new mutation for the UserAccount entity. +func newUserAccountMutation(c config, op Op, opts ...useraccountOption) *UserAccountMutation { + m := &UserAccountMutation{ + config: c, + op: op, + typ: TypeUserAccount, + clearedFields: make(map[string]struct{}), + } + for _, opt := range opts { + opt(m) + } + return m +} + +// withUserAccountID sets the ID field of the mutation. +func withUserAccountID(id uuid.UUID) useraccountOption { + return func(m *UserAccountMutation) { + var ( + err error + once sync.Once + value *UserAccount + ) + m.oldValue = func(ctx context.Context) (*UserAccount, error) { + once.Do(func() { + if m.done { + err = errors.New("querying old values post mutation is not allowed") + } else { + value, err = m.Client().UserAccount.Get(ctx, id) + } + }) + return value, err + } + m.id = &id + } +} + +// withUserAccount sets the old UserAccount of the mutation. +func withUserAccount(node *UserAccount) useraccountOption { + return func(m *UserAccountMutation) { + m.oldValue = func(context.Context) (*UserAccount, 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 UserAccountMutation) 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 UserAccountMutation) 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 UserAccount entities. +func (m *UserAccountMutation) 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 *UserAccountMutation) 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 *UserAccountMutation) 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().UserAccount.Query().Where(m.predicates...).IDs(ctx) + default: + return nil, fmt.Errorf("IDs is not allowed on %s operations", m.op) + } +} + +// SetProviderEnabled sets the "provider_enabled" field. +func (m *UserAccountMutation) SetProviderEnabled(b bool) { + m.provider_enabled = &b +} + +// ProviderEnabled returns the value of the "provider_enabled" field in the mutation. +func (m *UserAccountMutation) ProviderEnabled() (r bool, exists bool) { + v := m.provider_enabled + if v == nil { + return + } + return *v, true +} + +// OldProviderEnabled returns the old "provider_enabled" field's value of the UserAccount entity. +// If the UserAccount object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *UserAccountMutation) OldProviderEnabled(ctx context.Context) (v bool, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldProviderEnabled is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldProviderEnabled requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldProviderEnabled: %w", err) + } + return oldValue.ProviderEnabled, nil +} + +// ResetProviderEnabled resets all changes to the "provider_enabled" field. +func (m *UserAccountMutation) ResetProviderEnabled() { + m.provider_enabled = nil +} + +// SetOnboardingCompletedAt sets the "onboarding_completed_at" field. +func (m *UserAccountMutation) SetOnboardingCompletedAt(t time.Time) { + m.onboarding_completed_at = &t +} + +// OnboardingCompletedAt returns the value of the "onboarding_completed_at" field in the mutation. +func (m *UserAccountMutation) OnboardingCompletedAt() (r time.Time, exists bool) { + v := m.onboarding_completed_at + if v == nil { + return + } + return *v, true +} + +// OldOnboardingCompletedAt returns the old "onboarding_completed_at" field's value of the UserAccount entity. +// If the UserAccount object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *UserAccountMutation) OldOnboardingCompletedAt(ctx context.Context) (v time.Time, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldOnboardingCompletedAt is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldOnboardingCompletedAt requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldOnboardingCompletedAt: %w", err) + } + return oldValue.OnboardingCompletedAt, nil +} + +// ResetOnboardingCompletedAt resets all changes to the "onboarding_completed_at" field. +func (m *UserAccountMutation) ResetOnboardingCompletedAt() { + m.onboarding_completed_at = nil +} + +// SetCreatedAt sets the "created_at" field. +func (m *UserAccountMutation) SetCreatedAt(t time.Time) { + m.created_at = &t +} + +// CreatedAt returns the value of the "created_at" field in the mutation. +func (m *UserAccountMutation) CreatedAt() (r time.Time, exists bool) { + v := m.created_at + if v == nil { + return + } + return *v, true +} + +// OldCreatedAt returns the old "created_at" field's value of the UserAccount entity. +// If the UserAccount object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *UserAccountMutation) OldCreatedAt(ctx context.Context) (v time.Time, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldCreatedAt is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldCreatedAt requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldCreatedAt: %w", err) + } + return oldValue.CreatedAt, nil +} + +// ResetCreatedAt resets all changes to the "created_at" field. +func (m *UserAccountMutation) ResetCreatedAt() { + m.created_at = nil +} + +// SetUpdatedAt sets the "updated_at" field. +func (m *UserAccountMutation) SetUpdatedAt(t time.Time) { + m.updated_at = &t +} + +// UpdatedAt returns the value of the "updated_at" field in the mutation. +func (m *UserAccountMutation) UpdatedAt() (r time.Time, exists bool) { + v := m.updated_at + if v == nil { + return + } + return *v, true +} + +// OldUpdatedAt returns the old "updated_at" field's value of the UserAccount entity. +// If the UserAccount object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *UserAccountMutation) OldUpdatedAt(ctx context.Context) (v time.Time, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldUpdatedAt is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldUpdatedAt requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldUpdatedAt: %w", err) + } + return oldValue.UpdatedAt, nil +} + +// ResetUpdatedAt resets all changes to the "updated_at" field. +func (m *UserAccountMutation) ResetUpdatedAt() { + m.updated_at = nil +} + +// Where appends a list predicates to the UserAccountMutation builder. +func (m *UserAccountMutation) Where(ps ...predicate.UserAccount) { + m.predicates = append(m.predicates, ps...) +} + +// WhereP appends storage-level predicates to the UserAccountMutation builder. Using this method, +// users can use type-assertion to append predicates that do not depend on any generated package. +func (m *UserAccountMutation) WhereP(ps ...func(*sql.Selector)) { + p := make([]predicate.UserAccount, len(ps)) + for i := range ps { + p[i] = ps[i] + } + m.Where(p...) +} + +// Op returns the operation name. +func (m *UserAccountMutation) Op() Op { + return m.op +} + +// SetOp allows setting the mutation operation. +func (m *UserAccountMutation) SetOp(op Op) { + m.op = op +} + +// Type returns the node type of this mutation (UserAccount). +func (m *UserAccountMutation) 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 *UserAccountMutation) Fields() []string { + fields := make([]string, 0, 4) + if m.provider_enabled != nil { + fields = append(fields, useraccount.FieldProviderEnabled) + } + if m.onboarding_completed_at != nil { + fields = append(fields, useraccount.FieldOnboardingCompletedAt) + } + if m.created_at != nil { + fields = append(fields, useraccount.FieldCreatedAt) + } + if m.updated_at != nil { + fields = append(fields, useraccount.FieldUpdatedAt) + } + 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 *UserAccountMutation) Field(name string) (ent.Value, bool) { + switch name { + case useraccount.FieldProviderEnabled: + return m.ProviderEnabled() + case useraccount.FieldOnboardingCompletedAt: + return m.OnboardingCompletedAt() + case useraccount.FieldCreatedAt: + return m.CreatedAt() + case useraccount.FieldUpdatedAt: + return m.UpdatedAt() + } + 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 *UserAccountMutation) OldField(ctx context.Context, name string) (ent.Value, error) { + switch name { + case useraccount.FieldProviderEnabled: + return m.OldProviderEnabled(ctx) + case useraccount.FieldOnboardingCompletedAt: + return m.OldOnboardingCompletedAt(ctx) + case useraccount.FieldCreatedAt: + return m.OldCreatedAt(ctx) + case useraccount.FieldUpdatedAt: + return m.OldUpdatedAt(ctx) + } + return nil, fmt.Errorf("unknown UserAccount 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 *UserAccountMutation) SetField(name string, value ent.Value) error { + switch name { + case useraccount.FieldProviderEnabled: + v, ok := value.(bool) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetProviderEnabled(v) + return nil + case useraccount.FieldOnboardingCompletedAt: + v, ok := value.(time.Time) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetOnboardingCompletedAt(v) + return nil + case useraccount.FieldCreatedAt: + v, ok := value.(time.Time) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetCreatedAt(v) + return nil + case useraccount.FieldUpdatedAt: + v, ok := value.(time.Time) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetUpdatedAt(v) + return nil + } + return fmt.Errorf("unknown UserAccount field %s", name) +} + +// AddedFields returns all numeric fields that were incremented/decremented during +// this mutation. +func (m *UserAccountMutation) 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 *UserAccountMutation) 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 *UserAccountMutation) AddField(name string, value ent.Value) error { + switch name { + } + return fmt.Errorf("unknown UserAccount numeric field %s", name) +} + +// ClearedFields returns all nullable fields that were cleared during this +// mutation. +func (m *UserAccountMutation) ClearedFields() []string { + return nil +} + +// FieldCleared returns a boolean indicating if a field with the given name was +// cleared in this mutation. +func (m *UserAccountMutation) 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 *UserAccountMutation) ClearField(name string) error { + return fmt.Errorf("unknown UserAccount 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 *UserAccountMutation) ResetField(name string) error { + switch name { + case useraccount.FieldProviderEnabled: + m.ResetProviderEnabled() + return nil + case useraccount.FieldOnboardingCompletedAt: + m.ResetOnboardingCompletedAt() + return nil + case useraccount.FieldCreatedAt: + m.ResetCreatedAt() + return nil + case useraccount.FieldUpdatedAt: + m.ResetUpdatedAt() + return nil + } + return fmt.Errorf("unknown UserAccount field %s", name) +} + +// AddedEdges returns all edge names that were set/added in this mutation. +func (m *UserAccountMutation) AddedEdges() []string { + edges := make([]string, 0, 0) + return edges +} + +// AddedIDs returns all IDs (to other nodes) that were added for the given edge +// name in this mutation. +func (m *UserAccountMutation) AddedIDs(name string) []ent.Value { + return nil +} + +// RemovedEdges returns all edge names that were removed in this mutation. +func (m *UserAccountMutation) RemovedEdges() []string { + edges := make([]string, 0, 0) + return edges +} + +// RemovedIDs returns all IDs (to other nodes) that were removed for the edge with +// the given name in this mutation. +func (m *UserAccountMutation) RemovedIDs(name string) []ent.Value { + return nil +} + +// ClearedEdges returns all edge names that were cleared in this mutation. +func (m *UserAccountMutation) ClearedEdges() []string { + edges := make([]string, 0, 0) + return edges +} + +// EdgeCleared returns a boolean which indicates if the edge with the given name +// was cleared in this mutation. +func (m *UserAccountMutation) EdgeCleared(name string) bool { + return false +} + +// ClearEdge clears the value of the edge with the given name. It returns an error +// if that edge is not defined in the schema. +func (m *UserAccountMutation) ClearEdge(name string) error { + return fmt.Errorf("unknown UserAccount 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 *UserAccountMutation) ResetEdge(name string) error { + return fmt.Errorf("unknown UserAccount edge %s", name) +} diff --git a/backend/ent/platformrole.go b/backend/ent/platformrole.go new file mode 100644 index 0000000..70fbc5a --- /dev/null +++ b/backend/ent/platformrole.go @@ -0,0 +1,129 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "fmt" + "strings" + "time" + + "entgo.io/ent" + "entgo.io/ent/dialect/sql" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/platformrole" + "github.com/google/uuid" +) + +// PlatformRole is the model entity for the PlatformRole schema. +type PlatformRole struct { + config `json:"-"` + // ID of the ent. + ID uuid.UUID `json:"id,omitempty"` + // InternalUserID holds the value of the "internal_user_id" field. + InternalUserID uuid.UUID `json:"internal_user_id,omitempty"` + // Role holds the value of the "role" field. + Role string `json:"role,omitempty"` + // GrantedAt holds the value of the "granted_at" field. + GrantedAt time.Time `json:"granted_at,omitempty"` + selectValues sql.SelectValues +} + +// scanValues returns the types for scanning values from sql.Rows. +func (*PlatformRole) scanValues(columns []string) ([]any, error) { + values := make([]any, len(columns)) + for i := range columns { + switch columns[i] { + case platformrole.FieldRole: + values[i] = new(sql.NullString) + case platformrole.FieldGrantedAt: + values[i] = new(sql.NullTime) + case platformrole.FieldID, platformrole.FieldInternalUserID: + 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 PlatformRole fields. +func (_m *PlatformRole) 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 platformrole.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 platformrole.FieldInternalUserID: + if value, ok := values[i].(*uuid.UUID); !ok { + return fmt.Errorf("unexpected type %T for field internal_user_id", values[i]) + } else if value != nil { + _m.InternalUserID = *value + } + case platformrole.FieldRole: + if value, ok := values[i].(*sql.NullString); !ok { + return fmt.Errorf("unexpected type %T for field role", values[i]) + } else if value.Valid { + _m.Role = value.String + } + case platformrole.FieldGrantedAt: + if value, ok := values[i].(*sql.NullTime); !ok { + return fmt.Errorf("unexpected type %T for field granted_at", values[i]) + } else if value.Valid { + _m.GrantedAt = value.Time + } + default: + _m.selectValues.Set(columns[i], values[i]) + } + } + return nil +} + +// Value returns the ent.Value that was dynamically selected and assigned to the PlatformRole. +// This includes values selected through modifiers, order, etc. +func (_m *PlatformRole) Value(name string) (ent.Value, error) { + return _m.selectValues.Get(name) +} + +// Update returns a builder for updating this PlatformRole. +// Note that you need to call PlatformRole.Unwrap() before calling this method if this PlatformRole +// was returned from a transaction, and the transaction was committed or rolled back. +func (_m *PlatformRole) Update() *PlatformRoleUpdateOne { + return NewPlatformRoleClient(_m.config).UpdateOne(_m) +} + +// Unwrap unwraps the PlatformRole 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 *PlatformRole) Unwrap() *PlatformRole { + _tx, ok := _m.config.driver.(*txDriver) + if !ok { + panic("ent: PlatformRole is not a transactional entity") + } + _m.config.driver = _tx.drv + return _m +} + +// String implements the fmt.Stringer. +func (_m *PlatformRole) String() string { + var builder strings.Builder + builder.WriteString("PlatformRole(") + builder.WriteString(fmt.Sprintf("id=%v, ", _m.ID)) + builder.WriteString("internal_user_id=") + builder.WriteString(fmt.Sprintf("%v", _m.InternalUserID)) + builder.WriteString(", ") + builder.WriteString("role=") + builder.WriteString(_m.Role) + builder.WriteString(", ") + builder.WriteString("granted_at=") + builder.WriteString(_m.GrantedAt.Format(time.ANSIC)) + builder.WriteByte(')') + return builder.String() +} + +// PlatformRoles is a parsable slice of PlatformRole. +type PlatformRoles []*PlatformRole diff --git a/backend/ent/platformrole/platformrole.go b/backend/ent/platformrole/platformrole.go new file mode 100644 index 0000000..886b14f --- /dev/null +++ b/backend/ent/platformrole/platformrole.go @@ -0,0 +1,75 @@ +// Code generated by ent, DO NOT EDIT. + +package platformrole + +import ( + "time" + + "entgo.io/ent/dialect/sql" + "github.com/google/uuid" +) + +const ( + // Label holds the string label denoting the platformrole type in the database. + Label = "platform_role" + // FieldID holds the string denoting the id field in the database. + FieldID = "id" + // FieldInternalUserID holds the string denoting the internal_user_id field in the database. + FieldInternalUserID = "internal_user_id" + // FieldRole holds the string denoting the role field in the database. + FieldRole = "role" + // FieldGrantedAt holds the string denoting the granted_at field in the database. + FieldGrantedAt = "granted_at" + // Table holds the table name of the platformrole in the database. + Table = "platform_roles" +) + +// Columns holds all SQL columns for platformrole fields. +var Columns = []string{ + FieldID, + FieldInternalUserID, + FieldRole, + FieldGrantedAt, +} + +// 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 ( + // RoleValidator is a validator for the "role" field. It is called by the builders before save. + RoleValidator func(string) error + // DefaultGrantedAt holds the default value on creation for the "granted_at" field. + DefaultGrantedAt func() time.Time + // DefaultID holds the default value on creation for the "id" field. + DefaultID func() uuid.UUID +) + +// OrderOption defines the ordering options for the PlatformRole 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() +} + +// ByInternalUserID orders the results by the internal_user_id field. +func ByInternalUserID(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldInternalUserID, opts...).ToFunc() +} + +// ByRole orders the results by the role field. +func ByRole(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldRole, opts...).ToFunc() +} + +// ByGrantedAt orders the results by the granted_at field. +func ByGrantedAt(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldGrantedAt, opts...).ToFunc() +} diff --git a/backend/ent/platformrole/where.go b/backend/ent/platformrole/where.go new file mode 100644 index 0000000..9b1bfc1 --- /dev/null +++ b/backend/ent/platformrole/where.go @@ -0,0 +1,231 @@ +// Code generated by ent, DO NOT EDIT. + +package platformrole + +import ( + "time" + + "entgo.io/ent/dialect/sql" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/predicate" + "github.com/google/uuid" +) + +// ID filters vertices based on their ID field. +func ID(id uuid.UUID) predicate.PlatformRole { + return predicate.PlatformRole(sql.FieldEQ(FieldID, id)) +} + +// IDEQ applies the EQ predicate on the ID field. +func IDEQ(id uuid.UUID) predicate.PlatformRole { + return predicate.PlatformRole(sql.FieldEQ(FieldID, id)) +} + +// IDNEQ applies the NEQ predicate on the ID field. +func IDNEQ(id uuid.UUID) predicate.PlatformRole { + return predicate.PlatformRole(sql.FieldNEQ(FieldID, id)) +} + +// IDIn applies the In predicate on the ID field. +func IDIn(ids ...uuid.UUID) predicate.PlatformRole { + return predicate.PlatformRole(sql.FieldIn(FieldID, ids...)) +} + +// IDNotIn applies the NotIn predicate on the ID field. +func IDNotIn(ids ...uuid.UUID) predicate.PlatformRole { + return predicate.PlatformRole(sql.FieldNotIn(FieldID, ids...)) +} + +// IDGT applies the GT predicate on the ID field. +func IDGT(id uuid.UUID) predicate.PlatformRole { + return predicate.PlatformRole(sql.FieldGT(FieldID, id)) +} + +// IDGTE applies the GTE predicate on the ID field. +func IDGTE(id uuid.UUID) predicate.PlatformRole { + return predicate.PlatformRole(sql.FieldGTE(FieldID, id)) +} + +// IDLT applies the LT predicate on the ID field. +func IDLT(id uuid.UUID) predicate.PlatformRole { + return predicate.PlatformRole(sql.FieldLT(FieldID, id)) +} + +// IDLTE applies the LTE predicate on the ID field. +func IDLTE(id uuid.UUID) predicate.PlatformRole { + return predicate.PlatformRole(sql.FieldLTE(FieldID, id)) +} + +// InternalUserID applies equality check predicate on the "internal_user_id" field. It's identical to InternalUserIDEQ. +func InternalUserID(v uuid.UUID) predicate.PlatformRole { + return predicate.PlatformRole(sql.FieldEQ(FieldInternalUserID, v)) +} + +// Role applies equality check predicate on the "role" field. It's identical to RoleEQ. +func Role(v string) predicate.PlatformRole { + return predicate.PlatformRole(sql.FieldEQ(FieldRole, v)) +} + +// GrantedAt applies equality check predicate on the "granted_at" field. It's identical to GrantedAtEQ. +func GrantedAt(v time.Time) predicate.PlatformRole { + return predicate.PlatformRole(sql.FieldEQ(FieldGrantedAt, v)) +} + +// InternalUserIDEQ applies the EQ predicate on the "internal_user_id" field. +func InternalUserIDEQ(v uuid.UUID) predicate.PlatformRole { + return predicate.PlatformRole(sql.FieldEQ(FieldInternalUserID, v)) +} + +// InternalUserIDNEQ applies the NEQ predicate on the "internal_user_id" field. +func InternalUserIDNEQ(v uuid.UUID) predicate.PlatformRole { + return predicate.PlatformRole(sql.FieldNEQ(FieldInternalUserID, v)) +} + +// InternalUserIDIn applies the In predicate on the "internal_user_id" field. +func InternalUserIDIn(vs ...uuid.UUID) predicate.PlatformRole { + return predicate.PlatformRole(sql.FieldIn(FieldInternalUserID, vs...)) +} + +// InternalUserIDNotIn applies the NotIn predicate on the "internal_user_id" field. +func InternalUserIDNotIn(vs ...uuid.UUID) predicate.PlatformRole { + return predicate.PlatformRole(sql.FieldNotIn(FieldInternalUserID, vs...)) +} + +// InternalUserIDGT applies the GT predicate on the "internal_user_id" field. +func InternalUserIDGT(v uuid.UUID) predicate.PlatformRole { + return predicate.PlatformRole(sql.FieldGT(FieldInternalUserID, v)) +} + +// InternalUserIDGTE applies the GTE predicate on the "internal_user_id" field. +func InternalUserIDGTE(v uuid.UUID) predicate.PlatformRole { + return predicate.PlatformRole(sql.FieldGTE(FieldInternalUserID, v)) +} + +// InternalUserIDLT applies the LT predicate on the "internal_user_id" field. +func InternalUserIDLT(v uuid.UUID) predicate.PlatformRole { + return predicate.PlatformRole(sql.FieldLT(FieldInternalUserID, v)) +} + +// InternalUserIDLTE applies the LTE predicate on the "internal_user_id" field. +func InternalUserIDLTE(v uuid.UUID) predicate.PlatformRole { + return predicate.PlatformRole(sql.FieldLTE(FieldInternalUserID, v)) +} + +// RoleEQ applies the EQ predicate on the "role" field. +func RoleEQ(v string) predicate.PlatformRole { + return predicate.PlatformRole(sql.FieldEQ(FieldRole, v)) +} + +// RoleNEQ applies the NEQ predicate on the "role" field. +func RoleNEQ(v string) predicate.PlatformRole { + return predicate.PlatformRole(sql.FieldNEQ(FieldRole, v)) +} + +// RoleIn applies the In predicate on the "role" field. +func RoleIn(vs ...string) predicate.PlatformRole { + return predicate.PlatformRole(sql.FieldIn(FieldRole, vs...)) +} + +// RoleNotIn applies the NotIn predicate on the "role" field. +func RoleNotIn(vs ...string) predicate.PlatformRole { + return predicate.PlatformRole(sql.FieldNotIn(FieldRole, vs...)) +} + +// RoleGT applies the GT predicate on the "role" field. +func RoleGT(v string) predicate.PlatformRole { + return predicate.PlatformRole(sql.FieldGT(FieldRole, v)) +} + +// RoleGTE applies the GTE predicate on the "role" field. +func RoleGTE(v string) predicate.PlatformRole { + return predicate.PlatformRole(sql.FieldGTE(FieldRole, v)) +} + +// RoleLT applies the LT predicate on the "role" field. +func RoleLT(v string) predicate.PlatformRole { + return predicate.PlatformRole(sql.FieldLT(FieldRole, v)) +} + +// RoleLTE applies the LTE predicate on the "role" field. +func RoleLTE(v string) predicate.PlatformRole { + return predicate.PlatformRole(sql.FieldLTE(FieldRole, v)) +} + +// RoleContains applies the Contains predicate on the "role" field. +func RoleContains(v string) predicate.PlatformRole { + return predicate.PlatformRole(sql.FieldContains(FieldRole, v)) +} + +// RoleHasPrefix applies the HasPrefix predicate on the "role" field. +func RoleHasPrefix(v string) predicate.PlatformRole { + return predicate.PlatformRole(sql.FieldHasPrefix(FieldRole, v)) +} + +// RoleHasSuffix applies the HasSuffix predicate on the "role" field. +func RoleHasSuffix(v string) predicate.PlatformRole { + return predicate.PlatformRole(sql.FieldHasSuffix(FieldRole, v)) +} + +// RoleEqualFold applies the EqualFold predicate on the "role" field. +func RoleEqualFold(v string) predicate.PlatformRole { + return predicate.PlatformRole(sql.FieldEqualFold(FieldRole, v)) +} + +// RoleContainsFold applies the ContainsFold predicate on the "role" field. +func RoleContainsFold(v string) predicate.PlatformRole { + return predicate.PlatformRole(sql.FieldContainsFold(FieldRole, v)) +} + +// GrantedAtEQ applies the EQ predicate on the "granted_at" field. +func GrantedAtEQ(v time.Time) predicate.PlatformRole { + return predicate.PlatformRole(sql.FieldEQ(FieldGrantedAt, v)) +} + +// GrantedAtNEQ applies the NEQ predicate on the "granted_at" field. +func GrantedAtNEQ(v time.Time) predicate.PlatformRole { + return predicate.PlatformRole(sql.FieldNEQ(FieldGrantedAt, v)) +} + +// GrantedAtIn applies the In predicate on the "granted_at" field. +func GrantedAtIn(vs ...time.Time) predicate.PlatformRole { + return predicate.PlatformRole(sql.FieldIn(FieldGrantedAt, vs...)) +} + +// GrantedAtNotIn applies the NotIn predicate on the "granted_at" field. +func GrantedAtNotIn(vs ...time.Time) predicate.PlatformRole { + return predicate.PlatformRole(sql.FieldNotIn(FieldGrantedAt, vs...)) +} + +// GrantedAtGT applies the GT predicate on the "granted_at" field. +func GrantedAtGT(v time.Time) predicate.PlatformRole { + return predicate.PlatformRole(sql.FieldGT(FieldGrantedAt, v)) +} + +// GrantedAtGTE applies the GTE predicate on the "granted_at" field. +func GrantedAtGTE(v time.Time) predicate.PlatformRole { + return predicate.PlatformRole(sql.FieldGTE(FieldGrantedAt, v)) +} + +// GrantedAtLT applies the LT predicate on the "granted_at" field. +func GrantedAtLT(v time.Time) predicate.PlatformRole { + return predicate.PlatformRole(sql.FieldLT(FieldGrantedAt, v)) +} + +// GrantedAtLTE applies the LTE predicate on the "granted_at" field. +func GrantedAtLTE(v time.Time) predicate.PlatformRole { + return predicate.PlatformRole(sql.FieldLTE(FieldGrantedAt, v)) +} + +// And groups predicates with the AND operator between them. +func And(predicates ...predicate.PlatformRole) predicate.PlatformRole { + return predicate.PlatformRole(sql.AndPredicates(predicates...)) +} + +// Or groups predicates with the OR operator between them. +func Or(predicates ...predicate.PlatformRole) predicate.PlatformRole { + return predicate.PlatformRole(sql.OrPredicates(predicates...)) +} + +// Not applies the not operator on the given predicate. +func Not(p predicate.PlatformRole) predicate.PlatformRole { + return predicate.PlatformRole(sql.NotPredicates(p)) +} diff --git a/backend/ent/platformrole_create.go b/backend/ent/platformrole_create.go new file mode 100644 index 0000000..a6dfbfa --- /dev/null +++ b/backend/ent/platformrole_create.go @@ -0,0 +1,257 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "context" + "errors" + "fmt" + "time" + + "entgo.io/ent/dialect/sql/sqlgraph" + "entgo.io/ent/schema/field" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/platformrole" + "github.com/google/uuid" +) + +// PlatformRoleCreate is the builder for creating a PlatformRole entity. +type PlatformRoleCreate struct { + config + mutation *PlatformRoleMutation + hooks []Hook +} + +// SetInternalUserID sets the "internal_user_id" field. +func (_c *PlatformRoleCreate) SetInternalUserID(v uuid.UUID) *PlatformRoleCreate { + _c.mutation.SetInternalUserID(v) + return _c +} + +// SetRole sets the "role" field. +func (_c *PlatformRoleCreate) SetRole(v string) *PlatformRoleCreate { + _c.mutation.SetRole(v) + return _c +} + +// SetGrantedAt sets the "granted_at" field. +func (_c *PlatformRoleCreate) SetGrantedAt(v time.Time) *PlatformRoleCreate { + _c.mutation.SetGrantedAt(v) + return _c +} + +// SetNillableGrantedAt sets the "granted_at" field if the given value is not nil. +func (_c *PlatformRoleCreate) SetNillableGrantedAt(v *time.Time) *PlatformRoleCreate { + if v != nil { + _c.SetGrantedAt(*v) + } + return _c +} + +// SetID sets the "id" field. +func (_c *PlatformRoleCreate) SetID(v uuid.UUID) *PlatformRoleCreate { + _c.mutation.SetID(v) + return _c +} + +// SetNillableID sets the "id" field if the given value is not nil. +func (_c *PlatformRoleCreate) SetNillableID(v *uuid.UUID) *PlatformRoleCreate { + if v != nil { + _c.SetID(*v) + } + return _c +} + +// Mutation returns the PlatformRoleMutation object of the builder. +func (_c *PlatformRoleCreate) Mutation() *PlatformRoleMutation { + return _c.mutation +} + +// Save creates the PlatformRole in the database. +func (_c *PlatformRoleCreate) Save(ctx context.Context) (*PlatformRole, error) { + _c.defaults() + return withHooks(ctx, _c.sqlSave, _c.mutation, _c.hooks) +} + +// SaveX calls Save and panics if Save returns an error. +func (_c *PlatformRoleCreate) SaveX(ctx context.Context) *PlatformRole { + v, err := _c.Save(ctx) + if err != nil { + panic(err) + } + return v +} + +// Exec executes the query. +func (_c *PlatformRoleCreate) Exec(ctx context.Context) error { + _, err := _c.Save(ctx) + return err +} + +// ExecX is like Exec, but panics if an error occurs. +func (_c *PlatformRoleCreate) 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 *PlatformRoleCreate) defaults() { + if _, ok := _c.mutation.GrantedAt(); !ok { + v := platformrole.DefaultGrantedAt() + _c.mutation.SetGrantedAt(v) + } + if _, ok := _c.mutation.ID(); !ok { + v := platformrole.DefaultID() + _c.mutation.SetID(v) + } +} + +// check runs all checks and user-defined validators on the builder. +func (_c *PlatformRoleCreate) check() error { + if _, ok := _c.mutation.InternalUserID(); !ok { + return &ValidationError{Name: "internal_user_id", err: errors.New(`ent: missing required field "PlatformRole.internal_user_id"`)} + } + if _, ok := _c.mutation.Role(); !ok { + return &ValidationError{Name: "role", err: errors.New(`ent: missing required field "PlatformRole.role"`)} + } + if v, ok := _c.mutation.Role(); ok { + if err := platformrole.RoleValidator(v); err != nil { + return &ValidationError{Name: "role", err: fmt.Errorf(`ent: validator failed for field "PlatformRole.role": %w`, err)} + } + } + if _, ok := _c.mutation.GrantedAt(); !ok { + return &ValidationError{Name: "granted_at", err: errors.New(`ent: missing required field "PlatformRole.granted_at"`)} + } + return nil +} + +func (_c *PlatformRoleCreate) sqlSave(ctx context.Context) (*PlatformRole, 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 *PlatformRoleCreate) createSpec() (*PlatformRole, *sqlgraph.CreateSpec) { + var ( + _node = &PlatformRole{config: _c.config} + _spec = sqlgraph.NewCreateSpec(platformrole.Table, sqlgraph.NewFieldSpec(platformrole.FieldID, field.TypeUUID)) + ) + if id, ok := _c.mutation.ID(); ok { + _node.ID = id + _spec.ID.Value = &id + } + if value, ok := _c.mutation.InternalUserID(); ok { + _spec.SetField(platformrole.FieldInternalUserID, field.TypeUUID, value) + _node.InternalUserID = value + } + if value, ok := _c.mutation.Role(); ok { + _spec.SetField(platformrole.FieldRole, field.TypeString, value) + _node.Role = value + } + if value, ok := _c.mutation.GrantedAt(); ok { + _spec.SetField(platformrole.FieldGrantedAt, field.TypeTime, value) + _node.GrantedAt = value + } + return _node, _spec +} + +// PlatformRoleCreateBulk is the builder for creating many PlatformRole entities in bulk. +type PlatformRoleCreateBulk struct { + config + err error + builders []*PlatformRoleCreate +} + +// Save creates the PlatformRole entities in the database. +func (_c *PlatformRoleCreateBulk) Save(ctx context.Context) ([]*PlatformRole, error) { + if _c.err != nil { + return nil, _c.err + } + specs := make([]*sqlgraph.CreateSpec, len(_c.builders)) + nodes := make([]*PlatformRole, 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.(*PlatformRoleMutation) + 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 *PlatformRoleCreateBulk) SaveX(ctx context.Context) []*PlatformRole { + v, err := _c.Save(ctx) + if err != nil { + panic(err) + } + return v +} + +// Exec executes the query. +func (_c *PlatformRoleCreateBulk) Exec(ctx context.Context) error { + _, err := _c.Save(ctx) + return err +} + +// ExecX is like Exec, but panics if an error occurs. +func (_c *PlatformRoleCreateBulk) ExecX(ctx context.Context) { + if err := _c.Exec(ctx); err != nil { + panic(err) + } +} diff --git a/backend/ent/platformrole_delete.go b/backend/ent/platformrole_delete.go new file mode 100644 index 0000000..5e75995 --- /dev/null +++ b/backend/ent/platformrole_delete.go @@ -0,0 +1,88 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "context" + + "entgo.io/ent/dialect/sql" + "entgo.io/ent/dialect/sql/sqlgraph" + "entgo.io/ent/schema/field" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/platformrole" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/predicate" +) + +// PlatformRoleDelete is the builder for deleting a PlatformRole entity. +type PlatformRoleDelete struct { + config + hooks []Hook + mutation *PlatformRoleMutation +} + +// Where appends a list predicates to the PlatformRoleDelete builder. +func (_d *PlatformRoleDelete) Where(ps ...predicate.PlatformRole) *PlatformRoleDelete { + _d.mutation.Where(ps...) + return _d +} + +// Exec executes the deletion query and returns how many vertices were deleted. +func (_d *PlatformRoleDelete) 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 *PlatformRoleDelete) ExecX(ctx context.Context) int { + n, err := _d.Exec(ctx) + if err != nil { + panic(err) + } + return n +} + +func (_d *PlatformRoleDelete) sqlExec(ctx context.Context) (int, error) { + _spec := sqlgraph.NewDeleteSpec(platformrole.Table, sqlgraph.NewFieldSpec(platformrole.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 +} + +// PlatformRoleDeleteOne is the builder for deleting a single PlatformRole entity. +type PlatformRoleDeleteOne struct { + _d *PlatformRoleDelete +} + +// Where appends a list predicates to the PlatformRoleDelete builder. +func (_d *PlatformRoleDeleteOne) Where(ps ...predicate.PlatformRole) *PlatformRoleDeleteOne { + _d._d.mutation.Where(ps...) + return _d +} + +// Exec executes the deletion query. +func (_d *PlatformRoleDeleteOne) Exec(ctx context.Context) error { + n, err := _d._d.Exec(ctx) + switch { + case err != nil: + return err + case n == 0: + return &NotFoundError{platformrole.Label} + default: + return nil + } +} + +// ExecX is like Exec, but panics if an error occurs. +func (_d *PlatformRoleDeleteOne) ExecX(ctx context.Context) { + if err := _d.Exec(ctx); err != nil { + panic(err) + } +} diff --git a/backend/ent/platformrole_query.go b/backend/ent/platformrole_query.go new file mode 100644 index 0000000..93d8458 --- /dev/null +++ b/backend/ent/platformrole_query.go @@ -0,0 +1,528 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "context" + "fmt" + "math" + + "entgo.io/ent" + "entgo.io/ent/dialect/sql" + "entgo.io/ent/dialect/sql/sqlgraph" + "entgo.io/ent/schema/field" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/platformrole" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/predicate" + "github.com/google/uuid" +) + +// PlatformRoleQuery is the builder for querying PlatformRole entities. +type PlatformRoleQuery struct { + config + ctx *QueryContext + order []platformrole.OrderOption + inters []Interceptor + predicates []predicate.PlatformRole + // intermediate query (i.e. traversal path). + sql *sql.Selector + path func(context.Context) (*sql.Selector, error) +} + +// Where adds a new predicate for the PlatformRoleQuery builder. +func (_q *PlatformRoleQuery) Where(ps ...predicate.PlatformRole) *PlatformRoleQuery { + _q.predicates = append(_q.predicates, ps...) + return _q +} + +// Limit the number of records to be returned by this query. +func (_q *PlatformRoleQuery) Limit(limit int) *PlatformRoleQuery { + _q.ctx.Limit = &limit + return _q +} + +// Offset to start from. +func (_q *PlatformRoleQuery) Offset(offset int) *PlatformRoleQuery { + _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 *PlatformRoleQuery) Unique(unique bool) *PlatformRoleQuery { + _q.ctx.Unique = &unique + return _q +} + +// Order specifies how the records should be ordered. +func (_q *PlatformRoleQuery) Order(o ...platformrole.OrderOption) *PlatformRoleQuery { + _q.order = append(_q.order, o...) + return _q +} + +// First returns the first PlatformRole entity from the query. +// Returns a *NotFoundError when no PlatformRole was found. +func (_q *PlatformRoleQuery) First(ctx context.Context) (*PlatformRole, 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{platformrole.Label} + } + return nodes[0], nil +} + +// FirstX is like First, but panics if an error occurs. +func (_q *PlatformRoleQuery) FirstX(ctx context.Context) *PlatformRole { + node, err := _q.First(ctx) + if err != nil && !IsNotFound(err) { + panic(err) + } + return node +} + +// FirstID returns the first PlatformRole ID from the query. +// Returns a *NotFoundError when no PlatformRole ID was found. +func (_q *PlatformRoleQuery) 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{platformrole.Label} + return + } + return ids[0], nil +} + +// FirstIDX is like FirstID, but panics if an error occurs. +func (_q *PlatformRoleQuery) FirstIDX(ctx context.Context) uuid.UUID { + id, err := _q.FirstID(ctx) + if err != nil && !IsNotFound(err) { + panic(err) + } + return id +} + +// Only returns a single PlatformRole entity found by the query, ensuring it only returns one. +// Returns a *NotSingularError when more than one PlatformRole entity is found. +// Returns a *NotFoundError when no PlatformRole entities are found. +func (_q *PlatformRoleQuery) Only(ctx context.Context) (*PlatformRole, 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{platformrole.Label} + default: + return nil, &NotSingularError{platformrole.Label} + } +} + +// OnlyX is like Only, but panics if an error occurs. +func (_q *PlatformRoleQuery) OnlyX(ctx context.Context) *PlatformRole { + node, err := _q.Only(ctx) + if err != nil { + panic(err) + } + return node +} + +// OnlyID is like Only, but returns the only PlatformRole ID in the query. +// Returns a *NotSingularError when more than one PlatformRole ID is found. +// Returns a *NotFoundError when no entities are found. +func (_q *PlatformRoleQuery) 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{platformrole.Label} + default: + err = &NotSingularError{platformrole.Label} + } + return +} + +// OnlyIDX is like OnlyID, but panics if an error occurs. +func (_q *PlatformRoleQuery) 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 PlatformRoles. +func (_q *PlatformRoleQuery) All(ctx context.Context) ([]*PlatformRole, error) { + ctx = setContextOp(ctx, _q.ctx, ent.OpQueryAll) + if err := _q.prepareQuery(ctx); err != nil { + return nil, err + } + qr := querierAll[[]*PlatformRole, *PlatformRoleQuery]() + return withInterceptors[[]*PlatformRole](ctx, _q, qr, _q.inters) +} + +// AllX is like All, but panics if an error occurs. +func (_q *PlatformRoleQuery) AllX(ctx context.Context) []*PlatformRole { + nodes, err := _q.All(ctx) + if err != nil { + panic(err) + } + return nodes +} + +// IDs executes the query and returns a list of PlatformRole IDs. +func (_q *PlatformRoleQuery) 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(platformrole.FieldID).Scan(ctx, &ids); err != nil { + return nil, err + } + return ids, nil +} + +// IDsX is like IDs, but panics if an error occurs. +func (_q *PlatformRoleQuery) 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 *PlatformRoleQuery) 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[*PlatformRoleQuery](), _q.inters) +} + +// CountX is like Count, but panics if an error occurs. +func (_q *PlatformRoleQuery) 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 *PlatformRoleQuery) 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 *PlatformRoleQuery) ExistX(ctx context.Context) bool { + exist, err := _q.Exist(ctx) + if err != nil { + panic(err) + } + return exist +} + +// Clone returns a duplicate of the PlatformRoleQuery 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 *PlatformRoleQuery) Clone() *PlatformRoleQuery { + if _q == nil { + return nil + } + return &PlatformRoleQuery{ + config: _q.config, + ctx: _q.ctx.Clone(), + order: append([]platformrole.OrderOption{}, _q.order...), + inters: append([]Interceptor{}, _q.inters...), + predicates: append([]predicate.PlatformRole{}, _q.predicates...), + // clone intermediate query. + sql: _q.sql.Clone(), + path: _q.path, + } +} + +// GroupBy is used to group vertices by one or more fields/columns. +// It is often used with aggregate functions, like: count, max, mean, min, sum. +// +// Example: +// +// var v []struct { +// InternalUserID uuid.UUID `json:"internal_user_id,omitempty"` +// Count int `json:"count,omitempty"` +// } +// +// client.PlatformRole.Query(). +// GroupBy(platformrole.FieldInternalUserID). +// Aggregate(ent.Count()). +// Scan(ctx, &v) +func (_q *PlatformRoleQuery) GroupBy(field string, fields ...string) *PlatformRoleGroupBy { + _q.ctx.Fields = append([]string{field}, fields...) + grbuild := &PlatformRoleGroupBy{build: _q} + grbuild.flds = &_q.ctx.Fields + grbuild.label = platformrole.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 { +// InternalUserID uuid.UUID `json:"internal_user_id,omitempty"` +// } +// +// client.PlatformRole.Query(). +// Select(platformrole.FieldInternalUserID). +// Scan(ctx, &v) +func (_q *PlatformRoleQuery) Select(fields ...string) *PlatformRoleSelect { + _q.ctx.Fields = append(_q.ctx.Fields, fields...) + sbuild := &PlatformRoleSelect{PlatformRoleQuery: _q} + sbuild.label = platformrole.Label + sbuild.flds, sbuild.scan = &_q.ctx.Fields, sbuild.Scan + return sbuild +} + +// Aggregate returns a PlatformRoleSelect configured with the given aggregations. +func (_q *PlatformRoleQuery) Aggregate(fns ...AggregateFunc) *PlatformRoleSelect { + return _q.Select().Aggregate(fns...) +} + +func (_q *PlatformRoleQuery) 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 !platformrole.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 *PlatformRoleQuery) sqlAll(ctx context.Context, hooks ...queryHook) ([]*PlatformRole, error) { + var ( + nodes = []*PlatformRole{} + _spec = _q.querySpec() + ) + _spec.ScanValues = func(columns []string) ([]any, error) { + return (*PlatformRole).scanValues(nil, columns) + } + _spec.Assign = func(columns []string, values []any) error { + node := &PlatformRole{config: _q.config} + nodes = append(nodes, node) + return node.assignValues(columns, values) + } + for i := range hooks { + hooks[i](ctx, _spec) + } + if err := sqlgraph.QueryNodes(ctx, _q.driver, _spec); err != nil { + return nil, err + } + if len(nodes) == 0 { + return nodes, nil + } + return nodes, nil +} + +func (_q *PlatformRoleQuery) sqlCount(ctx context.Context) (int, error) { + _spec := _q.querySpec() + _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 *PlatformRoleQuery) querySpec() *sqlgraph.QuerySpec { + _spec := sqlgraph.NewQuerySpec(platformrole.Table, platformrole.Columns, sqlgraph.NewFieldSpec(platformrole.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, platformrole.FieldID) + for i := range fields { + if fields[i] != platformrole.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 *PlatformRoleQuery) sqlQuery(ctx context.Context) *sql.Selector { + builder := sql.Dialect(_q.driver.Dialect()) + t1 := builder.Table(platformrole.Table) + columns := _q.ctx.Fields + if len(columns) == 0 { + columns = platformrole.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 _, 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 +} + +// PlatformRoleGroupBy is the group-by builder for PlatformRole entities. +type PlatformRoleGroupBy struct { + selector + build *PlatformRoleQuery +} + +// Aggregate adds the given aggregation functions to the group-by query. +func (_g *PlatformRoleGroupBy) Aggregate(fns ...AggregateFunc) *PlatformRoleGroupBy { + _g.fns = append(_g.fns, fns...) + return _g +} + +// Scan applies the selector query and scans the result into the given value. +func (_g *PlatformRoleGroupBy) 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[*PlatformRoleQuery, *PlatformRoleGroupBy](ctx, _g.build, _g, _g.build.inters, v) +} + +func (_g *PlatformRoleGroupBy) sqlScan(ctx context.Context, root *PlatformRoleQuery, 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) +} + +// PlatformRoleSelect is the builder for selecting fields of PlatformRole entities. +type PlatformRoleSelect struct { + *PlatformRoleQuery + selector +} + +// Aggregate adds the given aggregation functions to the selector query. +func (_s *PlatformRoleSelect) Aggregate(fns ...AggregateFunc) *PlatformRoleSelect { + _s.fns = append(_s.fns, fns...) + return _s +} + +// Scan applies the selector query and scans the result into the given value. +func (_s *PlatformRoleSelect) 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[*PlatformRoleQuery, *PlatformRoleSelect](ctx, _s.PlatformRoleQuery, _s, _s.inters, v) +} + +func (_s *PlatformRoleSelect) sqlScan(ctx context.Context, root *PlatformRoleQuery, 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/backend/ent/platformrole_update.go b/backend/ent/platformrole_update.go new file mode 100644 index 0000000..bd5f411 --- /dev/null +++ b/backend/ent/platformrole_update.go @@ -0,0 +1,175 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "context" + "errors" + "fmt" + + "entgo.io/ent/dialect/sql" + "entgo.io/ent/dialect/sql/sqlgraph" + "entgo.io/ent/schema/field" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/platformrole" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/predicate" +) + +// PlatformRoleUpdate is the builder for updating PlatformRole entities. +type PlatformRoleUpdate struct { + config + hooks []Hook + mutation *PlatformRoleMutation +} + +// Where appends a list predicates to the PlatformRoleUpdate builder. +func (_u *PlatformRoleUpdate) Where(ps ...predicate.PlatformRole) *PlatformRoleUpdate { + _u.mutation.Where(ps...) + return _u +} + +// Mutation returns the PlatformRoleMutation object of the builder. +func (_u *PlatformRoleUpdate) Mutation() *PlatformRoleMutation { + return _u.mutation +} + +// Save executes the query and returns the number of nodes affected by the update operation. +func (_u *PlatformRoleUpdate) 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 *PlatformRoleUpdate) SaveX(ctx context.Context) int { + affected, err := _u.Save(ctx) + if err != nil { + panic(err) + } + return affected +} + +// Exec executes the query. +func (_u *PlatformRoleUpdate) Exec(ctx context.Context) error { + _, err := _u.Save(ctx) + return err +} + +// ExecX is like Exec, but panics if an error occurs. +func (_u *PlatformRoleUpdate) ExecX(ctx context.Context) { + if err := _u.Exec(ctx); err != nil { + panic(err) + } +} + +func (_u *PlatformRoleUpdate) sqlSave(ctx context.Context) (_node int, err error) { + _spec := sqlgraph.NewUpdateSpec(platformrole.Table, platformrole.Columns, sqlgraph.NewFieldSpec(platformrole.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 _node, err = sqlgraph.UpdateNodes(ctx, _u.driver, _spec); err != nil { + if _, ok := err.(*sqlgraph.NotFoundError); ok { + err = &NotFoundError{platformrole.Label} + } else if sqlgraph.IsConstraintError(err) { + err = &ConstraintError{msg: err.Error(), wrap: err} + } + return 0, err + } + _u.mutation.done = true + return _node, nil +} + +// PlatformRoleUpdateOne is the builder for updating a single PlatformRole entity. +type PlatformRoleUpdateOne struct { + config + fields []string + hooks []Hook + mutation *PlatformRoleMutation +} + +// Mutation returns the PlatformRoleMutation object of the builder. +func (_u *PlatformRoleUpdateOne) Mutation() *PlatformRoleMutation { + return _u.mutation +} + +// Where appends a list predicates to the PlatformRoleUpdate builder. +func (_u *PlatformRoleUpdateOne) Where(ps ...predicate.PlatformRole) *PlatformRoleUpdateOne { + _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 *PlatformRoleUpdateOne) Select(field string, fields ...string) *PlatformRoleUpdateOne { + _u.fields = append([]string{field}, fields...) + return _u +} + +// Save executes the query and returns the updated PlatformRole entity. +func (_u *PlatformRoleUpdateOne) Save(ctx context.Context) (*PlatformRole, error) { + return withHooks(ctx, _u.sqlSave, _u.mutation, _u.hooks) +} + +// SaveX is like Save, but panics if an error occurs. +func (_u *PlatformRoleUpdateOne) SaveX(ctx context.Context) *PlatformRole { + node, err := _u.Save(ctx) + if err != nil { + panic(err) + } + return node +} + +// Exec executes the query on the entity. +func (_u *PlatformRoleUpdateOne) Exec(ctx context.Context) error { + _, err := _u.Save(ctx) + return err +} + +// ExecX is like Exec, but panics if an error occurs. +func (_u *PlatformRoleUpdateOne) ExecX(ctx context.Context) { + if err := _u.Exec(ctx); err != nil { + panic(err) + } +} + +func (_u *PlatformRoleUpdateOne) sqlSave(ctx context.Context) (_node *PlatformRole, err error) { + _spec := sqlgraph.NewUpdateSpec(platformrole.Table, platformrole.Columns, sqlgraph.NewFieldSpec(platformrole.FieldID, field.TypeUUID)) + id, ok := _u.mutation.ID() + if !ok { + return nil, &ValidationError{Name: "id", err: errors.New(`ent: missing "PlatformRole.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, platformrole.FieldID) + for _, f := range fields { + if !platformrole.ValidColumn(f) { + return nil, &ValidationError{Name: f, err: fmt.Errorf("ent: invalid field %q for query", f)} + } + if f != platformrole.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) + } + } + } + _node = &PlatformRole{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{platformrole.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/backend/ent/predicate/predicate.go b/backend/ent/predicate/predicate.go new file mode 100644 index 0000000..cd7c794 --- /dev/null +++ b/backend/ent/predicate/predicate.go @@ -0,0 +1,64 @@ +// Code generated by ent, DO NOT EDIT. + +package predicate + +import ( + "entgo.io/ent/dialect/sql" +) + +// AdministrativeArea is the predicate function for administrativearea builders. +type AdministrativeArea func(*sql.Selector) + +// ContactRevealDailyLimit is the predicate function for contactrevealdailylimit builders. +type ContactRevealDailyLimit func(*sql.Selector) + +// ContactRevealEvent is the predicate function for contactrevealevent builders. +type ContactRevealEvent func(*sql.Selector) + +// InternalUser is the predicate function for internaluser builders. +type InternalUser func(*sql.Selector) + +// Listing is the predicate function for listing builders. +type Listing func(*sql.Selector) + +// ListingEvent is the predicate function for listingevent builders. +type ListingEvent func(*sql.Selector) + +// ListingMedia is the predicate function for listingmedia builders. +type ListingMedia func(*sql.Selector) + +// Locality is the predicate function for locality builders. +type Locality func(*sql.Selector) + +// PlatformRole is the predicate function for platformrole builders. +type PlatformRole func(*sql.Selector) + +// ProviderContactChannel is the predicate function for providercontactchannel builders. +type ProviderContactChannel func(*sql.Selector) + +// ProviderProfile is the predicate function for providerprofile builders. +type ProviderProfile func(*sql.Selector) + +// ProviderServiceLocality is the predicate function for providerservicelocality builders. +type ProviderServiceLocality func(*sql.Selector) + +// ProviderSpokenLanguage is the predicate function for providerspokenlanguage builders. +type ProviderSpokenLanguage func(*sql.Selector) + +// ServiceCategory is the predicate function for servicecategory builders. +type ServiceCategory func(*sql.Selector) + +// ServiceCategoryTranslation is the predicate function for servicecategorytranslation builders. +type ServiceCategoryTranslation func(*sql.Selector) + +// SpokenLanguage is the predicate function for spokenlanguage builders. +type SpokenLanguage func(*sql.Selector) + +// SpokenLanguageTranslation is the predicate function for spokenlanguagetranslation builders. +type SpokenLanguageTranslation func(*sql.Selector) + +// SupportedLocale is the predicate function for supportedlocale builders. +type SupportedLocale func(*sql.Selector) + +// UserAccount is the predicate function for useraccount builders. +type UserAccount func(*sql.Selector) diff --git a/backend/ent/providercontactchannel.go b/backend/ent/providercontactchannel.go new file mode 100644 index 0000000..dbb3af1 --- /dev/null +++ b/backend/ent/providercontactchannel.go @@ -0,0 +1,199 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "fmt" + "strings" + "time" + + "entgo.io/ent" + "entgo.io/ent/dialect/sql" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/providercontactchannel" + "github.com/google/uuid" +) + +// ProviderContactChannel is the model entity for the ProviderContactChannel schema. +type ProviderContactChannel struct { + config `json:"-"` + // ID of the ent. + ID uuid.UUID `json:"id,omitempty"` + // InternalUserID holds the value of the "internal_user_id" field. + InternalUserID uuid.UUID `json:"internal_user_id,omitempty"` + // Channel holds the value of the "channel" field. + Channel providercontactchannel.Channel `json:"channel,omitempty"` + // Ciphertext holds the value of the "ciphertext" field. + Ciphertext []byte `json:"ciphertext,omitempty"` + // Nonce holds the value of the "nonce" field. + Nonce []byte `json:"nonce,omitempty"` + // KeyVersion holds the value of the "key_version" field. + KeyVersion string `json:"key_version,omitempty"` + // Enabled holds the value of the "enabled" field. + Enabled bool `json:"enabled,omitempty"` + // RevealConsent holds the value of the "reveal_consent" field. + RevealConsent bool `json:"reveal_consent,omitempty"` + // CreatedAt holds the value of the "created_at" field. + CreatedAt time.Time `json:"created_at,omitempty"` + // UpdatedAt holds the value of the "updated_at" field. + UpdatedAt time.Time `json:"updated_at,omitempty"` + selectValues sql.SelectValues +} + +// scanValues returns the types for scanning values from sql.Rows. +func (*ProviderContactChannel) scanValues(columns []string) ([]any, error) { + values := make([]any, len(columns)) + for i := range columns { + switch columns[i] { + case providercontactchannel.FieldCiphertext, providercontactchannel.FieldNonce: + values[i] = new([]byte) + case providercontactchannel.FieldEnabled, providercontactchannel.FieldRevealConsent: + values[i] = new(sql.NullBool) + case providercontactchannel.FieldChannel, providercontactchannel.FieldKeyVersion: + values[i] = new(sql.NullString) + case providercontactchannel.FieldCreatedAt, providercontactchannel.FieldUpdatedAt: + values[i] = new(sql.NullTime) + case providercontactchannel.FieldID, providercontactchannel.FieldInternalUserID: + 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 ProviderContactChannel fields. +func (_m *ProviderContactChannel) 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 providercontactchannel.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 providercontactchannel.FieldInternalUserID: + if value, ok := values[i].(*uuid.UUID); !ok { + return fmt.Errorf("unexpected type %T for field internal_user_id", values[i]) + } else if value != nil { + _m.InternalUserID = *value + } + case providercontactchannel.FieldChannel: + if value, ok := values[i].(*sql.NullString); !ok { + return fmt.Errorf("unexpected type %T for field channel", values[i]) + } else if value.Valid { + _m.Channel = providercontactchannel.Channel(value.String) + } + case providercontactchannel.FieldCiphertext: + if value, ok := values[i].(*[]byte); !ok { + return fmt.Errorf("unexpected type %T for field ciphertext", values[i]) + } else if value != nil { + _m.Ciphertext = *value + } + case providercontactchannel.FieldNonce: + if value, ok := values[i].(*[]byte); !ok { + return fmt.Errorf("unexpected type %T for field nonce", values[i]) + } else if value != nil { + _m.Nonce = *value + } + case providercontactchannel.FieldKeyVersion: + if value, ok := values[i].(*sql.NullString); !ok { + return fmt.Errorf("unexpected type %T for field key_version", values[i]) + } else if value.Valid { + _m.KeyVersion = value.String + } + case providercontactchannel.FieldEnabled: + if value, ok := values[i].(*sql.NullBool); !ok { + return fmt.Errorf("unexpected type %T for field enabled", values[i]) + } else if value.Valid { + _m.Enabled = value.Bool + } + case providercontactchannel.FieldRevealConsent: + if value, ok := values[i].(*sql.NullBool); !ok { + return fmt.Errorf("unexpected type %T for field reveal_consent", values[i]) + } else if value.Valid { + _m.RevealConsent = value.Bool + } + case providercontactchannel.FieldCreatedAt: + if value, ok := values[i].(*sql.NullTime); !ok { + return fmt.Errorf("unexpected type %T for field created_at", values[i]) + } else if value.Valid { + _m.CreatedAt = value.Time + } + case providercontactchannel.FieldUpdatedAt: + if value, ok := values[i].(*sql.NullTime); !ok { + return fmt.Errorf("unexpected type %T for field updated_at", values[i]) + } else if value.Valid { + _m.UpdatedAt = value.Time + } + default: + _m.selectValues.Set(columns[i], values[i]) + } + } + return nil +} + +// Value returns the ent.Value that was dynamically selected and assigned to the ProviderContactChannel. +// This includes values selected through modifiers, order, etc. +func (_m *ProviderContactChannel) Value(name string) (ent.Value, error) { + return _m.selectValues.Get(name) +} + +// Update returns a builder for updating this ProviderContactChannel. +// Note that you need to call ProviderContactChannel.Unwrap() before calling this method if this ProviderContactChannel +// was returned from a transaction, and the transaction was committed or rolled back. +func (_m *ProviderContactChannel) Update() *ProviderContactChannelUpdateOne { + return NewProviderContactChannelClient(_m.config).UpdateOne(_m) +} + +// Unwrap unwraps the ProviderContactChannel 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 *ProviderContactChannel) Unwrap() *ProviderContactChannel { + _tx, ok := _m.config.driver.(*txDriver) + if !ok { + panic("ent: ProviderContactChannel is not a transactional entity") + } + _m.config.driver = _tx.drv + return _m +} + +// String implements the fmt.Stringer. +func (_m *ProviderContactChannel) String() string { + var builder strings.Builder + builder.WriteString("ProviderContactChannel(") + builder.WriteString(fmt.Sprintf("id=%v, ", _m.ID)) + builder.WriteString("internal_user_id=") + builder.WriteString(fmt.Sprintf("%v", _m.InternalUserID)) + builder.WriteString(", ") + builder.WriteString("channel=") + builder.WriteString(fmt.Sprintf("%v", _m.Channel)) + builder.WriteString(", ") + builder.WriteString("ciphertext=") + builder.WriteString(fmt.Sprintf("%v", _m.Ciphertext)) + builder.WriteString(", ") + builder.WriteString("nonce=") + builder.WriteString(fmt.Sprintf("%v", _m.Nonce)) + builder.WriteString(", ") + builder.WriteString("key_version=") + builder.WriteString(_m.KeyVersion) + builder.WriteString(", ") + builder.WriteString("enabled=") + builder.WriteString(fmt.Sprintf("%v", _m.Enabled)) + builder.WriteString(", ") + builder.WriteString("reveal_consent=") + builder.WriteString(fmt.Sprintf("%v", _m.RevealConsent)) + builder.WriteString(", ") + builder.WriteString("created_at=") + builder.WriteString(_m.CreatedAt.Format(time.ANSIC)) + builder.WriteString(", ") + builder.WriteString("updated_at=") + builder.WriteString(_m.UpdatedAt.Format(time.ANSIC)) + builder.WriteByte(')') + return builder.String() +} + +// ProviderContactChannels is a parsable slice of ProviderContactChannel. +type ProviderContactChannels []*ProviderContactChannel diff --git a/backend/ent/providercontactchannel/providercontactchannel.go b/backend/ent/providercontactchannel/providercontactchannel.go new file mode 100644 index 0000000..22cbf5c --- /dev/null +++ b/backend/ent/providercontactchannel/providercontactchannel.go @@ -0,0 +1,145 @@ +// Code generated by ent, DO NOT EDIT. + +package providercontactchannel + +import ( + "fmt" + "time" + + "entgo.io/ent/dialect/sql" + "github.com/google/uuid" +) + +const ( + // Label holds the string label denoting the providercontactchannel type in the database. + Label = "provider_contact_channel" + // FieldID holds the string denoting the id field in the database. + FieldID = "id" + // FieldInternalUserID holds the string denoting the internal_user_id field in the database. + FieldInternalUserID = "internal_user_id" + // FieldChannel holds the string denoting the channel field in the database. + FieldChannel = "channel" + // FieldCiphertext holds the string denoting the ciphertext field in the database. + FieldCiphertext = "ciphertext" + // FieldNonce holds the string denoting the nonce field in the database. + FieldNonce = "nonce" + // FieldKeyVersion holds the string denoting the key_version field in the database. + FieldKeyVersion = "key_version" + // FieldEnabled holds the string denoting the enabled field in the database. + FieldEnabled = "enabled" + // FieldRevealConsent holds the string denoting the reveal_consent field in the database. + FieldRevealConsent = "reveal_consent" + // FieldCreatedAt holds the string denoting the created_at field in the database. + FieldCreatedAt = "created_at" + // FieldUpdatedAt holds the string denoting the updated_at field in the database. + FieldUpdatedAt = "updated_at" + // Table holds the table name of the providercontactchannel in the database. + Table = "provider_contact_channels" +) + +// Columns holds all SQL columns for providercontactchannel fields. +var Columns = []string{ + FieldID, + FieldInternalUserID, + FieldChannel, + FieldCiphertext, + FieldNonce, + FieldKeyVersion, + FieldEnabled, + FieldRevealConsent, + FieldCreatedAt, + FieldUpdatedAt, +} + +// 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 ( + // KeyVersionValidator is a validator for the "key_version" field. It is called by the builders before save. + KeyVersionValidator func(string) error + // DefaultEnabled holds the default value on creation for the "enabled" field. + DefaultEnabled bool + // DefaultRevealConsent holds the default value on creation for the "reveal_consent" field. + DefaultRevealConsent bool + // DefaultCreatedAt holds the default value on creation for the "created_at" field. + DefaultCreatedAt func() time.Time + // DefaultUpdatedAt holds the default value on creation for the "updated_at" field. + DefaultUpdatedAt func() time.Time + // UpdateDefaultUpdatedAt holds the default value on update for the "updated_at" field. + UpdateDefaultUpdatedAt func() time.Time + // DefaultID holds the default value on creation for the "id" field. + DefaultID func() uuid.UUID +) + +// Channel defines the type for the "channel" enum field. +type Channel string + +// Channel values. +const ( + ChannelPhone Channel = "phone" + ChannelWhatsapp Channel = "whatsapp" +) + +func (c Channel) String() string { + return string(c) +} + +// ChannelValidator is a validator for the "channel" field enum values. It is called by the builders before save. +func ChannelValidator(c Channel) error { + switch c { + case ChannelPhone, ChannelWhatsapp: + return nil + default: + return fmt.Errorf("providercontactchannel: invalid enum value for channel field: %q", c) + } +} + +// OrderOption defines the ordering options for the ProviderContactChannel 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() +} + +// ByInternalUserID orders the results by the internal_user_id field. +func ByInternalUserID(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldInternalUserID, opts...).ToFunc() +} + +// ByChannel orders the results by the channel field. +func ByChannel(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldChannel, opts...).ToFunc() +} + +// ByKeyVersion orders the results by the key_version field. +func ByKeyVersion(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldKeyVersion, opts...).ToFunc() +} + +// ByEnabled orders the results by the enabled field. +func ByEnabled(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldEnabled, opts...).ToFunc() +} + +// ByRevealConsent orders the results by the reveal_consent field. +func ByRevealConsent(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldRevealConsent, opts...).ToFunc() +} + +// ByCreatedAt orders the results by the created_at field. +func ByCreatedAt(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldCreatedAt, opts...).ToFunc() +} + +// ByUpdatedAt orders the results by the updated_at field. +func ByUpdatedAt(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldUpdatedAt, opts...).ToFunc() +} diff --git a/backend/ent/providercontactchannel/where.go b/backend/ent/providercontactchannel/where.go new file mode 100644 index 0000000..a08619a --- /dev/null +++ b/backend/ent/providercontactchannel/where.go @@ -0,0 +1,416 @@ +// Code generated by ent, DO NOT EDIT. + +package providercontactchannel + +import ( + "time" + + "entgo.io/ent/dialect/sql" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/predicate" + "github.com/google/uuid" +) + +// ID filters vertices based on their ID field. +func ID(id uuid.UUID) predicate.ProviderContactChannel { + return predicate.ProviderContactChannel(sql.FieldEQ(FieldID, id)) +} + +// IDEQ applies the EQ predicate on the ID field. +func IDEQ(id uuid.UUID) predicate.ProviderContactChannel { + return predicate.ProviderContactChannel(sql.FieldEQ(FieldID, id)) +} + +// IDNEQ applies the NEQ predicate on the ID field. +func IDNEQ(id uuid.UUID) predicate.ProviderContactChannel { + return predicate.ProviderContactChannel(sql.FieldNEQ(FieldID, id)) +} + +// IDIn applies the In predicate on the ID field. +func IDIn(ids ...uuid.UUID) predicate.ProviderContactChannel { + return predicate.ProviderContactChannel(sql.FieldIn(FieldID, ids...)) +} + +// IDNotIn applies the NotIn predicate on the ID field. +func IDNotIn(ids ...uuid.UUID) predicate.ProviderContactChannel { + return predicate.ProviderContactChannel(sql.FieldNotIn(FieldID, ids...)) +} + +// IDGT applies the GT predicate on the ID field. +func IDGT(id uuid.UUID) predicate.ProviderContactChannel { + return predicate.ProviderContactChannel(sql.FieldGT(FieldID, id)) +} + +// IDGTE applies the GTE predicate on the ID field. +func IDGTE(id uuid.UUID) predicate.ProviderContactChannel { + return predicate.ProviderContactChannel(sql.FieldGTE(FieldID, id)) +} + +// IDLT applies the LT predicate on the ID field. +func IDLT(id uuid.UUID) predicate.ProviderContactChannel { + return predicate.ProviderContactChannel(sql.FieldLT(FieldID, id)) +} + +// IDLTE applies the LTE predicate on the ID field. +func IDLTE(id uuid.UUID) predicate.ProviderContactChannel { + return predicate.ProviderContactChannel(sql.FieldLTE(FieldID, id)) +} + +// InternalUserID applies equality check predicate on the "internal_user_id" field. It's identical to InternalUserIDEQ. +func InternalUserID(v uuid.UUID) predicate.ProviderContactChannel { + return predicate.ProviderContactChannel(sql.FieldEQ(FieldInternalUserID, v)) +} + +// Ciphertext applies equality check predicate on the "ciphertext" field. It's identical to CiphertextEQ. +func Ciphertext(v []byte) predicate.ProviderContactChannel { + return predicate.ProviderContactChannel(sql.FieldEQ(FieldCiphertext, v)) +} + +// Nonce applies equality check predicate on the "nonce" field. It's identical to NonceEQ. +func Nonce(v []byte) predicate.ProviderContactChannel { + return predicate.ProviderContactChannel(sql.FieldEQ(FieldNonce, v)) +} + +// KeyVersion applies equality check predicate on the "key_version" field. It's identical to KeyVersionEQ. +func KeyVersion(v string) predicate.ProviderContactChannel { + return predicate.ProviderContactChannel(sql.FieldEQ(FieldKeyVersion, v)) +} + +// Enabled applies equality check predicate on the "enabled" field. It's identical to EnabledEQ. +func Enabled(v bool) predicate.ProviderContactChannel { + return predicate.ProviderContactChannel(sql.FieldEQ(FieldEnabled, v)) +} + +// RevealConsent applies equality check predicate on the "reveal_consent" field. It's identical to RevealConsentEQ. +func RevealConsent(v bool) predicate.ProviderContactChannel { + return predicate.ProviderContactChannel(sql.FieldEQ(FieldRevealConsent, v)) +} + +// CreatedAt applies equality check predicate on the "created_at" field. It's identical to CreatedAtEQ. +func CreatedAt(v time.Time) predicate.ProviderContactChannel { + return predicate.ProviderContactChannel(sql.FieldEQ(FieldCreatedAt, v)) +} + +// UpdatedAt applies equality check predicate on the "updated_at" field. It's identical to UpdatedAtEQ. +func UpdatedAt(v time.Time) predicate.ProviderContactChannel { + return predicate.ProviderContactChannel(sql.FieldEQ(FieldUpdatedAt, v)) +} + +// InternalUserIDEQ applies the EQ predicate on the "internal_user_id" field. +func InternalUserIDEQ(v uuid.UUID) predicate.ProviderContactChannel { + return predicate.ProviderContactChannel(sql.FieldEQ(FieldInternalUserID, v)) +} + +// InternalUserIDNEQ applies the NEQ predicate on the "internal_user_id" field. +func InternalUserIDNEQ(v uuid.UUID) predicate.ProviderContactChannel { + return predicate.ProviderContactChannel(sql.FieldNEQ(FieldInternalUserID, v)) +} + +// InternalUserIDIn applies the In predicate on the "internal_user_id" field. +func InternalUserIDIn(vs ...uuid.UUID) predicate.ProviderContactChannel { + return predicate.ProviderContactChannel(sql.FieldIn(FieldInternalUserID, vs...)) +} + +// InternalUserIDNotIn applies the NotIn predicate on the "internal_user_id" field. +func InternalUserIDNotIn(vs ...uuid.UUID) predicate.ProviderContactChannel { + return predicate.ProviderContactChannel(sql.FieldNotIn(FieldInternalUserID, vs...)) +} + +// InternalUserIDGT applies the GT predicate on the "internal_user_id" field. +func InternalUserIDGT(v uuid.UUID) predicate.ProviderContactChannel { + return predicate.ProviderContactChannel(sql.FieldGT(FieldInternalUserID, v)) +} + +// InternalUserIDGTE applies the GTE predicate on the "internal_user_id" field. +func InternalUserIDGTE(v uuid.UUID) predicate.ProviderContactChannel { + return predicate.ProviderContactChannel(sql.FieldGTE(FieldInternalUserID, v)) +} + +// InternalUserIDLT applies the LT predicate on the "internal_user_id" field. +func InternalUserIDLT(v uuid.UUID) predicate.ProviderContactChannel { + return predicate.ProviderContactChannel(sql.FieldLT(FieldInternalUserID, v)) +} + +// InternalUserIDLTE applies the LTE predicate on the "internal_user_id" field. +func InternalUserIDLTE(v uuid.UUID) predicate.ProviderContactChannel { + return predicate.ProviderContactChannel(sql.FieldLTE(FieldInternalUserID, v)) +} + +// ChannelEQ applies the EQ predicate on the "channel" field. +func ChannelEQ(v Channel) predicate.ProviderContactChannel { + return predicate.ProviderContactChannel(sql.FieldEQ(FieldChannel, v)) +} + +// ChannelNEQ applies the NEQ predicate on the "channel" field. +func ChannelNEQ(v Channel) predicate.ProviderContactChannel { + return predicate.ProviderContactChannel(sql.FieldNEQ(FieldChannel, v)) +} + +// ChannelIn applies the In predicate on the "channel" field. +func ChannelIn(vs ...Channel) predicate.ProviderContactChannel { + return predicate.ProviderContactChannel(sql.FieldIn(FieldChannel, vs...)) +} + +// ChannelNotIn applies the NotIn predicate on the "channel" field. +func ChannelNotIn(vs ...Channel) predicate.ProviderContactChannel { + return predicate.ProviderContactChannel(sql.FieldNotIn(FieldChannel, vs...)) +} + +// CiphertextEQ applies the EQ predicate on the "ciphertext" field. +func CiphertextEQ(v []byte) predicate.ProviderContactChannel { + return predicate.ProviderContactChannel(sql.FieldEQ(FieldCiphertext, v)) +} + +// CiphertextNEQ applies the NEQ predicate on the "ciphertext" field. +func CiphertextNEQ(v []byte) predicate.ProviderContactChannel { + return predicate.ProviderContactChannel(sql.FieldNEQ(FieldCiphertext, v)) +} + +// CiphertextIn applies the In predicate on the "ciphertext" field. +func CiphertextIn(vs ...[]byte) predicate.ProviderContactChannel { + return predicate.ProviderContactChannel(sql.FieldIn(FieldCiphertext, vs...)) +} + +// CiphertextNotIn applies the NotIn predicate on the "ciphertext" field. +func CiphertextNotIn(vs ...[]byte) predicate.ProviderContactChannel { + return predicate.ProviderContactChannel(sql.FieldNotIn(FieldCiphertext, vs...)) +} + +// CiphertextGT applies the GT predicate on the "ciphertext" field. +func CiphertextGT(v []byte) predicate.ProviderContactChannel { + return predicate.ProviderContactChannel(sql.FieldGT(FieldCiphertext, v)) +} + +// CiphertextGTE applies the GTE predicate on the "ciphertext" field. +func CiphertextGTE(v []byte) predicate.ProviderContactChannel { + return predicate.ProviderContactChannel(sql.FieldGTE(FieldCiphertext, v)) +} + +// CiphertextLT applies the LT predicate on the "ciphertext" field. +func CiphertextLT(v []byte) predicate.ProviderContactChannel { + return predicate.ProviderContactChannel(sql.FieldLT(FieldCiphertext, v)) +} + +// CiphertextLTE applies the LTE predicate on the "ciphertext" field. +func CiphertextLTE(v []byte) predicate.ProviderContactChannel { + return predicate.ProviderContactChannel(sql.FieldLTE(FieldCiphertext, v)) +} + +// NonceEQ applies the EQ predicate on the "nonce" field. +func NonceEQ(v []byte) predicate.ProviderContactChannel { + return predicate.ProviderContactChannel(sql.FieldEQ(FieldNonce, v)) +} + +// NonceNEQ applies the NEQ predicate on the "nonce" field. +func NonceNEQ(v []byte) predicate.ProviderContactChannel { + return predicate.ProviderContactChannel(sql.FieldNEQ(FieldNonce, v)) +} + +// NonceIn applies the In predicate on the "nonce" field. +func NonceIn(vs ...[]byte) predicate.ProviderContactChannel { + return predicate.ProviderContactChannel(sql.FieldIn(FieldNonce, vs...)) +} + +// NonceNotIn applies the NotIn predicate on the "nonce" field. +func NonceNotIn(vs ...[]byte) predicate.ProviderContactChannel { + return predicate.ProviderContactChannel(sql.FieldNotIn(FieldNonce, vs...)) +} + +// NonceGT applies the GT predicate on the "nonce" field. +func NonceGT(v []byte) predicate.ProviderContactChannel { + return predicate.ProviderContactChannel(sql.FieldGT(FieldNonce, v)) +} + +// NonceGTE applies the GTE predicate on the "nonce" field. +func NonceGTE(v []byte) predicate.ProviderContactChannel { + return predicate.ProviderContactChannel(sql.FieldGTE(FieldNonce, v)) +} + +// NonceLT applies the LT predicate on the "nonce" field. +func NonceLT(v []byte) predicate.ProviderContactChannel { + return predicate.ProviderContactChannel(sql.FieldLT(FieldNonce, v)) +} + +// NonceLTE applies the LTE predicate on the "nonce" field. +func NonceLTE(v []byte) predicate.ProviderContactChannel { + return predicate.ProviderContactChannel(sql.FieldLTE(FieldNonce, v)) +} + +// KeyVersionEQ applies the EQ predicate on the "key_version" field. +func KeyVersionEQ(v string) predicate.ProviderContactChannel { + return predicate.ProviderContactChannel(sql.FieldEQ(FieldKeyVersion, v)) +} + +// KeyVersionNEQ applies the NEQ predicate on the "key_version" field. +func KeyVersionNEQ(v string) predicate.ProviderContactChannel { + return predicate.ProviderContactChannel(sql.FieldNEQ(FieldKeyVersion, v)) +} + +// KeyVersionIn applies the In predicate on the "key_version" field. +func KeyVersionIn(vs ...string) predicate.ProviderContactChannel { + return predicate.ProviderContactChannel(sql.FieldIn(FieldKeyVersion, vs...)) +} + +// KeyVersionNotIn applies the NotIn predicate on the "key_version" field. +func KeyVersionNotIn(vs ...string) predicate.ProviderContactChannel { + return predicate.ProviderContactChannel(sql.FieldNotIn(FieldKeyVersion, vs...)) +} + +// KeyVersionGT applies the GT predicate on the "key_version" field. +func KeyVersionGT(v string) predicate.ProviderContactChannel { + return predicate.ProviderContactChannel(sql.FieldGT(FieldKeyVersion, v)) +} + +// KeyVersionGTE applies the GTE predicate on the "key_version" field. +func KeyVersionGTE(v string) predicate.ProviderContactChannel { + return predicate.ProviderContactChannel(sql.FieldGTE(FieldKeyVersion, v)) +} + +// KeyVersionLT applies the LT predicate on the "key_version" field. +func KeyVersionLT(v string) predicate.ProviderContactChannel { + return predicate.ProviderContactChannel(sql.FieldLT(FieldKeyVersion, v)) +} + +// KeyVersionLTE applies the LTE predicate on the "key_version" field. +func KeyVersionLTE(v string) predicate.ProviderContactChannel { + return predicate.ProviderContactChannel(sql.FieldLTE(FieldKeyVersion, v)) +} + +// KeyVersionContains applies the Contains predicate on the "key_version" field. +func KeyVersionContains(v string) predicate.ProviderContactChannel { + return predicate.ProviderContactChannel(sql.FieldContains(FieldKeyVersion, v)) +} + +// KeyVersionHasPrefix applies the HasPrefix predicate on the "key_version" field. +func KeyVersionHasPrefix(v string) predicate.ProviderContactChannel { + return predicate.ProviderContactChannel(sql.FieldHasPrefix(FieldKeyVersion, v)) +} + +// KeyVersionHasSuffix applies the HasSuffix predicate on the "key_version" field. +func KeyVersionHasSuffix(v string) predicate.ProviderContactChannel { + return predicate.ProviderContactChannel(sql.FieldHasSuffix(FieldKeyVersion, v)) +} + +// KeyVersionEqualFold applies the EqualFold predicate on the "key_version" field. +func KeyVersionEqualFold(v string) predicate.ProviderContactChannel { + return predicate.ProviderContactChannel(sql.FieldEqualFold(FieldKeyVersion, v)) +} + +// KeyVersionContainsFold applies the ContainsFold predicate on the "key_version" field. +func KeyVersionContainsFold(v string) predicate.ProviderContactChannel { + return predicate.ProviderContactChannel(sql.FieldContainsFold(FieldKeyVersion, v)) +} + +// EnabledEQ applies the EQ predicate on the "enabled" field. +func EnabledEQ(v bool) predicate.ProviderContactChannel { + return predicate.ProviderContactChannel(sql.FieldEQ(FieldEnabled, v)) +} + +// EnabledNEQ applies the NEQ predicate on the "enabled" field. +func EnabledNEQ(v bool) predicate.ProviderContactChannel { + return predicate.ProviderContactChannel(sql.FieldNEQ(FieldEnabled, v)) +} + +// RevealConsentEQ applies the EQ predicate on the "reveal_consent" field. +func RevealConsentEQ(v bool) predicate.ProviderContactChannel { + return predicate.ProviderContactChannel(sql.FieldEQ(FieldRevealConsent, v)) +} + +// RevealConsentNEQ applies the NEQ predicate on the "reveal_consent" field. +func RevealConsentNEQ(v bool) predicate.ProviderContactChannel { + return predicate.ProviderContactChannel(sql.FieldNEQ(FieldRevealConsent, v)) +} + +// CreatedAtEQ applies the EQ predicate on the "created_at" field. +func CreatedAtEQ(v time.Time) predicate.ProviderContactChannel { + return predicate.ProviderContactChannel(sql.FieldEQ(FieldCreatedAt, v)) +} + +// CreatedAtNEQ applies the NEQ predicate on the "created_at" field. +func CreatedAtNEQ(v time.Time) predicate.ProviderContactChannel { + return predicate.ProviderContactChannel(sql.FieldNEQ(FieldCreatedAt, v)) +} + +// CreatedAtIn applies the In predicate on the "created_at" field. +func CreatedAtIn(vs ...time.Time) predicate.ProviderContactChannel { + return predicate.ProviderContactChannel(sql.FieldIn(FieldCreatedAt, vs...)) +} + +// CreatedAtNotIn applies the NotIn predicate on the "created_at" field. +func CreatedAtNotIn(vs ...time.Time) predicate.ProviderContactChannel { + return predicate.ProviderContactChannel(sql.FieldNotIn(FieldCreatedAt, vs...)) +} + +// CreatedAtGT applies the GT predicate on the "created_at" field. +func CreatedAtGT(v time.Time) predicate.ProviderContactChannel { + return predicate.ProviderContactChannel(sql.FieldGT(FieldCreatedAt, v)) +} + +// CreatedAtGTE applies the GTE predicate on the "created_at" field. +func CreatedAtGTE(v time.Time) predicate.ProviderContactChannel { + return predicate.ProviderContactChannel(sql.FieldGTE(FieldCreatedAt, v)) +} + +// CreatedAtLT applies the LT predicate on the "created_at" field. +func CreatedAtLT(v time.Time) predicate.ProviderContactChannel { + return predicate.ProviderContactChannel(sql.FieldLT(FieldCreatedAt, v)) +} + +// CreatedAtLTE applies the LTE predicate on the "created_at" field. +func CreatedAtLTE(v time.Time) predicate.ProviderContactChannel { + return predicate.ProviderContactChannel(sql.FieldLTE(FieldCreatedAt, v)) +} + +// UpdatedAtEQ applies the EQ predicate on the "updated_at" field. +func UpdatedAtEQ(v time.Time) predicate.ProviderContactChannel { + return predicate.ProviderContactChannel(sql.FieldEQ(FieldUpdatedAt, v)) +} + +// UpdatedAtNEQ applies the NEQ predicate on the "updated_at" field. +func UpdatedAtNEQ(v time.Time) predicate.ProviderContactChannel { + return predicate.ProviderContactChannel(sql.FieldNEQ(FieldUpdatedAt, v)) +} + +// UpdatedAtIn applies the In predicate on the "updated_at" field. +func UpdatedAtIn(vs ...time.Time) predicate.ProviderContactChannel { + return predicate.ProviderContactChannel(sql.FieldIn(FieldUpdatedAt, vs...)) +} + +// UpdatedAtNotIn applies the NotIn predicate on the "updated_at" field. +func UpdatedAtNotIn(vs ...time.Time) predicate.ProviderContactChannel { + return predicate.ProviderContactChannel(sql.FieldNotIn(FieldUpdatedAt, vs...)) +} + +// UpdatedAtGT applies the GT predicate on the "updated_at" field. +func UpdatedAtGT(v time.Time) predicate.ProviderContactChannel { + return predicate.ProviderContactChannel(sql.FieldGT(FieldUpdatedAt, v)) +} + +// UpdatedAtGTE applies the GTE predicate on the "updated_at" field. +func UpdatedAtGTE(v time.Time) predicate.ProviderContactChannel { + return predicate.ProviderContactChannel(sql.FieldGTE(FieldUpdatedAt, v)) +} + +// UpdatedAtLT applies the LT predicate on the "updated_at" field. +func UpdatedAtLT(v time.Time) predicate.ProviderContactChannel { + return predicate.ProviderContactChannel(sql.FieldLT(FieldUpdatedAt, v)) +} + +// UpdatedAtLTE applies the LTE predicate on the "updated_at" field. +func UpdatedAtLTE(v time.Time) predicate.ProviderContactChannel { + return predicate.ProviderContactChannel(sql.FieldLTE(FieldUpdatedAt, v)) +} + +// And groups predicates with the AND operator between them. +func And(predicates ...predicate.ProviderContactChannel) predicate.ProviderContactChannel { + return predicate.ProviderContactChannel(sql.AndPredicates(predicates...)) +} + +// Or groups predicates with the OR operator between them. +func Or(predicates ...predicate.ProviderContactChannel) predicate.ProviderContactChannel { + return predicate.ProviderContactChannel(sql.OrPredicates(predicates...)) +} + +// Not applies the not operator on the given predicate. +func Not(p predicate.ProviderContactChannel) predicate.ProviderContactChannel { + return predicate.ProviderContactChannel(sql.NotPredicates(p)) +} diff --git a/backend/ent/providercontactchannel_create.go b/backend/ent/providercontactchannel_create.go new file mode 100644 index 0000000..21fce50 --- /dev/null +++ b/backend/ent/providercontactchannel_create.go @@ -0,0 +1,376 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "context" + "errors" + "fmt" + "time" + + "entgo.io/ent/dialect/sql/sqlgraph" + "entgo.io/ent/schema/field" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/providercontactchannel" + "github.com/google/uuid" +) + +// ProviderContactChannelCreate is the builder for creating a ProviderContactChannel entity. +type ProviderContactChannelCreate struct { + config + mutation *ProviderContactChannelMutation + hooks []Hook +} + +// SetInternalUserID sets the "internal_user_id" field. +func (_c *ProviderContactChannelCreate) SetInternalUserID(v uuid.UUID) *ProviderContactChannelCreate { + _c.mutation.SetInternalUserID(v) + return _c +} + +// SetChannel sets the "channel" field. +func (_c *ProviderContactChannelCreate) SetChannel(v providercontactchannel.Channel) *ProviderContactChannelCreate { + _c.mutation.SetChannel(v) + return _c +} + +// SetCiphertext sets the "ciphertext" field. +func (_c *ProviderContactChannelCreate) SetCiphertext(v []byte) *ProviderContactChannelCreate { + _c.mutation.SetCiphertext(v) + return _c +} + +// SetNonce sets the "nonce" field. +func (_c *ProviderContactChannelCreate) SetNonce(v []byte) *ProviderContactChannelCreate { + _c.mutation.SetNonce(v) + return _c +} + +// SetKeyVersion sets the "key_version" field. +func (_c *ProviderContactChannelCreate) SetKeyVersion(v string) *ProviderContactChannelCreate { + _c.mutation.SetKeyVersion(v) + return _c +} + +// SetEnabled sets the "enabled" field. +func (_c *ProviderContactChannelCreate) SetEnabled(v bool) *ProviderContactChannelCreate { + _c.mutation.SetEnabled(v) + return _c +} + +// SetNillableEnabled sets the "enabled" field if the given value is not nil. +func (_c *ProviderContactChannelCreate) SetNillableEnabled(v *bool) *ProviderContactChannelCreate { + if v != nil { + _c.SetEnabled(*v) + } + return _c +} + +// SetRevealConsent sets the "reveal_consent" field. +func (_c *ProviderContactChannelCreate) SetRevealConsent(v bool) *ProviderContactChannelCreate { + _c.mutation.SetRevealConsent(v) + return _c +} + +// SetNillableRevealConsent sets the "reveal_consent" field if the given value is not nil. +func (_c *ProviderContactChannelCreate) SetNillableRevealConsent(v *bool) *ProviderContactChannelCreate { + if v != nil { + _c.SetRevealConsent(*v) + } + return _c +} + +// SetCreatedAt sets the "created_at" field. +func (_c *ProviderContactChannelCreate) SetCreatedAt(v time.Time) *ProviderContactChannelCreate { + _c.mutation.SetCreatedAt(v) + return _c +} + +// SetNillableCreatedAt sets the "created_at" field if the given value is not nil. +func (_c *ProviderContactChannelCreate) SetNillableCreatedAt(v *time.Time) *ProviderContactChannelCreate { + if v != nil { + _c.SetCreatedAt(*v) + } + return _c +} + +// SetUpdatedAt sets the "updated_at" field. +func (_c *ProviderContactChannelCreate) SetUpdatedAt(v time.Time) *ProviderContactChannelCreate { + _c.mutation.SetUpdatedAt(v) + return _c +} + +// SetNillableUpdatedAt sets the "updated_at" field if the given value is not nil. +func (_c *ProviderContactChannelCreate) SetNillableUpdatedAt(v *time.Time) *ProviderContactChannelCreate { + if v != nil { + _c.SetUpdatedAt(*v) + } + return _c +} + +// SetID sets the "id" field. +func (_c *ProviderContactChannelCreate) SetID(v uuid.UUID) *ProviderContactChannelCreate { + _c.mutation.SetID(v) + return _c +} + +// SetNillableID sets the "id" field if the given value is not nil. +func (_c *ProviderContactChannelCreate) SetNillableID(v *uuid.UUID) *ProviderContactChannelCreate { + if v != nil { + _c.SetID(*v) + } + return _c +} + +// Mutation returns the ProviderContactChannelMutation object of the builder. +func (_c *ProviderContactChannelCreate) Mutation() *ProviderContactChannelMutation { + return _c.mutation +} + +// Save creates the ProviderContactChannel in the database. +func (_c *ProviderContactChannelCreate) Save(ctx context.Context) (*ProviderContactChannel, error) { + _c.defaults() + return withHooks(ctx, _c.sqlSave, _c.mutation, _c.hooks) +} + +// SaveX calls Save and panics if Save returns an error. +func (_c *ProviderContactChannelCreate) SaveX(ctx context.Context) *ProviderContactChannel { + v, err := _c.Save(ctx) + if err != nil { + panic(err) + } + return v +} + +// Exec executes the query. +func (_c *ProviderContactChannelCreate) Exec(ctx context.Context) error { + _, err := _c.Save(ctx) + return err +} + +// ExecX is like Exec, but panics if an error occurs. +func (_c *ProviderContactChannelCreate) 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 *ProviderContactChannelCreate) defaults() { + if _, ok := _c.mutation.Enabled(); !ok { + v := providercontactchannel.DefaultEnabled + _c.mutation.SetEnabled(v) + } + if _, ok := _c.mutation.RevealConsent(); !ok { + v := providercontactchannel.DefaultRevealConsent + _c.mutation.SetRevealConsent(v) + } + if _, ok := _c.mutation.CreatedAt(); !ok { + v := providercontactchannel.DefaultCreatedAt() + _c.mutation.SetCreatedAt(v) + } + if _, ok := _c.mutation.UpdatedAt(); !ok { + v := providercontactchannel.DefaultUpdatedAt() + _c.mutation.SetUpdatedAt(v) + } + if _, ok := _c.mutation.ID(); !ok { + v := providercontactchannel.DefaultID() + _c.mutation.SetID(v) + } +} + +// check runs all checks and user-defined validators on the builder. +func (_c *ProviderContactChannelCreate) check() error { + if _, ok := _c.mutation.InternalUserID(); !ok { + return &ValidationError{Name: "internal_user_id", err: errors.New(`ent: missing required field "ProviderContactChannel.internal_user_id"`)} + } + if _, ok := _c.mutation.Channel(); !ok { + return &ValidationError{Name: "channel", err: errors.New(`ent: missing required field "ProviderContactChannel.channel"`)} + } + if v, ok := _c.mutation.Channel(); ok { + if err := providercontactchannel.ChannelValidator(v); err != nil { + return &ValidationError{Name: "channel", err: fmt.Errorf(`ent: validator failed for field "ProviderContactChannel.channel": %w`, err)} + } + } + if _, ok := _c.mutation.Ciphertext(); !ok { + return &ValidationError{Name: "ciphertext", err: errors.New(`ent: missing required field "ProviderContactChannel.ciphertext"`)} + } + if _, ok := _c.mutation.Nonce(); !ok { + return &ValidationError{Name: "nonce", err: errors.New(`ent: missing required field "ProviderContactChannel.nonce"`)} + } + if _, ok := _c.mutation.KeyVersion(); !ok { + return &ValidationError{Name: "key_version", err: errors.New(`ent: missing required field "ProviderContactChannel.key_version"`)} + } + if v, ok := _c.mutation.KeyVersion(); ok { + if err := providercontactchannel.KeyVersionValidator(v); err != nil { + return &ValidationError{Name: "key_version", err: fmt.Errorf(`ent: validator failed for field "ProviderContactChannel.key_version": %w`, err)} + } + } + if _, ok := _c.mutation.Enabled(); !ok { + return &ValidationError{Name: "enabled", err: errors.New(`ent: missing required field "ProviderContactChannel.enabled"`)} + } + if _, ok := _c.mutation.RevealConsent(); !ok { + return &ValidationError{Name: "reveal_consent", err: errors.New(`ent: missing required field "ProviderContactChannel.reveal_consent"`)} + } + if _, ok := _c.mutation.CreatedAt(); !ok { + return &ValidationError{Name: "created_at", err: errors.New(`ent: missing required field "ProviderContactChannel.created_at"`)} + } + if _, ok := _c.mutation.UpdatedAt(); !ok { + return &ValidationError{Name: "updated_at", err: errors.New(`ent: missing required field "ProviderContactChannel.updated_at"`)} + } + return nil +} + +func (_c *ProviderContactChannelCreate) sqlSave(ctx context.Context) (*ProviderContactChannel, 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 *ProviderContactChannelCreate) createSpec() (*ProviderContactChannel, *sqlgraph.CreateSpec) { + var ( + _node = &ProviderContactChannel{config: _c.config} + _spec = sqlgraph.NewCreateSpec(providercontactchannel.Table, sqlgraph.NewFieldSpec(providercontactchannel.FieldID, field.TypeUUID)) + ) + if id, ok := _c.mutation.ID(); ok { + _node.ID = id + _spec.ID.Value = &id + } + if value, ok := _c.mutation.InternalUserID(); ok { + _spec.SetField(providercontactchannel.FieldInternalUserID, field.TypeUUID, value) + _node.InternalUserID = value + } + if value, ok := _c.mutation.Channel(); ok { + _spec.SetField(providercontactchannel.FieldChannel, field.TypeEnum, value) + _node.Channel = value + } + if value, ok := _c.mutation.Ciphertext(); ok { + _spec.SetField(providercontactchannel.FieldCiphertext, field.TypeBytes, value) + _node.Ciphertext = value + } + if value, ok := _c.mutation.Nonce(); ok { + _spec.SetField(providercontactchannel.FieldNonce, field.TypeBytes, value) + _node.Nonce = value + } + if value, ok := _c.mutation.KeyVersion(); ok { + _spec.SetField(providercontactchannel.FieldKeyVersion, field.TypeString, value) + _node.KeyVersion = value + } + if value, ok := _c.mutation.Enabled(); ok { + _spec.SetField(providercontactchannel.FieldEnabled, field.TypeBool, value) + _node.Enabled = value + } + if value, ok := _c.mutation.RevealConsent(); ok { + _spec.SetField(providercontactchannel.FieldRevealConsent, field.TypeBool, value) + _node.RevealConsent = value + } + if value, ok := _c.mutation.CreatedAt(); ok { + _spec.SetField(providercontactchannel.FieldCreatedAt, field.TypeTime, value) + _node.CreatedAt = value + } + if value, ok := _c.mutation.UpdatedAt(); ok { + _spec.SetField(providercontactchannel.FieldUpdatedAt, field.TypeTime, value) + _node.UpdatedAt = value + } + return _node, _spec +} + +// ProviderContactChannelCreateBulk is the builder for creating many ProviderContactChannel entities in bulk. +type ProviderContactChannelCreateBulk struct { + config + err error + builders []*ProviderContactChannelCreate +} + +// Save creates the ProviderContactChannel entities in the database. +func (_c *ProviderContactChannelCreateBulk) Save(ctx context.Context) ([]*ProviderContactChannel, error) { + if _c.err != nil { + return nil, _c.err + } + specs := make([]*sqlgraph.CreateSpec, len(_c.builders)) + nodes := make([]*ProviderContactChannel, 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.(*ProviderContactChannelMutation) + 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 *ProviderContactChannelCreateBulk) SaveX(ctx context.Context) []*ProviderContactChannel { + v, err := _c.Save(ctx) + if err != nil { + panic(err) + } + return v +} + +// Exec executes the query. +func (_c *ProviderContactChannelCreateBulk) Exec(ctx context.Context) error { + _, err := _c.Save(ctx) + return err +} + +// ExecX is like Exec, but panics if an error occurs. +func (_c *ProviderContactChannelCreateBulk) ExecX(ctx context.Context) { + if err := _c.Exec(ctx); err != nil { + panic(err) + } +} diff --git a/backend/ent/providercontactchannel_delete.go b/backend/ent/providercontactchannel_delete.go new file mode 100644 index 0000000..e4dcff1 --- /dev/null +++ b/backend/ent/providercontactchannel_delete.go @@ -0,0 +1,88 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "context" + + "entgo.io/ent/dialect/sql" + "entgo.io/ent/dialect/sql/sqlgraph" + "entgo.io/ent/schema/field" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/predicate" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/providercontactchannel" +) + +// ProviderContactChannelDelete is the builder for deleting a ProviderContactChannel entity. +type ProviderContactChannelDelete struct { + config + hooks []Hook + mutation *ProviderContactChannelMutation +} + +// Where appends a list predicates to the ProviderContactChannelDelete builder. +func (_d *ProviderContactChannelDelete) Where(ps ...predicate.ProviderContactChannel) *ProviderContactChannelDelete { + _d.mutation.Where(ps...) + return _d +} + +// Exec executes the deletion query and returns how many vertices were deleted. +func (_d *ProviderContactChannelDelete) 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 *ProviderContactChannelDelete) ExecX(ctx context.Context) int { + n, err := _d.Exec(ctx) + if err != nil { + panic(err) + } + return n +} + +func (_d *ProviderContactChannelDelete) sqlExec(ctx context.Context) (int, error) { + _spec := sqlgraph.NewDeleteSpec(providercontactchannel.Table, sqlgraph.NewFieldSpec(providercontactchannel.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 +} + +// ProviderContactChannelDeleteOne is the builder for deleting a single ProviderContactChannel entity. +type ProviderContactChannelDeleteOne struct { + _d *ProviderContactChannelDelete +} + +// Where appends a list predicates to the ProviderContactChannelDelete builder. +func (_d *ProviderContactChannelDeleteOne) Where(ps ...predicate.ProviderContactChannel) *ProviderContactChannelDeleteOne { + _d._d.mutation.Where(ps...) + return _d +} + +// Exec executes the deletion query. +func (_d *ProviderContactChannelDeleteOne) Exec(ctx context.Context) error { + n, err := _d._d.Exec(ctx) + switch { + case err != nil: + return err + case n == 0: + return &NotFoundError{providercontactchannel.Label} + default: + return nil + } +} + +// ExecX is like Exec, but panics if an error occurs. +func (_d *ProviderContactChannelDeleteOne) ExecX(ctx context.Context) { + if err := _d.Exec(ctx); err != nil { + panic(err) + } +} diff --git a/backend/ent/providercontactchannel_query.go b/backend/ent/providercontactchannel_query.go new file mode 100644 index 0000000..10544c0 --- /dev/null +++ b/backend/ent/providercontactchannel_query.go @@ -0,0 +1,528 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "context" + "fmt" + "math" + + "entgo.io/ent" + "entgo.io/ent/dialect/sql" + "entgo.io/ent/dialect/sql/sqlgraph" + "entgo.io/ent/schema/field" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/predicate" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/providercontactchannel" + "github.com/google/uuid" +) + +// ProviderContactChannelQuery is the builder for querying ProviderContactChannel entities. +type ProviderContactChannelQuery struct { + config + ctx *QueryContext + order []providercontactchannel.OrderOption + inters []Interceptor + predicates []predicate.ProviderContactChannel + // intermediate query (i.e. traversal path). + sql *sql.Selector + path func(context.Context) (*sql.Selector, error) +} + +// Where adds a new predicate for the ProviderContactChannelQuery builder. +func (_q *ProviderContactChannelQuery) Where(ps ...predicate.ProviderContactChannel) *ProviderContactChannelQuery { + _q.predicates = append(_q.predicates, ps...) + return _q +} + +// Limit the number of records to be returned by this query. +func (_q *ProviderContactChannelQuery) Limit(limit int) *ProviderContactChannelQuery { + _q.ctx.Limit = &limit + return _q +} + +// Offset to start from. +func (_q *ProviderContactChannelQuery) Offset(offset int) *ProviderContactChannelQuery { + _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 *ProviderContactChannelQuery) Unique(unique bool) *ProviderContactChannelQuery { + _q.ctx.Unique = &unique + return _q +} + +// Order specifies how the records should be ordered. +func (_q *ProviderContactChannelQuery) Order(o ...providercontactchannel.OrderOption) *ProviderContactChannelQuery { + _q.order = append(_q.order, o...) + return _q +} + +// First returns the first ProviderContactChannel entity from the query. +// Returns a *NotFoundError when no ProviderContactChannel was found. +func (_q *ProviderContactChannelQuery) First(ctx context.Context) (*ProviderContactChannel, 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{providercontactchannel.Label} + } + return nodes[0], nil +} + +// FirstX is like First, but panics if an error occurs. +func (_q *ProviderContactChannelQuery) FirstX(ctx context.Context) *ProviderContactChannel { + node, err := _q.First(ctx) + if err != nil && !IsNotFound(err) { + panic(err) + } + return node +} + +// FirstID returns the first ProviderContactChannel ID from the query. +// Returns a *NotFoundError when no ProviderContactChannel ID was found. +func (_q *ProviderContactChannelQuery) 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{providercontactchannel.Label} + return + } + return ids[0], nil +} + +// FirstIDX is like FirstID, but panics if an error occurs. +func (_q *ProviderContactChannelQuery) FirstIDX(ctx context.Context) uuid.UUID { + id, err := _q.FirstID(ctx) + if err != nil && !IsNotFound(err) { + panic(err) + } + return id +} + +// Only returns a single ProviderContactChannel entity found by the query, ensuring it only returns one. +// Returns a *NotSingularError when more than one ProviderContactChannel entity is found. +// Returns a *NotFoundError when no ProviderContactChannel entities are found. +func (_q *ProviderContactChannelQuery) Only(ctx context.Context) (*ProviderContactChannel, 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{providercontactchannel.Label} + default: + return nil, &NotSingularError{providercontactchannel.Label} + } +} + +// OnlyX is like Only, but panics if an error occurs. +func (_q *ProviderContactChannelQuery) OnlyX(ctx context.Context) *ProviderContactChannel { + node, err := _q.Only(ctx) + if err != nil { + panic(err) + } + return node +} + +// OnlyID is like Only, but returns the only ProviderContactChannel ID in the query. +// Returns a *NotSingularError when more than one ProviderContactChannel ID is found. +// Returns a *NotFoundError when no entities are found. +func (_q *ProviderContactChannelQuery) 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{providercontactchannel.Label} + default: + err = &NotSingularError{providercontactchannel.Label} + } + return +} + +// OnlyIDX is like OnlyID, but panics if an error occurs. +func (_q *ProviderContactChannelQuery) 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 ProviderContactChannels. +func (_q *ProviderContactChannelQuery) All(ctx context.Context) ([]*ProviderContactChannel, error) { + ctx = setContextOp(ctx, _q.ctx, ent.OpQueryAll) + if err := _q.prepareQuery(ctx); err != nil { + return nil, err + } + qr := querierAll[[]*ProviderContactChannel, *ProviderContactChannelQuery]() + return withInterceptors[[]*ProviderContactChannel](ctx, _q, qr, _q.inters) +} + +// AllX is like All, but panics if an error occurs. +func (_q *ProviderContactChannelQuery) AllX(ctx context.Context) []*ProviderContactChannel { + nodes, err := _q.All(ctx) + if err != nil { + panic(err) + } + return nodes +} + +// IDs executes the query and returns a list of ProviderContactChannel IDs. +func (_q *ProviderContactChannelQuery) 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(providercontactchannel.FieldID).Scan(ctx, &ids); err != nil { + return nil, err + } + return ids, nil +} + +// IDsX is like IDs, but panics if an error occurs. +func (_q *ProviderContactChannelQuery) 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 *ProviderContactChannelQuery) 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[*ProviderContactChannelQuery](), _q.inters) +} + +// CountX is like Count, but panics if an error occurs. +func (_q *ProviderContactChannelQuery) 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 *ProviderContactChannelQuery) 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 *ProviderContactChannelQuery) ExistX(ctx context.Context) bool { + exist, err := _q.Exist(ctx) + if err != nil { + panic(err) + } + return exist +} + +// Clone returns a duplicate of the ProviderContactChannelQuery 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 *ProviderContactChannelQuery) Clone() *ProviderContactChannelQuery { + if _q == nil { + return nil + } + return &ProviderContactChannelQuery{ + config: _q.config, + ctx: _q.ctx.Clone(), + order: append([]providercontactchannel.OrderOption{}, _q.order...), + inters: append([]Interceptor{}, _q.inters...), + predicates: append([]predicate.ProviderContactChannel{}, _q.predicates...), + // clone intermediate query. + sql: _q.sql.Clone(), + path: _q.path, + } +} + +// GroupBy is used to group vertices by one or more fields/columns. +// It is often used with aggregate functions, like: count, max, mean, min, sum. +// +// Example: +// +// var v []struct { +// InternalUserID uuid.UUID `json:"internal_user_id,omitempty"` +// Count int `json:"count,omitempty"` +// } +// +// client.ProviderContactChannel.Query(). +// GroupBy(providercontactchannel.FieldInternalUserID). +// Aggregate(ent.Count()). +// Scan(ctx, &v) +func (_q *ProviderContactChannelQuery) GroupBy(field string, fields ...string) *ProviderContactChannelGroupBy { + _q.ctx.Fields = append([]string{field}, fields...) + grbuild := &ProviderContactChannelGroupBy{build: _q} + grbuild.flds = &_q.ctx.Fields + grbuild.label = providercontactchannel.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 { +// InternalUserID uuid.UUID `json:"internal_user_id,omitempty"` +// } +// +// client.ProviderContactChannel.Query(). +// Select(providercontactchannel.FieldInternalUserID). +// Scan(ctx, &v) +func (_q *ProviderContactChannelQuery) Select(fields ...string) *ProviderContactChannelSelect { + _q.ctx.Fields = append(_q.ctx.Fields, fields...) + sbuild := &ProviderContactChannelSelect{ProviderContactChannelQuery: _q} + sbuild.label = providercontactchannel.Label + sbuild.flds, sbuild.scan = &_q.ctx.Fields, sbuild.Scan + return sbuild +} + +// Aggregate returns a ProviderContactChannelSelect configured with the given aggregations. +func (_q *ProviderContactChannelQuery) Aggregate(fns ...AggregateFunc) *ProviderContactChannelSelect { + return _q.Select().Aggregate(fns...) +} + +func (_q *ProviderContactChannelQuery) 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 !providercontactchannel.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 *ProviderContactChannelQuery) sqlAll(ctx context.Context, hooks ...queryHook) ([]*ProviderContactChannel, error) { + var ( + nodes = []*ProviderContactChannel{} + _spec = _q.querySpec() + ) + _spec.ScanValues = func(columns []string) ([]any, error) { + return (*ProviderContactChannel).scanValues(nil, columns) + } + _spec.Assign = func(columns []string, values []any) error { + node := &ProviderContactChannel{config: _q.config} + nodes = append(nodes, node) + return node.assignValues(columns, values) + } + for i := range hooks { + hooks[i](ctx, _spec) + } + if err := sqlgraph.QueryNodes(ctx, _q.driver, _spec); err != nil { + return nil, err + } + if len(nodes) == 0 { + return nodes, nil + } + return nodes, nil +} + +func (_q *ProviderContactChannelQuery) sqlCount(ctx context.Context) (int, error) { + _spec := _q.querySpec() + _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 *ProviderContactChannelQuery) querySpec() *sqlgraph.QuerySpec { + _spec := sqlgraph.NewQuerySpec(providercontactchannel.Table, providercontactchannel.Columns, sqlgraph.NewFieldSpec(providercontactchannel.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, providercontactchannel.FieldID) + for i := range fields { + if fields[i] != providercontactchannel.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 *ProviderContactChannelQuery) sqlQuery(ctx context.Context) *sql.Selector { + builder := sql.Dialect(_q.driver.Dialect()) + t1 := builder.Table(providercontactchannel.Table) + columns := _q.ctx.Fields + if len(columns) == 0 { + columns = providercontactchannel.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 _, 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 +} + +// ProviderContactChannelGroupBy is the group-by builder for ProviderContactChannel entities. +type ProviderContactChannelGroupBy struct { + selector + build *ProviderContactChannelQuery +} + +// Aggregate adds the given aggregation functions to the group-by query. +func (_g *ProviderContactChannelGroupBy) Aggregate(fns ...AggregateFunc) *ProviderContactChannelGroupBy { + _g.fns = append(_g.fns, fns...) + return _g +} + +// Scan applies the selector query and scans the result into the given value. +func (_g *ProviderContactChannelGroupBy) 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[*ProviderContactChannelQuery, *ProviderContactChannelGroupBy](ctx, _g.build, _g, _g.build.inters, v) +} + +func (_g *ProviderContactChannelGroupBy) sqlScan(ctx context.Context, root *ProviderContactChannelQuery, 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) +} + +// ProviderContactChannelSelect is the builder for selecting fields of ProviderContactChannel entities. +type ProviderContactChannelSelect struct { + *ProviderContactChannelQuery + selector +} + +// Aggregate adds the given aggregation functions to the selector query. +func (_s *ProviderContactChannelSelect) Aggregate(fns ...AggregateFunc) *ProviderContactChannelSelect { + _s.fns = append(_s.fns, fns...) + return _s +} + +// Scan applies the selector query and scans the result into the given value. +func (_s *ProviderContactChannelSelect) 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[*ProviderContactChannelQuery, *ProviderContactChannelSelect](ctx, _s.ProviderContactChannelQuery, _s, _s.inters, v) +} + +func (_s *ProviderContactChannelSelect) sqlScan(ctx context.Context, root *ProviderContactChannelQuery, 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/backend/ent/providercontactchannel_update.go b/backend/ent/providercontactchannel_update.go new file mode 100644 index 0000000..6d4804e --- /dev/null +++ b/backend/ent/providercontactchannel_update.go @@ -0,0 +1,280 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "context" + "errors" + "fmt" + "time" + + "entgo.io/ent/dialect/sql" + "entgo.io/ent/dialect/sql/sqlgraph" + "entgo.io/ent/schema/field" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/predicate" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/providercontactchannel" +) + +// ProviderContactChannelUpdate is the builder for updating ProviderContactChannel entities. +type ProviderContactChannelUpdate struct { + config + hooks []Hook + mutation *ProviderContactChannelMutation +} + +// Where appends a list predicates to the ProviderContactChannelUpdate builder. +func (_u *ProviderContactChannelUpdate) Where(ps ...predicate.ProviderContactChannel) *ProviderContactChannelUpdate { + _u.mutation.Where(ps...) + return _u +} + +// SetEnabled sets the "enabled" field. +func (_u *ProviderContactChannelUpdate) SetEnabled(v bool) *ProviderContactChannelUpdate { + _u.mutation.SetEnabled(v) + return _u +} + +// SetNillableEnabled sets the "enabled" field if the given value is not nil. +func (_u *ProviderContactChannelUpdate) SetNillableEnabled(v *bool) *ProviderContactChannelUpdate { + if v != nil { + _u.SetEnabled(*v) + } + return _u +} + +// SetRevealConsent sets the "reveal_consent" field. +func (_u *ProviderContactChannelUpdate) SetRevealConsent(v bool) *ProviderContactChannelUpdate { + _u.mutation.SetRevealConsent(v) + return _u +} + +// SetNillableRevealConsent sets the "reveal_consent" field if the given value is not nil. +func (_u *ProviderContactChannelUpdate) SetNillableRevealConsent(v *bool) *ProviderContactChannelUpdate { + if v != nil { + _u.SetRevealConsent(*v) + } + return _u +} + +// SetUpdatedAt sets the "updated_at" field. +func (_u *ProviderContactChannelUpdate) SetUpdatedAt(v time.Time) *ProviderContactChannelUpdate { + _u.mutation.SetUpdatedAt(v) + return _u +} + +// Mutation returns the ProviderContactChannelMutation object of the builder. +func (_u *ProviderContactChannelUpdate) Mutation() *ProviderContactChannelMutation { + return _u.mutation +} + +// Save executes the query and returns the number of nodes affected by the update operation. +func (_u *ProviderContactChannelUpdate) Save(ctx context.Context) (int, error) { + _u.defaults() + return withHooks(ctx, _u.sqlSave, _u.mutation, _u.hooks) +} + +// SaveX is like Save, but panics if an error occurs. +func (_u *ProviderContactChannelUpdate) SaveX(ctx context.Context) int { + affected, err := _u.Save(ctx) + if err != nil { + panic(err) + } + return affected +} + +// Exec executes the query. +func (_u *ProviderContactChannelUpdate) Exec(ctx context.Context) error { + _, err := _u.Save(ctx) + return err +} + +// ExecX is like Exec, but panics if an error occurs. +func (_u *ProviderContactChannelUpdate) ExecX(ctx context.Context) { + if err := _u.Exec(ctx); err != nil { + panic(err) + } +} + +// defaults sets the default values of the builder before save. +func (_u *ProviderContactChannelUpdate) defaults() { + if _, ok := _u.mutation.UpdatedAt(); !ok { + v := providercontactchannel.UpdateDefaultUpdatedAt() + _u.mutation.SetUpdatedAt(v) + } +} + +func (_u *ProviderContactChannelUpdate) sqlSave(ctx context.Context) (_node int, err error) { + _spec := sqlgraph.NewUpdateSpec(providercontactchannel.Table, providercontactchannel.Columns, sqlgraph.NewFieldSpec(providercontactchannel.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.Enabled(); ok { + _spec.SetField(providercontactchannel.FieldEnabled, field.TypeBool, value) + } + if value, ok := _u.mutation.RevealConsent(); ok { + _spec.SetField(providercontactchannel.FieldRevealConsent, field.TypeBool, value) + } + if value, ok := _u.mutation.UpdatedAt(); ok { + _spec.SetField(providercontactchannel.FieldUpdatedAt, field.TypeTime, value) + } + if _node, err = sqlgraph.UpdateNodes(ctx, _u.driver, _spec); err != nil { + if _, ok := err.(*sqlgraph.NotFoundError); ok { + err = &NotFoundError{providercontactchannel.Label} + } else if sqlgraph.IsConstraintError(err) { + err = &ConstraintError{msg: err.Error(), wrap: err} + } + return 0, err + } + _u.mutation.done = true + return _node, nil +} + +// ProviderContactChannelUpdateOne is the builder for updating a single ProviderContactChannel entity. +type ProviderContactChannelUpdateOne struct { + config + fields []string + hooks []Hook + mutation *ProviderContactChannelMutation +} + +// SetEnabled sets the "enabled" field. +func (_u *ProviderContactChannelUpdateOne) SetEnabled(v bool) *ProviderContactChannelUpdateOne { + _u.mutation.SetEnabled(v) + return _u +} + +// SetNillableEnabled sets the "enabled" field if the given value is not nil. +func (_u *ProviderContactChannelUpdateOne) SetNillableEnabled(v *bool) *ProviderContactChannelUpdateOne { + if v != nil { + _u.SetEnabled(*v) + } + return _u +} + +// SetRevealConsent sets the "reveal_consent" field. +func (_u *ProviderContactChannelUpdateOne) SetRevealConsent(v bool) *ProviderContactChannelUpdateOne { + _u.mutation.SetRevealConsent(v) + return _u +} + +// SetNillableRevealConsent sets the "reveal_consent" field if the given value is not nil. +func (_u *ProviderContactChannelUpdateOne) SetNillableRevealConsent(v *bool) *ProviderContactChannelUpdateOne { + if v != nil { + _u.SetRevealConsent(*v) + } + return _u +} + +// SetUpdatedAt sets the "updated_at" field. +func (_u *ProviderContactChannelUpdateOne) SetUpdatedAt(v time.Time) *ProviderContactChannelUpdateOne { + _u.mutation.SetUpdatedAt(v) + return _u +} + +// Mutation returns the ProviderContactChannelMutation object of the builder. +func (_u *ProviderContactChannelUpdateOne) Mutation() *ProviderContactChannelMutation { + return _u.mutation +} + +// Where appends a list predicates to the ProviderContactChannelUpdate builder. +func (_u *ProviderContactChannelUpdateOne) Where(ps ...predicate.ProviderContactChannel) *ProviderContactChannelUpdateOne { + _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 *ProviderContactChannelUpdateOne) Select(field string, fields ...string) *ProviderContactChannelUpdateOne { + _u.fields = append([]string{field}, fields...) + return _u +} + +// Save executes the query and returns the updated ProviderContactChannel entity. +func (_u *ProviderContactChannelUpdateOne) Save(ctx context.Context) (*ProviderContactChannel, error) { + _u.defaults() + return withHooks(ctx, _u.sqlSave, _u.mutation, _u.hooks) +} + +// SaveX is like Save, but panics if an error occurs. +func (_u *ProviderContactChannelUpdateOne) SaveX(ctx context.Context) *ProviderContactChannel { + node, err := _u.Save(ctx) + if err != nil { + panic(err) + } + return node +} + +// Exec executes the query on the entity. +func (_u *ProviderContactChannelUpdateOne) Exec(ctx context.Context) error { + _, err := _u.Save(ctx) + return err +} + +// ExecX is like Exec, but panics if an error occurs. +func (_u *ProviderContactChannelUpdateOne) ExecX(ctx context.Context) { + if err := _u.Exec(ctx); err != nil { + panic(err) + } +} + +// defaults sets the default values of the builder before save. +func (_u *ProviderContactChannelUpdateOne) defaults() { + if _, ok := _u.mutation.UpdatedAt(); !ok { + v := providercontactchannel.UpdateDefaultUpdatedAt() + _u.mutation.SetUpdatedAt(v) + } +} + +func (_u *ProviderContactChannelUpdateOne) sqlSave(ctx context.Context) (_node *ProviderContactChannel, err error) { + _spec := sqlgraph.NewUpdateSpec(providercontactchannel.Table, providercontactchannel.Columns, sqlgraph.NewFieldSpec(providercontactchannel.FieldID, field.TypeUUID)) + id, ok := _u.mutation.ID() + if !ok { + return nil, &ValidationError{Name: "id", err: errors.New(`ent: missing "ProviderContactChannel.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, providercontactchannel.FieldID) + for _, f := range fields { + if !providercontactchannel.ValidColumn(f) { + return nil, &ValidationError{Name: f, err: fmt.Errorf("ent: invalid field %q for query", f)} + } + if f != providercontactchannel.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.Enabled(); ok { + _spec.SetField(providercontactchannel.FieldEnabled, field.TypeBool, value) + } + if value, ok := _u.mutation.RevealConsent(); ok { + _spec.SetField(providercontactchannel.FieldRevealConsent, field.TypeBool, value) + } + if value, ok := _u.mutation.UpdatedAt(); ok { + _spec.SetField(providercontactchannel.FieldUpdatedAt, field.TypeTime, value) + } + _node = &ProviderContactChannel{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{providercontactchannel.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/backend/ent/providerprofile.go b/backend/ent/providerprofile.go new file mode 100644 index 0000000..00eff1d --- /dev/null +++ b/backend/ent/providerprofile.go @@ -0,0 +1,284 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "fmt" + "strings" + "time" + + "entgo.io/ent" + "entgo.io/ent/dialect/sql" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/providerprofile" + "github.com/google/uuid" +) + +// ProviderProfile is the model entity for the ProviderProfile schema. +type ProviderProfile struct { + config `json:"-"` + // ID of the ent. + ID uuid.UUID `json:"id,omitempty"` + // DisplayName holds the value of the "display_name" field. + DisplayName string `json:"display_name,omitempty"` + // ProviderType holds the value of the "provider_type" field. + ProviderType string `json:"provider_type,omitempty"` + // Bio holds the value of the "bio" field. + Bio string `json:"bio,omitempty"` + // PrimaryLocalityID holds the value of the "primary_locality_id" field. + PrimaryLocalityID uuid.UUID `json:"primary_locality_id,omitempty"` + // MaxTravelDistanceKm holds the value of the "max_travel_distance_km" field. + MaxTravelDistanceKm int `json:"max_travel_distance_km,omitempty"` + // TravelsToCustomer holds the value of the "travels_to_customer" field. + TravelsToCustomer bool `json:"travels_to_customer,omitempty"` + // ReceivesCustomer holds the value of the "receives_customer" field. + ReceivesCustomer bool `json:"receives_customer,omitempty"` + // RemoteServices holds the value of the "remote_services" field. + RemoteServices bool `json:"remote_services,omitempty"` + // CreatedAt holds the value of the "created_at" field. + CreatedAt time.Time `json:"created_at,omitempty"` + // UpdatedAt holds the value of the "updated_at" field. + UpdatedAt time.Time `json:"updated_at,omitempty"` + // Edges holds the relations/edges for other nodes in the graph. + // The values are being populated by the ProviderProfileQuery when eager-loading is set. + Edges ProviderProfileEdges `json:"edges"` + selectValues sql.SelectValues +} + +// ProviderProfileEdges holds the relations/edges for other nodes in the graph. +type ProviderProfileEdges struct { + // ServiceLocalities holds the value of the service_localities edge. + ServiceLocalities []*Locality `json:"service_localities,omitempty"` + // SpokenLanguages holds the value of the spoken_languages edge. + SpokenLanguages []*SpokenLanguage `json:"spoken_languages,omitempty"` + // ServiceLocalityLinks holds the value of the service_locality_links edge. + ServiceLocalityLinks []*ProviderServiceLocality `json:"service_locality_links,omitempty"` + // SpokenLanguageLinks holds the value of the spoken_language_links edge. + SpokenLanguageLinks []*ProviderSpokenLanguage `json:"spoken_language_links,omitempty"` + // loadedTypes holds the information for reporting if a + // type was loaded (or requested) in eager-loading or not. + loadedTypes [4]bool +} + +// ServiceLocalitiesOrErr returns the ServiceLocalities value or an error if the edge +// was not loaded in eager-loading. +func (e ProviderProfileEdges) ServiceLocalitiesOrErr() ([]*Locality, error) { + if e.loadedTypes[0] { + return e.ServiceLocalities, nil + } + return nil, &NotLoadedError{edge: "service_localities"} +} + +// SpokenLanguagesOrErr returns the SpokenLanguages value or an error if the edge +// was not loaded in eager-loading. +func (e ProviderProfileEdges) SpokenLanguagesOrErr() ([]*SpokenLanguage, error) { + if e.loadedTypes[1] { + return e.SpokenLanguages, nil + } + return nil, &NotLoadedError{edge: "spoken_languages"} +} + +// ServiceLocalityLinksOrErr returns the ServiceLocalityLinks value or an error if the edge +// was not loaded in eager-loading. +func (e ProviderProfileEdges) ServiceLocalityLinksOrErr() ([]*ProviderServiceLocality, error) { + if e.loadedTypes[2] { + return e.ServiceLocalityLinks, nil + } + return nil, &NotLoadedError{edge: "service_locality_links"} +} + +// SpokenLanguageLinksOrErr returns the SpokenLanguageLinks value or an error if the edge +// was not loaded in eager-loading. +func (e ProviderProfileEdges) SpokenLanguageLinksOrErr() ([]*ProviderSpokenLanguage, error) { + if e.loadedTypes[3] { + return e.SpokenLanguageLinks, nil + } + return nil, &NotLoadedError{edge: "spoken_language_links"} +} + +// scanValues returns the types for scanning values from sql.Rows. +func (*ProviderProfile) scanValues(columns []string) ([]any, error) { + values := make([]any, len(columns)) + for i := range columns { + switch columns[i] { + case providerprofile.FieldTravelsToCustomer, providerprofile.FieldReceivesCustomer, providerprofile.FieldRemoteServices: + values[i] = new(sql.NullBool) + case providerprofile.FieldMaxTravelDistanceKm: + values[i] = new(sql.NullInt64) + case providerprofile.FieldDisplayName, providerprofile.FieldProviderType, providerprofile.FieldBio: + values[i] = new(sql.NullString) + case providerprofile.FieldCreatedAt, providerprofile.FieldUpdatedAt: + values[i] = new(sql.NullTime) + case providerprofile.FieldID, providerprofile.FieldPrimaryLocalityID: + 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 ProviderProfile fields. +func (_m *ProviderProfile) 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 providerprofile.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 providerprofile.FieldDisplayName: + if value, ok := values[i].(*sql.NullString); !ok { + return fmt.Errorf("unexpected type %T for field display_name", values[i]) + } else if value.Valid { + _m.DisplayName = value.String + } + case providerprofile.FieldProviderType: + if value, ok := values[i].(*sql.NullString); !ok { + return fmt.Errorf("unexpected type %T for field provider_type", values[i]) + } else if value.Valid { + _m.ProviderType = value.String + } + case providerprofile.FieldBio: + if value, ok := values[i].(*sql.NullString); !ok { + return fmt.Errorf("unexpected type %T for field bio", values[i]) + } else if value.Valid { + _m.Bio = value.String + } + case providerprofile.FieldPrimaryLocalityID: + if value, ok := values[i].(*uuid.UUID); !ok { + return fmt.Errorf("unexpected type %T for field primary_locality_id", values[i]) + } else if value != nil { + _m.PrimaryLocalityID = *value + } + case providerprofile.FieldMaxTravelDistanceKm: + if value, ok := values[i].(*sql.NullInt64); !ok { + return fmt.Errorf("unexpected type %T for field max_travel_distance_km", values[i]) + } else if value.Valid { + _m.MaxTravelDistanceKm = int(value.Int64) + } + case providerprofile.FieldTravelsToCustomer: + if value, ok := values[i].(*sql.NullBool); !ok { + return fmt.Errorf("unexpected type %T for field travels_to_customer", values[i]) + } else if value.Valid { + _m.TravelsToCustomer = value.Bool + } + case providerprofile.FieldReceivesCustomer: + if value, ok := values[i].(*sql.NullBool); !ok { + return fmt.Errorf("unexpected type %T for field receives_customer", values[i]) + } else if value.Valid { + _m.ReceivesCustomer = value.Bool + } + case providerprofile.FieldRemoteServices: + if value, ok := values[i].(*sql.NullBool); !ok { + return fmt.Errorf("unexpected type %T for field remote_services", values[i]) + } else if value.Valid { + _m.RemoteServices = value.Bool + } + case providerprofile.FieldCreatedAt: + if value, ok := values[i].(*sql.NullTime); !ok { + return fmt.Errorf("unexpected type %T for field created_at", values[i]) + } else if value.Valid { + _m.CreatedAt = value.Time + } + case providerprofile.FieldUpdatedAt: + if value, ok := values[i].(*sql.NullTime); !ok { + return fmt.Errorf("unexpected type %T for field updated_at", values[i]) + } else if value.Valid { + _m.UpdatedAt = value.Time + } + default: + _m.selectValues.Set(columns[i], values[i]) + } + } + return nil +} + +// Value returns the ent.Value that was dynamically selected and assigned to the ProviderProfile. +// This includes values selected through modifiers, order, etc. +func (_m *ProviderProfile) Value(name string) (ent.Value, error) { + return _m.selectValues.Get(name) +} + +// QueryServiceLocalities queries the "service_localities" edge of the ProviderProfile entity. +func (_m *ProviderProfile) QueryServiceLocalities() *LocalityQuery { + return NewProviderProfileClient(_m.config).QueryServiceLocalities(_m) +} + +// QuerySpokenLanguages queries the "spoken_languages" edge of the ProviderProfile entity. +func (_m *ProviderProfile) QuerySpokenLanguages() *SpokenLanguageQuery { + return NewProviderProfileClient(_m.config).QuerySpokenLanguages(_m) +} + +// QueryServiceLocalityLinks queries the "service_locality_links" edge of the ProviderProfile entity. +func (_m *ProviderProfile) QueryServiceLocalityLinks() *ProviderServiceLocalityQuery { + return NewProviderProfileClient(_m.config).QueryServiceLocalityLinks(_m) +} + +// QuerySpokenLanguageLinks queries the "spoken_language_links" edge of the ProviderProfile entity. +func (_m *ProviderProfile) QuerySpokenLanguageLinks() *ProviderSpokenLanguageQuery { + return NewProviderProfileClient(_m.config).QuerySpokenLanguageLinks(_m) +} + +// Update returns a builder for updating this ProviderProfile. +// Note that you need to call ProviderProfile.Unwrap() before calling this method if this ProviderProfile +// was returned from a transaction, and the transaction was committed or rolled back. +func (_m *ProviderProfile) Update() *ProviderProfileUpdateOne { + return NewProviderProfileClient(_m.config).UpdateOne(_m) +} + +// Unwrap unwraps the ProviderProfile 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 *ProviderProfile) Unwrap() *ProviderProfile { + _tx, ok := _m.config.driver.(*txDriver) + if !ok { + panic("ent: ProviderProfile is not a transactional entity") + } + _m.config.driver = _tx.drv + return _m +} + +// String implements the fmt.Stringer. +func (_m *ProviderProfile) String() string { + var builder strings.Builder + builder.WriteString("ProviderProfile(") + builder.WriteString(fmt.Sprintf("id=%v, ", _m.ID)) + builder.WriteString("display_name=") + builder.WriteString(_m.DisplayName) + builder.WriteString(", ") + builder.WriteString("provider_type=") + builder.WriteString(_m.ProviderType) + builder.WriteString(", ") + builder.WriteString("bio=") + builder.WriteString(_m.Bio) + builder.WriteString(", ") + builder.WriteString("primary_locality_id=") + builder.WriteString(fmt.Sprintf("%v", _m.PrimaryLocalityID)) + builder.WriteString(", ") + builder.WriteString("max_travel_distance_km=") + builder.WriteString(fmt.Sprintf("%v", _m.MaxTravelDistanceKm)) + builder.WriteString(", ") + builder.WriteString("travels_to_customer=") + builder.WriteString(fmt.Sprintf("%v", _m.TravelsToCustomer)) + builder.WriteString(", ") + builder.WriteString("receives_customer=") + builder.WriteString(fmt.Sprintf("%v", _m.ReceivesCustomer)) + builder.WriteString(", ") + builder.WriteString("remote_services=") + builder.WriteString(fmt.Sprintf("%v", _m.RemoteServices)) + builder.WriteString(", ") + builder.WriteString("created_at=") + builder.WriteString(_m.CreatedAt.Format(time.ANSIC)) + builder.WriteString(", ") + builder.WriteString("updated_at=") + builder.WriteString(_m.UpdatedAt.Format(time.ANSIC)) + builder.WriteByte(')') + return builder.String() +} + +// ProviderProfiles is a parsable slice of ProviderProfile. +type ProviderProfiles []*ProviderProfile diff --git a/backend/ent/providerprofile/providerprofile.go b/backend/ent/providerprofile/providerprofile.go new file mode 100644 index 0000000..dc56bb3 --- /dev/null +++ b/backend/ent/providerprofile/providerprofile.go @@ -0,0 +1,274 @@ +// Code generated by ent, DO NOT EDIT. + +package providerprofile + +import ( + "time" + + "entgo.io/ent/dialect/sql" + "entgo.io/ent/dialect/sql/sqlgraph" +) + +const ( + // Label holds the string label denoting the providerprofile type in the database. + Label = "provider_profile" + // FieldID holds the string denoting the id field in the database. + FieldID = "internal_user_id" + // FieldDisplayName holds the string denoting the display_name field in the database. + FieldDisplayName = "display_name" + // FieldProviderType holds the string denoting the provider_type field in the database. + FieldProviderType = "provider_type" + // FieldBio holds the string denoting the bio field in the database. + FieldBio = "bio" + // FieldPrimaryLocalityID holds the string denoting the primary_locality_id field in the database. + FieldPrimaryLocalityID = "primary_locality_id" + // FieldMaxTravelDistanceKm holds the string denoting the max_travel_distance_km field in the database. + FieldMaxTravelDistanceKm = "max_travel_distance_km" + // FieldTravelsToCustomer holds the string denoting the travels_to_customer field in the database. + FieldTravelsToCustomer = "travels_to_customer" + // FieldReceivesCustomer holds the string denoting the receives_customer field in the database. + FieldReceivesCustomer = "receives_customer" + // FieldRemoteServices holds the string denoting the remote_services field in the database. + FieldRemoteServices = "remote_services" + // FieldCreatedAt holds the string denoting the created_at field in the database. + FieldCreatedAt = "created_at" + // FieldUpdatedAt holds the string denoting the updated_at field in the database. + FieldUpdatedAt = "updated_at" + // EdgeServiceLocalities holds the string denoting the service_localities edge name in mutations. + EdgeServiceLocalities = "service_localities" + // EdgeSpokenLanguages holds the string denoting the spoken_languages edge name in mutations. + EdgeSpokenLanguages = "spoken_languages" + // EdgeServiceLocalityLinks holds the string denoting the service_locality_links edge name in mutations. + EdgeServiceLocalityLinks = "service_locality_links" + // EdgeSpokenLanguageLinks holds the string denoting the spoken_language_links edge name in mutations. + EdgeSpokenLanguageLinks = "spoken_language_links" + // LocalityFieldID holds the string denoting the ID field of the Locality. + LocalityFieldID = "id" + // SpokenLanguageFieldID holds the string denoting the ID field of the SpokenLanguage. + SpokenLanguageFieldID = "id" + // Table holds the table name of the providerprofile in the database. + Table = "provider_profiles" + // ServiceLocalitiesTable is the table that holds the service_localities relation/edge. The primary key declared below. + ServiceLocalitiesTable = "provider_service_localities" + // ServiceLocalitiesInverseTable is the table name for the Locality entity. + // It exists in this package in order to avoid circular dependency with the "locality" package. + ServiceLocalitiesInverseTable = "localities" + // SpokenLanguagesTable is the table that holds the spoken_languages relation/edge. The primary key declared below. + SpokenLanguagesTable = "provider_spoken_languages" + // SpokenLanguagesInverseTable is the table name for the SpokenLanguage entity. + // It exists in this package in order to avoid circular dependency with the "spokenlanguage" package. + SpokenLanguagesInverseTable = "spoken_languages" + // ServiceLocalityLinksTable is the table that holds the service_locality_links relation/edge. + ServiceLocalityLinksTable = "provider_service_localities" + // ServiceLocalityLinksInverseTable is the table name for the ProviderServiceLocality entity. + // It exists in this package in order to avoid circular dependency with the "providerservicelocality" package. + ServiceLocalityLinksInverseTable = "provider_service_localities" + // ServiceLocalityLinksColumn is the table column denoting the service_locality_links relation/edge. + ServiceLocalityLinksColumn = "internal_user_id" + // SpokenLanguageLinksTable is the table that holds the spoken_language_links relation/edge. + SpokenLanguageLinksTable = "provider_spoken_languages" + // SpokenLanguageLinksInverseTable is the table name for the ProviderSpokenLanguage entity. + // It exists in this package in order to avoid circular dependency with the "providerspokenlanguage" package. + SpokenLanguageLinksInverseTable = "provider_spoken_languages" + // SpokenLanguageLinksColumn is the table column denoting the spoken_language_links relation/edge. + SpokenLanguageLinksColumn = "internal_user_id" +) + +// Columns holds all SQL columns for providerprofile fields. +var Columns = []string{ + FieldID, + FieldDisplayName, + FieldProviderType, + FieldBio, + FieldPrimaryLocalityID, + FieldMaxTravelDistanceKm, + FieldTravelsToCustomer, + FieldReceivesCustomer, + FieldRemoteServices, + FieldCreatedAt, + FieldUpdatedAt, +} + +var ( + // ServiceLocalitiesPrimaryKey and ServiceLocalitiesColumn2 are the table columns denoting the + // primary key for the service_localities relation (M2M). + ServiceLocalitiesPrimaryKey = []string{"internal_user_id", "locality_id"} + // SpokenLanguagesPrimaryKey and SpokenLanguagesColumn2 are the table columns denoting the + // primary key for the spoken_languages relation (M2M). + SpokenLanguagesPrimaryKey = []string{"internal_user_id", "language_code"} +) + +// 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 ( + // DisplayNameValidator is a validator for the "display_name" field. It is called by the builders before save. + DisplayNameValidator func(string) error + // ProviderTypeValidator is a validator for the "provider_type" field. It is called by the builders before save. + ProviderTypeValidator func(string) error + // BioValidator is a validator for the "bio" field. It is called by the builders before save. + BioValidator func(string) error + // MaxTravelDistanceKmValidator is a validator for the "max_travel_distance_km" field. It is called by the builders before save. + MaxTravelDistanceKmValidator func(int) error + // DefaultTravelsToCustomer holds the default value on creation for the "travels_to_customer" field. + DefaultTravelsToCustomer bool + // DefaultReceivesCustomer holds the default value on creation for the "receives_customer" field. + DefaultReceivesCustomer bool + // DefaultRemoteServices holds the default value on creation for the "remote_services" field. + DefaultRemoteServices bool + // DefaultCreatedAt holds the default value on creation for the "created_at" field. + DefaultCreatedAt func() time.Time + // DefaultUpdatedAt holds the default value on creation for the "updated_at" field. + DefaultUpdatedAt func() time.Time + // UpdateDefaultUpdatedAt holds the default value on update for the "updated_at" field. + UpdateDefaultUpdatedAt func() time.Time +) + +// OrderOption defines the ordering options for the ProviderProfile 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() +} + +// ByDisplayName orders the results by the display_name field. +func ByDisplayName(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldDisplayName, opts...).ToFunc() +} + +// ByProviderType orders the results by the provider_type field. +func ByProviderType(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldProviderType, opts...).ToFunc() +} + +// ByBio orders the results by the bio field. +func ByBio(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldBio, opts...).ToFunc() +} + +// ByPrimaryLocalityID orders the results by the primary_locality_id field. +func ByPrimaryLocalityID(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldPrimaryLocalityID, opts...).ToFunc() +} + +// ByMaxTravelDistanceKm orders the results by the max_travel_distance_km field. +func ByMaxTravelDistanceKm(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldMaxTravelDistanceKm, opts...).ToFunc() +} + +// ByTravelsToCustomer orders the results by the travels_to_customer field. +func ByTravelsToCustomer(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldTravelsToCustomer, opts...).ToFunc() +} + +// ByReceivesCustomer orders the results by the receives_customer field. +func ByReceivesCustomer(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldReceivesCustomer, opts...).ToFunc() +} + +// ByRemoteServices orders the results by the remote_services field. +func ByRemoteServices(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldRemoteServices, opts...).ToFunc() +} + +// ByCreatedAt orders the results by the created_at field. +func ByCreatedAt(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldCreatedAt, opts...).ToFunc() +} + +// ByUpdatedAt orders the results by the updated_at field. +func ByUpdatedAt(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldUpdatedAt, opts...).ToFunc() +} + +// ByServiceLocalitiesCount orders the results by service_localities count. +func ByServiceLocalitiesCount(opts ...sql.OrderTermOption) OrderOption { + return func(s *sql.Selector) { + sqlgraph.OrderByNeighborsCount(s, newServiceLocalitiesStep(), opts...) + } +} + +// ByServiceLocalities orders the results by service_localities terms. +func ByServiceLocalities(term sql.OrderTerm, terms ...sql.OrderTerm) OrderOption { + return func(s *sql.Selector) { + sqlgraph.OrderByNeighborTerms(s, newServiceLocalitiesStep(), append([]sql.OrderTerm{term}, terms...)...) + } +} + +// BySpokenLanguagesCount orders the results by spoken_languages count. +func BySpokenLanguagesCount(opts ...sql.OrderTermOption) OrderOption { + return func(s *sql.Selector) { + sqlgraph.OrderByNeighborsCount(s, newSpokenLanguagesStep(), opts...) + } +} + +// BySpokenLanguages orders the results by spoken_languages terms. +func BySpokenLanguages(term sql.OrderTerm, terms ...sql.OrderTerm) OrderOption { + return func(s *sql.Selector) { + sqlgraph.OrderByNeighborTerms(s, newSpokenLanguagesStep(), append([]sql.OrderTerm{term}, terms...)...) + } +} + +// ByServiceLocalityLinksCount orders the results by service_locality_links count. +func ByServiceLocalityLinksCount(opts ...sql.OrderTermOption) OrderOption { + return func(s *sql.Selector) { + sqlgraph.OrderByNeighborsCount(s, newServiceLocalityLinksStep(), opts...) + } +} + +// ByServiceLocalityLinks orders the results by service_locality_links terms. +func ByServiceLocalityLinks(term sql.OrderTerm, terms ...sql.OrderTerm) OrderOption { + return func(s *sql.Selector) { + sqlgraph.OrderByNeighborTerms(s, newServiceLocalityLinksStep(), append([]sql.OrderTerm{term}, terms...)...) + } +} + +// BySpokenLanguageLinksCount orders the results by spoken_language_links count. +func BySpokenLanguageLinksCount(opts ...sql.OrderTermOption) OrderOption { + return func(s *sql.Selector) { + sqlgraph.OrderByNeighborsCount(s, newSpokenLanguageLinksStep(), opts...) + } +} + +// BySpokenLanguageLinks orders the results by spoken_language_links terms. +func BySpokenLanguageLinks(term sql.OrderTerm, terms ...sql.OrderTerm) OrderOption { + return func(s *sql.Selector) { + sqlgraph.OrderByNeighborTerms(s, newSpokenLanguageLinksStep(), append([]sql.OrderTerm{term}, terms...)...) + } +} +func newServiceLocalitiesStep() *sqlgraph.Step { + return sqlgraph.NewStep( + sqlgraph.From(Table, FieldID), + sqlgraph.To(ServiceLocalitiesInverseTable, LocalityFieldID), + sqlgraph.Edge(sqlgraph.M2M, false, ServiceLocalitiesTable, ServiceLocalitiesPrimaryKey...), + ) +} +func newSpokenLanguagesStep() *sqlgraph.Step { + return sqlgraph.NewStep( + sqlgraph.From(Table, FieldID), + sqlgraph.To(SpokenLanguagesInverseTable, SpokenLanguageFieldID), + sqlgraph.Edge(sqlgraph.M2M, false, SpokenLanguagesTable, SpokenLanguagesPrimaryKey...), + ) +} +func newServiceLocalityLinksStep() *sqlgraph.Step { + return sqlgraph.NewStep( + sqlgraph.From(Table, FieldID), + sqlgraph.To(ServiceLocalityLinksInverseTable, ServiceLocalityLinksColumn), + sqlgraph.Edge(sqlgraph.O2M, true, ServiceLocalityLinksTable, ServiceLocalityLinksColumn), + ) +} +func newSpokenLanguageLinksStep() *sqlgraph.Step { + return sqlgraph.NewStep( + sqlgraph.From(Table, FieldID), + sqlgraph.To(SpokenLanguageLinksInverseTable, SpokenLanguageLinksColumn), + sqlgraph.Edge(sqlgraph.O2M, true, SpokenLanguageLinksTable, SpokenLanguageLinksColumn), + ) +} diff --git a/backend/ent/providerprofile/where.go b/backend/ent/providerprofile/where.go new file mode 100644 index 0000000..4bfc35d --- /dev/null +++ b/backend/ent/providerprofile/where.go @@ -0,0 +1,599 @@ +// Code generated by ent, DO NOT EDIT. + +package providerprofile + +import ( + "time" + + "entgo.io/ent/dialect/sql" + "entgo.io/ent/dialect/sql/sqlgraph" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/predicate" + "github.com/google/uuid" +) + +// ID filters vertices based on their ID field. +func ID(id uuid.UUID) predicate.ProviderProfile { + return predicate.ProviderProfile(sql.FieldEQ(FieldID, id)) +} + +// IDEQ applies the EQ predicate on the ID field. +func IDEQ(id uuid.UUID) predicate.ProviderProfile { + return predicate.ProviderProfile(sql.FieldEQ(FieldID, id)) +} + +// IDNEQ applies the NEQ predicate on the ID field. +func IDNEQ(id uuid.UUID) predicate.ProviderProfile { + return predicate.ProviderProfile(sql.FieldNEQ(FieldID, id)) +} + +// IDIn applies the In predicate on the ID field. +func IDIn(ids ...uuid.UUID) predicate.ProviderProfile { + return predicate.ProviderProfile(sql.FieldIn(FieldID, ids...)) +} + +// IDNotIn applies the NotIn predicate on the ID field. +func IDNotIn(ids ...uuid.UUID) predicate.ProviderProfile { + return predicate.ProviderProfile(sql.FieldNotIn(FieldID, ids...)) +} + +// IDGT applies the GT predicate on the ID field. +func IDGT(id uuid.UUID) predicate.ProviderProfile { + return predicate.ProviderProfile(sql.FieldGT(FieldID, id)) +} + +// IDGTE applies the GTE predicate on the ID field. +func IDGTE(id uuid.UUID) predicate.ProviderProfile { + return predicate.ProviderProfile(sql.FieldGTE(FieldID, id)) +} + +// IDLT applies the LT predicate on the ID field. +func IDLT(id uuid.UUID) predicate.ProviderProfile { + return predicate.ProviderProfile(sql.FieldLT(FieldID, id)) +} + +// IDLTE applies the LTE predicate on the ID field. +func IDLTE(id uuid.UUID) predicate.ProviderProfile { + return predicate.ProviderProfile(sql.FieldLTE(FieldID, id)) +} + +// DisplayName applies equality check predicate on the "display_name" field. It's identical to DisplayNameEQ. +func DisplayName(v string) predicate.ProviderProfile { + return predicate.ProviderProfile(sql.FieldEQ(FieldDisplayName, v)) +} + +// ProviderType applies equality check predicate on the "provider_type" field. It's identical to ProviderTypeEQ. +func ProviderType(v string) predicate.ProviderProfile { + return predicate.ProviderProfile(sql.FieldEQ(FieldProviderType, v)) +} + +// Bio applies equality check predicate on the "bio" field. It's identical to BioEQ. +func Bio(v string) predicate.ProviderProfile { + return predicate.ProviderProfile(sql.FieldEQ(FieldBio, v)) +} + +// PrimaryLocalityID applies equality check predicate on the "primary_locality_id" field. It's identical to PrimaryLocalityIDEQ. +func PrimaryLocalityID(v uuid.UUID) predicate.ProviderProfile { + return predicate.ProviderProfile(sql.FieldEQ(FieldPrimaryLocalityID, v)) +} + +// MaxTravelDistanceKm applies equality check predicate on the "max_travel_distance_km" field. It's identical to MaxTravelDistanceKmEQ. +func MaxTravelDistanceKm(v int) predicate.ProviderProfile { + return predicate.ProviderProfile(sql.FieldEQ(FieldMaxTravelDistanceKm, v)) +} + +// TravelsToCustomer applies equality check predicate on the "travels_to_customer" field. It's identical to TravelsToCustomerEQ. +func TravelsToCustomer(v bool) predicate.ProviderProfile { + return predicate.ProviderProfile(sql.FieldEQ(FieldTravelsToCustomer, v)) +} + +// ReceivesCustomer applies equality check predicate on the "receives_customer" field. It's identical to ReceivesCustomerEQ. +func ReceivesCustomer(v bool) predicate.ProviderProfile { + return predicate.ProviderProfile(sql.FieldEQ(FieldReceivesCustomer, v)) +} + +// RemoteServices applies equality check predicate on the "remote_services" field. It's identical to RemoteServicesEQ. +func RemoteServices(v bool) predicate.ProviderProfile { + return predicate.ProviderProfile(sql.FieldEQ(FieldRemoteServices, v)) +} + +// CreatedAt applies equality check predicate on the "created_at" field. It's identical to CreatedAtEQ. +func CreatedAt(v time.Time) predicate.ProviderProfile { + return predicate.ProviderProfile(sql.FieldEQ(FieldCreatedAt, v)) +} + +// UpdatedAt applies equality check predicate on the "updated_at" field. It's identical to UpdatedAtEQ. +func UpdatedAt(v time.Time) predicate.ProviderProfile { + return predicate.ProviderProfile(sql.FieldEQ(FieldUpdatedAt, v)) +} + +// DisplayNameEQ applies the EQ predicate on the "display_name" field. +func DisplayNameEQ(v string) predicate.ProviderProfile { + return predicate.ProviderProfile(sql.FieldEQ(FieldDisplayName, v)) +} + +// DisplayNameNEQ applies the NEQ predicate on the "display_name" field. +func DisplayNameNEQ(v string) predicate.ProviderProfile { + return predicate.ProviderProfile(sql.FieldNEQ(FieldDisplayName, v)) +} + +// DisplayNameIn applies the In predicate on the "display_name" field. +func DisplayNameIn(vs ...string) predicate.ProviderProfile { + return predicate.ProviderProfile(sql.FieldIn(FieldDisplayName, vs...)) +} + +// DisplayNameNotIn applies the NotIn predicate on the "display_name" field. +func DisplayNameNotIn(vs ...string) predicate.ProviderProfile { + return predicate.ProviderProfile(sql.FieldNotIn(FieldDisplayName, vs...)) +} + +// DisplayNameGT applies the GT predicate on the "display_name" field. +func DisplayNameGT(v string) predicate.ProviderProfile { + return predicate.ProviderProfile(sql.FieldGT(FieldDisplayName, v)) +} + +// DisplayNameGTE applies the GTE predicate on the "display_name" field. +func DisplayNameGTE(v string) predicate.ProviderProfile { + return predicate.ProviderProfile(sql.FieldGTE(FieldDisplayName, v)) +} + +// DisplayNameLT applies the LT predicate on the "display_name" field. +func DisplayNameLT(v string) predicate.ProviderProfile { + return predicate.ProviderProfile(sql.FieldLT(FieldDisplayName, v)) +} + +// DisplayNameLTE applies the LTE predicate on the "display_name" field. +func DisplayNameLTE(v string) predicate.ProviderProfile { + return predicate.ProviderProfile(sql.FieldLTE(FieldDisplayName, v)) +} + +// DisplayNameContains applies the Contains predicate on the "display_name" field. +func DisplayNameContains(v string) predicate.ProviderProfile { + return predicate.ProviderProfile(sql.FieldContains(FieldDisplayName, v)) +} + +// DisplayNameHasPrefix applies the HasPrefix predicate on the "display_name" field. +func DisplayNameHasPrefix(v string) predicate.ProviderProfile { + return predicate.ProviderProfile(sql.FieldHasPrefix(FieldDisplayName, v)) +} + +// DisplayNameHasSuffix applies the HasSuffix predicate on the "display_name" field. +func DisplayNameHasSuffix(v string) predicate.ProviderProfile { + return predicate.ProviderProfile(sql.FieldHasSuffix(FieldDisplayName, v)) +} + +// DisplayNameEqualFold applies the EqualFold predicate on the "display_name" field. +func DisplayNameEqualFold(v string) predicate.ProviderProfile { + return predicate.ProviderProfile(sql.FieldEqualFold(FieldDisplayName, v)) +} + +// DisplayNameContainsFold applies the ContainsFold predicate on the "display_name" field. +func DisplayNameContainsFold(v string) predicate.ProviderProfile { + return predicate.ProviderProfile(sql.FieldContainsFold(FieldDisplayName, v)) +} + +// ProviderTypeEQ applies the EQ predicate on the "provider_type" field. +func ProviderTypeEQ(v string) predicate.ProviderProfile { + return predicate.ProviderProfile(sql.FieldEQ(FieldProviderType, v)) +} + +// ProviderTypeNEQ applies the NEQ predicate on the "provider_type" field. +func ProviderTypeNEQ(v string) predicate.ProviderProfile { + return predicate.ProviderProfile(sql.FieldNEQ(FieldProviderType, v)) +} + +// ProviderTypeIn applies the In predicate on the "provider_type" field. +func ProviderTypeIn(vs ...string) predicate.ProviderProfile { + return predicate.ProviderProfile(sql.FieldIn(FieldProviderType, vs...)) +} + +// ProviderTypeNotIn applies the NotIn predicate on the "provider_type" field. +func ProviderTypeNotIn(vs ...string) predicate.ProviderProfile { + return predicate.ProviderProfile(sql.FieldNotIn(FieldProviderType, vs...)) +} + +// ProviderTypeGT applies the GT predicate on the "provider_type" field. +func ProviderTypeGT(v string) predicate.ProviderProfile { + return predicate.ProviderProfile(sql.FieldGT(FieldProviderType, v)) +} + +// ProviderTypeGTE applies the GTE predicate on the "provider_type" field. +func ProviderTypeGTE(v string) predicate.ProviderProfile { + return predicate.ProviderProfile(sql.FieldGTE(FieldProviderType, v)) +} + +// ProviderTypeLT applies the LT predicate on the "provider_type" field. +func ProviderTypeLT(v string) predicate.ProviderProfile { + return predicate.ProviderProfile(sql.FieldLT(FieldProviderType, v)) +} + +// ProviderTypeLTE applies the LTE predicate on the "provider_type" field. +func ProviderTypeLTE(v string) predicate.ProviderProfile { + return predicate.ProviderProfile(sql.FieldLTE(FieldProviderType, v)) +} + +// ProviderTypeContains applies the Contains predicate on the "provider_type" field. +func ProviderTypeContains(v string) predicate.ProviderProfile { + return predicate.ProviderProfile(sql.FieldContains(FieldProviderType, v)) +} + +// ProviderTypeHasPrefix applies the HasPrefix predicate on the "provider_type" field. +func ProviderTypeHasPrefix(v string) predicate.ProviderProfile { + return predicate.ProviderProfile(sql.FieldHasPrefix(FieldProviderType, v)) +} + +// ProviderTypeHasSuffix applies the HasSuffix predicate on the "provider_type" field. +func ProviderTypeHasSuffix(v string) predicate.ProviderProfile { + return predicate.ProviderProfile(sql.FieldHasSuffix(FieldProviderType, v)) +} + +// ProviderTypeEqualFold applies the EqualFold predicate on the "provider_type" field. +func ProviderTypeEqualFold(v string) predicate.ProviderProfile { + return predicate.ProviderProfile(sql.FieldEqualFold(FieldProviderType, v)) +} + +// ProviderTypeContainsFold applies the ContainsFold predicate on the "provider_type" field. +func ProviderTypeContainsFold(v string) predicate.ProviderProfile { + return predicate.ProviderProfile(sql.FieldContainsFold(FieldProviderType, v)) +} + +// BioEQ applies the EQ predicate on the "bio" field. +func BioEQ(v string) predicate.ProviderProfile { + return predicate.ProviderProfile(sql.FieldEQ(FieldBio, v)) +} + +// BioNEQ applies the NEQ predicate on the "bio" field. +func BioNEQ(v string) predicate.ProviderProfile { + return predicate.ProviderProfile(sql.FieldNEQ(FieldBio, v)) +} + +// BioIn applies the In predicate on the "bio" field. +func BioIn(vs ...string) predicate.ProviderProfile { + return predicate.ProviderProfile(sql.FieldIn(FieldBio, vs...)) +} + +// BioNotIn applies the NotIn predicate on the "bio" field. +func BioNotIn(vs ...string) predicate.ProviderProfile { + return predicate.ProviderProfile(sql.FieldNotIn(FieldBio, vs...)) +} + +// BioGT applies the GT predicate on the "bio" field. +func BioGT(v string) predicate.ProviderProfile { + return predicate.ProviderProfile(sql.FieldGT(FieldBio, v)) +} + +// BioGTE applies the GTE predicate on the "bio" field. +func BioGTE(v string) predicate.ProviderProfile { + return predicate.ProviderProfile(sql.FieldGTE(FieldBio, v)) +} + +// BioLT applies the LT predicate on the "bio" field. +func BioLT(v string) predicate.ProviderProfile { + return predicate.ProviderProfile(sql.FieldLT(FieldBio, v)) +} + +// BioLTE applies the LTE predicate on the "bio" field. +func BioLTE(v string) predicate.ProviderProfile { + return predicate.ProviderProfile(sql.FieldLTE(FieldBio, v)) +} + +// BioContains applies the Contains predicate on the "bio" field. +func BioContains(v string) predicate.ProviderProfile { + return predicate.ProviderProfile(sql.FieldContains(FieldBio, v)) +} + +// BioHasPrefix applies the HasPrefix predicate on the "bio" field. +func BioHasPrefix(v string) predicate.ProviderProfile { + return predicate.ProviderProfile(sql.FieldHasPrefix(FieldBio, v)) +} + +// BioHasSuffix applies the HasSuffix predicate on the "bio" field. +func BioHasSuffix(v string) predicate.ProviderProfile { + return predicate.ProviderProfile(sql.FieldHasSuffix(FieldBio, v)) +} + +// BioEqualFold applies the EqualFold predicate on the "bio" field. +func BioEqualFold(v string) predicate.ProviderProfile { + return predicate.ProviderProfile(sql.FieldEqualFold(FieldBio, v)) +} + +// BioContainsFold applies the ContainsFold predicate on the "bio" field. +func BioContainsFold(v string) predicate.ProviderProfile { + return predicate.ProviderProfile(sql.FieldContainsFold(FieldBio, v)) +} + +// PrimaryLocalityIDEQ applies the EQ predicate on the "primary_locality_id" field. +func PrimaryLocalityIDEQ(v uuid.UUID) predicate.ProviderProfile { + return predicate.ProviderProfile(sql.FieldEQ(FieldPrimaryLocalityID, v)) +} + +// PrimaryLocalityIDNEQ applies the NEQ predicate on the "primary_locality_id" field. +func PrimaryLocalityIDNEQ(v uuid.UUID) predicate.ProviderProfile { + return predicate.ProviderProfile(sql.FieldNEQ(FieldPrimaryLocalityID, v)) +} + +// PrimaryLocalityIDIn applies the In predicate on the "primary_locality_id" field. +func PrimaryLocalityIDIn(vs ...uuid.UUID) predicate.ProviderProfile { + return predicate.ProviderProfile(sql.FieldIn(FieldPrimaryLocalityID, vs...)) +} + +// PrimaryLocalityIDNotIn applies the NotIn predicate on the "primary_locality_id" field. +func PrimaryLocalityIDNotIn(vs ...uuid.UUID) predicate.ProviderProfile { + return predicate.ProviderProfile(sql.FieldNotIn(FieldPrimaryLocalityID, vs...)) +} + +// PrimaryLocalityIDGT applies the GT predicate on the "primary_locality_id" field. +func PrimaryLocalityIDGT(v uuid.UUID) predicate.ProviderProfile { + return predicate.ProviderProfile(sql.FieldGT(FieldPrimaryLocalityID, v)) +} + +// PrimaryLocalityIDGTE applies the GTE predicate on the "primary_locality_id" field. +func PrimaryLocalityIDGTE(v uuid.UUID) predicate.ProviderProfile { + return predicate.ProviderProfile(sql.FieldGTE(FieldPrimaryLocalityID, v)) +} + +// PrimaryLocalityIDLT applies the LT predicate on the "primary_locality_id" field. +func PrimaryLocalityIDLT(v uuid.UUID) predicate.ProviderProfile { + return predicate.ProviderProfile(sql.FieldLT(FieldPrimaryLocalityID, v)) +} + +// PrimaryLocalityIDLTE applies the LTE predicate on the "primary_locality_id" field. +func PrimaryLocalityIDLTE(v uuid.UUID) predicate.ProviderProfile { + return predicate.ProviderProfile(sql.FieldLTE(FieldPrimaryLocalityID, v)) +} + +// MaxTravelDistanceKmEQ applies the EQ predicate on the "max_travel_distance_km" field. +func MaxTravelDistanceKmEQ(v int) predicate.ProviderProfile { + return predicate.ProviderProfile(sql.FieldEQ(FieldMaxTravelDistanceKm, v)) +} + +// MaxTravelDistanceKmNEQ applies the NEQ predicate on the "max_travel_distance_km" field. +func MaxTravelDistanceKmNEQ(v int) predicate.ProviderProfile { + return predicate.ProviderProfile(sql.FieldNEQ(FieldMaxTravelDistanceKm, v)) +} + +// MaxTravelDistanceKmIn applies the In predicate on the "max_travel_distance_km" field. +func MaxTravelDistanceKmIn(vs ...int) predicate.ProviderProfile { + return predicate.ProviderProfile(sql.FieldIn(FieldMaxTravelDistanceKm, vs...)) +} + +// MaxTravelDistanceKmNotIn applies the NotIn predicate on the "max_travel_distance_km" field. +func MaxTravelDistanceKmNotIn(vs ...int) predicate.ProviderProfile { + return predicate.ProviderProfile(sql.FieldNotIn(FieldMaxTravelDistanceKm, vs...)) +} + +// MaxTravelDistanceKmGT applies the GT predicate on the "max_travel_distance_km" field. +func MaxTravelDistanceKmGT(v int) predicate.ProviderProfile { + return predicate.ProviderProfile(sql.FieldGT(FieldMaxTravelDistanceKm, v)) +} + +// MaxTravelDistanceKmGTE applies the GTE predicate on the "max_travel_distance_km" field. +func MaxTravelDistanceKmGTE(v int) predicate.ProviderProfile { + return predicate.ProviderProfile(sql.FieldGTE(FieldMaxTravelDistanceKm, v)) +} + +// MaxTravelDistanceKmLT applies the LT predicate on the "max_travel_distance_km" field. +func MaxTravelDistanceKmLT(v int) predicate.ProviderProfile { + return predicate.ProviderProfile(sql.FieldLT(FieldMaxTravelDistanceKm, v)) +} + +// MaxTravelDistanceKmLTE applies the LTE predicate on the "max_travel_distance_km" field. +func MaxTravelDistanceKmLTE(v int) predicate.ProviderProfile { + return predicate.ProviderProfile(sql.FieldLTE(FieldMaxTravelDistanceKm, v)) +} + +// TravelsToCustomerEQ applies the EQ predicate on the "travels_to_customer" field. +func TravelsToCustomerEQ(v bool) predicate.ProviderProfile { + return predicate.ProviderProfile(sql.FieldEQ(FieldTravelsToCustomer, v)) +} + +// TravelsToCustomerNEQ applies the NEQ predicate on the "travels_to_customer" field. +func TravelsToCustomerNEQ(v bool) predicate.ProviderProfile { + return predicate.ProviderProfile(sql.FieldNEQ(FieldTravelsToCustomer, v)) +} + +// ReceivesCustomerEQ applies the EQ predicate on the "receives_customer" field. +func ReceivesCustomerEQ(v bool) predicate.ProviderProfile { + return predicate.ProviderProfile(sql.FieldEQ(FieldReceivesCustomer, v)) +} + +// ReceivesCustomerNEQ applies the NEQ predicate on the "receives_customer" field. +func ReceivesCustomerNEQ(v bool) predicate.ProviderProfile { + return predicate.ProviderProfile(sql.FieldNEQ(FieldReceivesCustomer, v)) +} + +// RemoteServicesEQ applies the EQ predicate on the "remote_services" field. +func RemoteServicesEQ(v bool) predicate.ProviderProfile { + return predicate.ProviderProfile(sql.FieldEQ(FieldRemoteServices, v)) +} + +// RemoteServicesNEQ applies the NEQ predicate on the "remote_services" field. +func RemoteServicesNEQ(v bool) predicate.ProviderProfile { + return predicate.ProviderProfile(sql.FieldNEQ(FieldRemoteServices, v)) +} + +// CreatedAtEQ applies the EQ predicate on the "created_at" field. +func CreatedAtEQ(v time.Time) predicate.ProviderProfile { + return predicate.ProviderProfile(sql.FieldEQ(FieldCreatedAt, v)) +} + +// CreatedAtNEQ applies the NEQ predicate on the "created_at" field. +func CreatedAtNEQ(v time.Time) predicate.ProviderProfile { + return predicate.ProviderProfile(sql.FieldNEQ(FieldCreatedAt, v)) +} + +// CreatedAtIn applies the In predicate on the "created_at" field. +func CreatedAtIn(vs ...time.Time) predicate.ProviderProfile { + return predicate.ProviderProfile(sql.FieldIn(FieldCreatedAt, vs...)) +} + +// CreatedAtNotIn applies the NotIn predicate on the "created_at" field. +func CreatedAtNotIn(vs ...time.Time) predicate.ProviderProfile { + return predicate.ProviderProfile(sql.FieldNotIn(FieldCreatedAt, vs...)) +} + +// CreatedAtGT applies the GT predicate on the "created_at" field. +func CreatedAtGT(v time.Time) predicate.ProviderProfile { + return predicate.ProviderProfile(sql.FieldGT(FieldCreatedAt, v)) +} + +// CreatedAtGTE applies the GTE predicate on the "created_at" field. +func CreatedAtGTE(v time.Time) predicate.ProviderProfile { + return predicate.ProviderProfile(sql.FieldGTE(FieldCreatedAt, v)) +} + +// CreatedAtLT applies the LT predicate on the "created_at" field. +func CreatedAtLT(v time.Time) predicate.ProviderProfile { + return predicate.ProviderProfile(sql.FieldLT(FieldCreatedAt, v)) +} + +// CreatedAtLTE applies the LTE predicate on the "created_at" field. +func CreatedAtLTE(v time.Time) predicate.ProviderProfile { + return predicate.ProviderProfile(sql.FieldLTE(FieldCreatedAt, v)) +} + +// UpdatedAtEQ applies the EQ predicate on the "updated_at" field. +func UpdatedAtEQ(v time.Time) predicate.ProviderProfile { + return predicate.ProviderProfile(sql.FieldEQ(FieldUpdatedAt, v)) +} + +// UpdatedAtNEQ applies the NEQ predicate on the "updated_at" field. +func UpdatedAtNEQ(v time.Time) predicate.ProviderProfile { + return predicate.ProviderProfile(sql.FieldNEQ(FieldUpdatedAt, v)) +} + +// UpdatedAtIn applies the In predicate on the "updated_at" field. +func UpdatedAtIn(vs ...time.Time) predicate.ProviderProfile { + return predicate.ProviderProfile(sql.FieldIn(FieldUpdatedAt, vs...)) +} + +// UpdatedAtNotIn applies the NotIn predicate on the "updated_at" field. +func UpdatedAtNotIn(vs ...time.Time) predicate.ProviderProfile { + return predicate.ProviderProfile(sql.FieldNotIn(FieldUpdatedAt, vs...)) +} + +// UpdatedAtGT applies the GT predicate on the "updated_at" field. +func UpdatedAtGT(v time.Time) predicate.ProviderProfile { + return predicate.ProviderProfile(sql.FieldGT(FieldUpdatedAt, v)) +} + +// UpdatedAtGTE applies the GTE predicate on the "updated_at" field. +func UpdatedAtGTE(v time.Time) predicate.ProviderProfile { + return predicate.ProviderProfile(sql.FieldGTE(FieldUpdatedAt, v)) +} + +// UpdatedAtLT applies the LT predicate on the "updated_at" field. +func UpdatedAtLT(v time.Time) predicate.ProviderProfile { + return predicate.ProviderProfile(sql.FieldLT(FieldUpdatedAt, v)) +} + +// UpdatedAtLTE applies the LTE predicate on the "updated_at" field. +func UpdatedAtLTE(v time.Time) predicate.ProviderProfile { + return predicate.ProviderProfile(sql.FieldLTE(FieldUpdatedAt, v)) +} + +// HasServiceLocalities applies the HasEdge predicate on the "service_localities" edge. +func HasServiceLocalities() predicate.ProviderProfile { + return predicate.ProviderProfile(func(s *sql.Selector) { + step := sqlgraph.NewStep( + sqlgraph.From(Table, FieldID), + sqlgraph.Edge(sqlgraph.M2M, false, ServiceLocalitiesTable, ServiceLocalitiesPrimaryKey...), + ) + sqlgraph.HasNeighbors(s, step) + }) +} + +// HasServiceLocalitiesWith applies the HasEdge predicate on the "service_localities" edge with a given conditions (other predicates). +func HasServiceLocalitiesWith(preds ...predicate.Locality) predicate.ProviderProfile { + return predicate.ProviderProfile(func(s *sql.Selector) { + step := newServiceLocalitiesStep() + sqlgraph.HasNeighborsWith(s, step, func(s *sql.Selector) { + for _, p := range preds { + p(s) + } + }) + }) +} + +// HasSpokenLanguages applies the HasEdge predicate on the "spoken_languages" edge. +func HasSpokenLanguages() predicate.ProviderProfile { + return predicate.ProviderProfile(func(s *sql.Selector) { + step := sqlgraph.NewStep( + sqlgraph.From(Table, FieldID), + sqlgraph.Edge(sqlgraph.M2M, false, SpokenLanguagesTable, SpokenLanguagesPrimaryKey...), + ) + sqlgraph.HasNeighbors(s, step) + }) +} + +// HasSpokenLanguagesWith applies the HasEdge predicate on the "spoken_languages" edge with a given conditions (other predicates). +func HasSpokenLanguagesWith(preds ...predicate.SpokenLanguage) predicate.ProviderProfile { + return predicate.ProviderProfile(func(s *sql.Selector) { + step := newSpokenLanguagesStep() + sqlgraph.HasNeighborsWith(s, step, func(s *sql.Selector) { + for _, p := range preds { + p(s) + } + }) + }) +} + +// HasServiceLocalityLinks applies the HasEdge predicate on the "service_locality_links" edge. +func HasServiceLocalityLinks() predicate.ProviderProfile { + return predicate.ProviderProfile(func(s *sql.Selector) { + step := sqlgraph.NewStep( + sqlgraph.From(Table, FieldID), + sqlgraph.Edge(sqlgraph.O2M, true, ServiceLocalityLinksTable, ServiceLocalityLinksColumn), + ) + sqlgraph.HasNeighbors(s, step) + }) +} + +// HasServiceLocalityLinksWith applies the HasEdge predicate on the "service_locality_links" edge with a given conditions (other predicates). +func HasServiceLocalityLinksWith(preds ...predicate.ProviderServiceLocality) predicate.ProviderProfile { + return predicate.ProviderProfile(func(s *sql.Selector) { + step := newServiceLocalityLinksStep() + sqlgraph.HasNeighborsWith(s, step, func(s *sql.Selector) { + for _, p := range preds { + p(s) + } + }) + }) +} + +// HasSpokenLanguageLinks applies the HasEdge predicate on the "spoken_language_links" edge. +func HasSpokenLanguageLinks() predicate.ProviderProfile { + return predicate.ProviderProfile(func(s *sql.Selector) { + step := sqlgraph.NewStep( + sqlgraph.From(Table, FieldID), + sqlgraph.Edge(sqlgraph.O2M, true, SpokenLanguageLinksTable, SpokenLanguageLinksColumn), + ) + sqlgraph.HasNeighbors(s, step) + }) +} + +// HasSpokenLanguageLinksWith applies the HasEdge predicate on the "spoken_language_links" edge with a given conditions (other predicates). +func HasSpokenLanguageLinksWith(preds ...predicate.ProviderSpokenLanguage) predicate.ProviderProfile { + return predicate.ProviderProfile(func(s *sql.Selector) { + step := newSpokenLanguageLinksStep() + 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.ProviderProfile) predicate.ProviderProfile { + return predicate.ProviderProfile(sql.AndPredicates(predicates...)) +} + +// Or groups predicates with the OR operator between them. +func Or(predicates ...predicate.ProviderProfile) predicate.ProviderProfile { + return predicate.ProviderProfile(sql.OrPredicates(predicates...)) +} + +// Not applies the not operator on the given predicate. +func Not(p predicate.ProviderProfile) predicate.ProviderProfile { + return predicate.ProviderProfile(sql.NotPredicates(p)) +} diff --git a/backend/ent/providerprofile_create.go b/backend/ent/providerprofile_create.go new file mode 100644 index 0000000..eaa53cf --- /dev/null +++ b/backend/ent/providerprofile_create.go @@ -0,0 +1,463 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "context" + "errors" + "fmt" + "time" + + "entgo.io/ent/dialect/sql/sqlgraph" + "entgo.io/ent/schema/field" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/locality" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/providerprofile" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/spokenlanguage" + "github.com/google/uuid" +) + +// ProviderProfileCreate is the builder for creating a ProviderProfile entity. +type ProviderProfileCreate struct { + config + mutation *ProviderProfileMutation + hooks []Hook +} + +// SetDisplayName sets the "display_name" field. +func (_c *ProviderProfileCreate) SetDisplayName(v string) *ProviderProfileCreate { + _c.mutation.SetDisplayName(v) + return _c +} + +// SetProviderType sets the "provider_type" field. +func (_c *ProviderProfileCreate) SetProviderType(v string) *ProviderProfileCreate { + _c.mutation.SetProviderType(v) + return _c +} + +// SetBio sets the "bio" field. +func (_c *ProviderProfileCreate) SetBio(v string) *ProviderProfileCreate { + _c.mutation.SetBio(v) + return _c +} + +// SetPrimaryLocalityID sets the "primary_locality_id" field. +func (_c *ProviderProfileCreate) SetPrimaryLocalityID(v uuid.UUID) *ProviderProfileCreate { + _c.mutation.SetPrimaryLocalityID(v) + return _c +} + +// SetMaxTravelDistanceKm sets the "max_travel_distance_km" field. +func (_c *ProviderProfileCreate) SetMaxTravelDistanceKm(v int) *ProviderProfileCreate { + _c.mutation.SetMaxTravelDistanceKm(v) + return _c +} + +// SetTravelsToCustomer sets the "travels_to_customer" field. +func (_c *ProviderProfileCreate) SetTravelsToCustomer(v bool) *ProviderProfileCreate { + _c.mutation.SetTravelsToCustomer(v) + return _c +} + +// SetNillableTravelsToCustomer sets the "travels_to_customer" field if the given value is not nil. +func (_c *ProviderProfileCreate) SetNillableTravelsToCustomer(v *bool) *ProviderProfileCreate { + if v != nil { + _c.SetTravelsToCustomer(*v) + } + return _c +} + +// SetReceivesCustomer sets the "receives_customer" field. +func (_c *ProviderProfileCreate) SetReceivesCustomer(v bool) *ProviderProfileCreate { + _c.mutation.SetReceivesCustomer(v) + return _c +} + +// SetNillableReceivesCustomer sets the "receives_customer" field if the given value is not nil. +func (_c *ProviderProfileCreate) SetNillableReceivesCustomer(v *bool) *ProviderProfileCreate { + if v != nil { + _c.SetReceivesCustomer(*v) + } + return _c +} + +// SetRemoteServices sets the "remote_services" field. +func (_c *ProviderProfileCreate) SetRemoteServices(v bool) *ProviderProfileCreate { + _c.mutation.SetRemoteServices(v) + return _c +} + +// SetNillableRemoteServices sets the "remote_services" field if the given value is not nil. +func (_c *ProviderProfileCreate) SetNillableRemoteServices(v *bool) *ProviderProfileCreate { + if v != nil { + _c.SetRemoteServices(*v) + } + return _c +} + +// SetCreatedAt sets the "created_at" field. +func (_c *ProviderProfileCreate) SetCreatedAt(v time.Time) *ProviderProfileCreate { + _c.mutation.SetCreatedAt(v) + return _c +} + +// SetNillableCreatedAt sets the "created_at" field if the given value is not nil. +func (_c *ProviderProfileCreate) SetNillableCreatedAt(v *time.Time) *ProviderProfileCreate { + if v != nil { + _c.SetCreatedAt(*v) + } + return _c +} + +// SetUpdatedAt sets the "updated_at" field. +func (_c *ProviderProfileCreate) SetUpdatedAt(v time.Time) *ProviderProfileCreate { + _c.mutation.SetUpdatedAt(v) + return _c +} + +// SetNillableUpdatedAt sets the "updated_at" field if the given value is not nil. +func (_c *ProviderProfileCreate) SetNillableUpdatedAt(v *time.Time) *ProviderProfileCreate { + if v != nil { + _c.SetUpdatedAt(*v) + } + return _c +} + +// SetID sets the "id" field. +func (_c *ProviderProfileCreate) SetID(v uuid.UUID) *ProviderProfileCreate { + _c.mutation.SetID(v) + return _c +} + +// AddServiceLocalityIDs adds the "service_localities" edge to the Locality entity by IDs. +func (_c *ProviderProfileCreate) AddServiceLocalityIDs(ids ...uuid.UUID) *ProviderProfileCreate { + _c.mutation.AddServiceLocalityIDs(ids...) + return _c +} + +// AddServiceLocalities adds the "service_localities" edges to the Locality entity. +func (_c *ProviderProfileCreate) AddServiceLocalities(v ...*Locality) *ProviderProfileCreate { + ids := make([]uuid.UUID, len(v)) + for i := range v { + ids[i] = v[i].ID + } + return _c.AddServiceLocalityIDs(ids...) +} + +// AddSpokenLanguageIDs adds the "spoken_languages" edge to the SpokenLanguage entity by IDs. +func (_c *ProviderProfileCreate) AddSpokenLanguageIDs(ids ...string) *ProviderProfileCreate { + _c.mutation.AddSpokenLanguageIDs(ids...) + return _c +} + +// AddSpokenLanguages adds the "spoken_languages" edges to the SpokenLanguage entity. +func (_c *ProviderProfileCreate) AddSpokenLanguages(v ...*SpokenLanguage) *ProviderProfileCreate { + ids := make([]string, len(v)) + for i := range v { + ids[i] = v[i].ID + } + return _c.AddSpokenLanguageIDs(ids...) +} + +// Mutation returns the ProviderProfileMutation object of the builder. +func (_c *ProviderProfileCreate) Mutation() *ProviderProfileMutation { + return _c.mutation +} + +// Save creates the ProviderProfile in the database. +func (_c *ProviderProfileCreate) Save(ctx context.Context) (*ProviderProfile, error) { + _c.defaults() + return withHooks(ctx, _c.sqlSave, _c.mutation, _c.hooks) +} + +// SaveX calls Save and panics if Save returns an error. +func (_c *ProviderProfileCreate) SaveX(ctx context.Context) *ProviderProfile { + v, err := _c.Save(ctx) + if err != nil { + panic(err) + } + return v +} + +// Exec executes the query. +func (_c *ProviderProfileCreate) Exec(ctx context.Context) error { + _, err := _c.Save(ctx) + return err +} + +// ExecX is like Exec, but panics if an error occurs. +func (_c *ProviderProfileCreate) 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 *ProviderProfileCreate) defaults() { + if _, ok := _c.mutation.TravelsToCustomer(); !ok { + v := providerprofile.DefaultTravelsToCustomer + _c.mutation.SetTravelsToCustomer(v) + } + if _, ok := _c.mutation.ReceivesCustomer(); !ok { + v := providerprofile.DefaultReceivesCustomer + _c.mutation.SetReceivesCustomer(v) + } + if _, ok := _c.mutation.RemoteServices(); !ok { + v := providerprofile.DefaultRemoteServices + _c.mutation.SetRemoteServices(v) + } + if _, ok := _c.mutation.CreatedAt(); !ok { + v := providerprofile.DefaultCreatedAt() + _c.mutation.SetCreatedAt(v) + } + if _, ok := _c.mutation.UpdatedAt(); !ok { + v := providerprofile.DefaultUpdatedAt() + _c.mutation.SetUpdatedAt(v) + } +} + +// check runs all checks and user-defined validators on the builder. +func (_c *ProviderProfileCreate) check() error { + if _, ok := _c.mutation.DisplayName(); !ok { + return &ValidationError{Name: "display_name", err: errors.New(`ent: missing required field "ProviderProfile.display_name"`)} + } + if v, ok := _c.mutation.DisplayName(); ok { + if err := providerprofile.DisplayNameValidator(v); err != nil { + return &ValidationError{Name: "display_name", err: fmt.Errorf(`ent: validator failed for field "ProviderProfile.display_name": %w`, err)} + } + } + if _, ok := _c.mutation.ProviderType(); !ok { + return &ValidationError{Name: "provider_type", err: errors.New(`ent: missing required field "ProviderProfile.provider_type"`)} + } + if v, ok := _c.mutation.ProviderType(); ok { + if err := providerprofile.ProviderTypeValidator(v); err != nil { + return &ValidationError{Name: "provider_type", err: fmt.Errorf(`ent: validator failed for field "ProviderProfile.provider_type": %w`, err)} + } + } + if _, ok := _c.mutation.Bio(); !ok { + return &ValidationError{Name: "bio", err: errors.New(`ent: missing required field "ProviderProfile.bio"`)} + } + if v, ok := _c.mutation.Bio(); ok { + if err := providerprofile.BioValidator(v); err != nil { + return &ValidationError{Name: "bio", err: fmt.Errorf(`ent: validator failed for field "ProviderProfile.bio": %w`, err)} + } + } + if _, ok := _c.mutation.PrimaryLocalityID(); !ok { + return &ValidationError{Name: "primary_locality_id", err: errors.New(`ent: missing required field "ProviderProfile.primary_locality_id"`)} + } + if _, ok := _c.mutation.MaxTravelDistanceKm(); !ok { + return &ValidationError{Name: "max_travel_distance_km", err: errors.New(`ent: missing required field "ProviderProfile.max_travel_distance_km"`)} + } + if v, ok := _c.mutation.MaxTravelDistanceKm(); ok { + if err := providerprofile.MaxTravelDistanceKmValidator(v); err != nil { + return &ValidationError{Name: "max_travel_distance_km", err: fmt.Errorf(`ent: validator failed for field "ProviderProfile.max_travel_distance_km": %w`, err)} + } + } + if _, ok := _c.mutation.TravelsToCustomer(); !ok { + return &ValidationError{Name: "travels_to_customer", err: errors.New(`ent: missing required field "ProviderProfile.travels_to_customer"`)} + } + if _, ok := _c.mutation.ReceivesCustomer(); !ok { + return &ValidationError{Name: "receives_customer", err: errors.New(`ent: missing required field "ProviderProfile.receives_customer"`)} + } + if _, ok := _c.mutation.RemoteServices(); !ok { + return &ValidationError{Name: "remote_services", err: errors.New(`ent: missing required field "ProviderProfile.remote_services"`)} + } + if _, ok := _c.mutation.CreatedAt(); !ok { + return &ValidationError{Name: "created_at", err: errors.New(`ent: missing required field "ProviderProfile.created_at"`)} + } + if _, ok := _c.mutation.UpdatedAt(); !ok { + return &ValidationError{Name: "updated_at", err: errors.New(`ent: missing required field "ProviderProfile.updated_at"`)} + } + return nil +} + +func (_c *ProviderProfileCreate) sqlSave(ctx context.Context) (*ProviderProfile, 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 *ProviderProfileCreate) createSpec() (*ProviderProfile, *sqlgraph.CreateSpec) { + var ( + _node = &ProviderProfile{config: _c.config} + _spec = sqlgraph.NewCreateSpec(providerprofile.Table, sqlgraph.NewFieldSpec(providerprofile.FieldID, field.TypeUUID)) + ) + if id, ok := _c.mutation.ID(); ok { + _node.ID = id + _spec.ID.Value = &id + } + if value, ok := _c.mutation.DisplayName(); ok { + _spec.SetField(providerprofile.FieldDisplayName, field.TypeString, value) + _node.DisplayName = value + } + if value, ok := _c.mutation.ProviderType(); ok { + _spec.SetField(providerprofile.FieldProviderType, field.TypeString, value) + _node.ProviderType = value + } + if value, ok := _c.mutation.Bio(); ok { + _spec.SetField(providerprofile.FieldBio, field.TypeString, value) + _node.Bio = value + } + if value, ok := _c.mutation.PrimaryLocalityID(); ok { + _spec.SetField(providerprofile.FieldPrimaryLocalityID, field.TypeUUID, value) + _node.PrimaryLocalityID = value + } + if value, ok := _c.mutation.MaxTravelDistanceKm(); ok { + _spec.SetField(providerprofile.FieldMaxTravelDistanceKm, field.TypeInt, value) + _node.MaxTravelDistanceKm = value + } + if value, ok := _c.mutation.TravelsToCustomer(); ok { + _spec.SetField(providerprofile.FieldTravelsToCustomer, field.TypeBool, value) + _node.TravelsToCustomer = value + } + if value, ok := _c.mutation.ReceivesCustomer(); ok { + _spec.SetField(providerprofile.FieldReceivesCustomer, field.TypeBool, value) + _node.ReceivesCustomer = value + } + if value, ok := _c.mutation.RemoteServices(); ok { + _spec.SetField(providerprofile.FieldRemoteServices, field.TypeBool, value) + _node.RemoteServices = value + } + if value, ok := _c.mutation.CreatedAt(); ok { + _spec.SetField(providerprofile.FieldCreatedAt, field.TypeTime, value) + _node.CreatedAt = value + } + if value, ok := _c.mutation.UpdatedAt(); ok { + _spec.SetField(providerprofile.FieldUpdatedAt, field.TypeTime, value) + _node.UpdatedAt = value + } + if nodes := _c.mutation.ServiceLocalitiesIDs(); len(nodes) > 0 { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.M2M, + Inverse: false, + Table: providerprofile.ServiceLocalitiesTable, + Columns: providerprofile.ServiceLocalitiesPrimaryKey, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(locality.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.SpokenLanguagesIDs(); len(nodes) > 0 { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.M2M, + Inverse: false, + Table: providerprofile.SpokenLanguagesTable, + Columns: providerprofile.SpokenLanguagesPrimaryKey, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(spokenlanguage.FieldID, field.TypeString), + }, + } + for _, k := range nodes { + edge.Target.Nodes = append(edge.Target.Nodes, k) + } + _spec.Edges = append(_spec.Edges, edge) + } + return _node, _spec +} + +// ProviderProfileCreateBulk is the builder for creating many ProviderProfile entities in bulk. +type ProviderProfileCreateBulk struct { + config + err error + builders []*ProviderProfileCreate +} + +// Save creates the ProviderProfile entities in the database. +func (_c *ProviderProfileCreateBulk) Save(ctx context.Context) ([]*ProviderProfile, error) { + if _c.err != nil { + return nil, _c.err + } + specs := make([]*sqlgraph.CreateSpec, len(_c.builders)) + nodes := make([]*ProviderProfile, 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.(*ProviderProfileMutation) + 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 *ProviderProfileCreateBulk) SaveX(ctx context.Context) []*ProviderProfile { + v, err := _c.Save(ctx) + if err != nil { + panic(err) + } + return v +} + +// Exec executes the query. +func (_c *ProviderProfileCreateBulk) Exec(ctx context.Context) error { + _, err := _c.Save(ctx) + return err +} + +// ExecX is like Exec, but panics if an error occurs. +func (_c *ProviderProfileCreateBulk) ExecX(ctx context.Context) { + if err := _c.Exec(ctx); err != nil { + panic(err) + } +} diff --git a/backend/ent/providerprofile_delete.go b/backend/ent/providerprofile_delete.go new file mode 100644 index 0000000..8253b58 --- /dev/null +++ b/backend/ent/providerprofile_delete.go @@ -0,0 +1,88 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "context" + + "entgo.io/ent/dialect/sql" + "entgo.io/ent/dialect/sql/sqlgraph" + "entgo.io/ent/schema/field" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/predicate" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/providerprofile" +) + +// ProviderProfileDelete is the builder for deleting a ProviderProfile entity. +type ProviderProfileDelete struct { + config + hooks []Hook + mutation *ProviderProfileMutation +} + +// Where appends a list predicates to the ProviderProfileDelete builder. +func (_d *ProviderProfileDelete) Where(ps ...predicate.ProviderProfile) *ProviderProfileDelete { + _d.mutation.Where(ps...) + return _d +} + +// Exec executes the deletion query and returns how many vertices were deleted. +func (_d *ProviderProfileDelete) 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 *ProviderProfileDelete) ExecX(ctx context.Context) int { + n, err := _d.Exec(ctx) + if err != nil { + panic(err) + } + return n +} + +func (_d *ProviderProfileDelete) sqlExec(ctx context.Context) (int, error) { + _spec := sqlgraph.NewDeleteSpec(providerprofile.Table, sqlgraph.NewFieldSpec(providerprofile.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 +} + +// ProviderProfileDeleteOne is the builder for deleting a single ProviderProfile entity. +type ProviderProfileDeleteOne struct { + _d *ProviderProfileDelete +} + +// Where appends a list predicates to the ProviderProfileDelete builder. +func (_d *ProviderProfileDeleteOne) Where(ps ...predicate.ProviderProfile) *ProviderProfileDeleteOne { + _d._d.mutation.Where(ps...) + return _d +} + +// Exec executes the deletion query. +func (_d *ProviderProfileDeleteOne) Exec(ctx context.Context) error { + n, err := _d._d.Exec(ctx) + switch { + case err != nil: + return err + case n == 0: + return &NotFoundError{providerprofile.Label} + default: + return nil + } +} + +// ExecX is like Exec, but panics if an error occurs. +func (_d *ProviderProfileDeleteOne) ExecX(ctx context.Context) { + if err := _d.Exec(ctx); err != nil { + panic(err) + } +} diff --git a/backend/ent/providerprofile_query.go b/backend/ent/providerprofile_query.go new file mode 100644 index 0000000..ed3203a --- /dev/null +++ b/backend/ent/providerprofile_query.go @@ -0,0 +1,899 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "context" + "database/sql/driver" + "fmt" + "math" + + "entgo.io/ent" + "entgo.io/ent/dialect/sql" + "entgo.io/ent/dialect/sql/sqlgraph" + "entgo.io/ent/schema/field" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/locality" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/predicate" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/providerprofile" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/providerservicelocality" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/providerspokenlanguage" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/spokenlanguage" + "github.com/google/uuid" +) + +// ProviderProfileQuery is the builder for querying ProviderProfile entities. +type ProviderProfileQuery struct { + config + ctx *QueryContext + order []providerprofile.OrderOption + inters []Interceptor + predicates []predicate.ProviderProfile + withServiceLocalities *LocalityQuery + withSpokenLanguages *SpokenLanguageQuery + withServiceLocalityLinks *ProviderServiceLocalityQuery + withSpokenLanguageLinks *ProviderSpokenLanguageQuery + // intermediate query (i.e. traversal path). + sql *sql.Selector + path func(context.Context) (*sql.Selector, error) +} + +// Where adds a new predicate for the ProviderProfileQuery builder. +func (_q *ProviderProfileQuery) Where(ps ...predicate.ProviderProfile) *ProviderProfileQuery { + _q.predicates = append(_q.predicates, ps...) + return _q +} + +// Limit the number of records to be returned by this query. +func (_q *ProviderProfileQuery) Limit(limit int) *ProviderProfileQuery { + _q.ctx.Limit = &limit + return _q +} + +// Offset to start from. +func (_q *ProviderProfileQuery) Offset(offset int) *ProviderProfileQuery { + _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 *ProviderProfileQuery) Unique(unique bool) *ProviderProfileQuery { + _q.ctx.Unique = &unique + return _q +} + +// Order specifies how the records should be ordered. +func (_q *ProviderProfileQuery) Order(o ...providerprofile.OrderOption) *ProviderProfileQuery { + _q.order = append(_q.order, o...) + return _q +} + +// QueryServiceLocalities chains the current query on the "service_localities" edge. +func (_q *ProviderProfileQuery) QueryServiceLocalities() *LocalityQuery { + query := (&LocalityClient{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(providerprofile.Table, providerprofile.FieldID, selector), + sqlgraph.To(locality.Table, locality.FieldID), + sqlgraph.Edge(sqlgraph.M2M, false, providerprofile.ServiceLocalitiesTable, providerprofile.ServiceLocalitiesPrimaryKey...), + ) + fromU = sqlgraph.SetNeighbors(_q.driver.Dialect(), step) + return fromU, nil + } + return query +} + +// QuerySpokenLanguages chains the current query on the "spoken_languages" edge. +func (_q *ProviderProfileQuery) QuerySpokenLanguages() *SpokenLanguageQuery { + query := (&SpokenLanguageClient{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(providerprofile.Table, providerprofile.FieldID, selector), + sqlgraph.To(spokenlanguage.Table, spokenlanguage.FieldID), + sqlgraph.Edge(sqlgraph.M2M, false, providerprofile.SpokenLanguagesTable, providerprofile.SpokenLanguagesPrimaryKey...), + ) + fromU = sqlgraph.SetNeighbors(_q.driver.Dialect(), step) + return fromU, nil + } + return query +} + +// QueryServiceLocalityLinks chains the current query on the "service_locality_links" edge. +func (_q *ProviderProfileQuery) QueryServiceLocalityLinks() *ProviderServiceLocalityQuery { + query := (&ProviderServiceLocalityClient{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(providerprofile.Table, providerprofile.FieldID, selector), + sqlgraph.To(providerservicelocality.Table, providerservicelocality.ProfileColumn), + sqlgraph.Edge(sqlgraph.O2M, true, providerprofile.ServiceLocalityLinksTable, providerprofile.ServiceLocalityLinksColumn), + ) + fromU = sqlgraph.SetNeighbors(_q.driver.Dialect(), step) + return fromU, nil + } + return query +} + +// QuerySpokenLanguageLinks chains the current query on the "spoken_language_links" edge. +func (_q *ProviderProfileQuery) QuerySpokenLanguageLinks() *ProviderSpokenLanguageQuery { + query := (&ProviderSpokenLanguageClient{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(providerprofile.Table, providerprofile.FieldID, selector), + sqlgraph.To(providerspokenlanguage.Table, providerspokenlanguage.ProfileColumn), + sqlgraph.Edge(sqlgraph.O2M, true, providerprofile.SpokenLanguageLinksTable, providerprofile.SpokenLanguageLinksColumn), + ) + fromU = sqlgraph.SetNeighbors(_q.driver.Dialect(), step) + return fromU, nil + } + return query +} + +// First returns the first ProviderProfile entity from the query. +// Returns a *NotFoundError when no ProviderProfile was found. +func (_q *ProviderProfileQuery) First(ctx context.Context) (*ProviderProfile, 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{providerprofile.Label} + } + return nodes[0], nil +} + +// FirstX is like First, but panics if an error occurs. +func (_q *ProviderProfileQuery) FirstX(ctx context.Context) *ProviderProfile { + node, err := _q.First(ctx) + if err != nil && !IsNotFound(err) { + panic(err) + } + return node +} + +// FirstID returns the first ProviderProfile ID from the query. +// Returns a *NotFoundError when no ProviderProfile ID was found. +func (_q *ProviderProfileQuery) 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{providerprofile.Label} + return + } + return ids[0], nil +} + +// FirstIDX is like FirstID, but panics if an error occurs. +func (_q *ProviderProfileQuery) FirstIDX(ctx context.Context) uuid.UUID { + id, err := _q.FirstID(ctx) + if err != nil && !IsNotFound(err) { + panic(err) + } + return id +} + +// Only returns a single ProviderProfile entity found by the query, ensuring it only returns one. +// Returns a *NotSingularError when more than one ProviderProfile entity is found. +// Returns a *NotFoundError when no ProviderProfile entities are found. +func (_q *ProviderProfileQuery) Only(ctx context.Context) (*ProviderProfile, 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{providerprofile.Label} + default: + return nil, &NotSingularError{providerprofile.Label} + } +} + +// OnlyX is like Only, but panics if an error occurs. +func (_q *ProviderProfileQuery) OnlyX(ctx context.Context) *ProviderProfile { + node, err := _q.Only(ctx) + if err != nil { + panic(err) + } + return node +} + +// OnlyID is like Only, but returns the only ProviderProfile ID in the query. +// Returns a *NotSingularError when more than one ProviderProfile ID is found. +// Returns a *NotFoundError when no entities are found. +func (_q *ProviderProfileQuery) 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{providerprofile.Label} + default: + err = &NotSingularError{providerprofile.Label} + } + return +} + +// OnlyIDX is like OnlyID, but panics if an error occurs. +func (_q *ProviderProfileQuery) 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 ProviderProfiles. +func (_q *ProviderProfileQuery) All(ctx context.Context) ([]*ProviderProfile, error) { + ctx = setContextOp(ctx, _q.ctx, ent.OpQueryAll) + if err := _q.prepareQuery(ctx); err != nil { + return nil, err + } + qr := querierAll[[]*ProviderProfile, *ProviderProfileQuery]() + return withInterceptors[[]*ProviderProfile](ctx, _q, qr, _q.inters) +} + +// AllX is like All, but panics if an error occurs. +func (_q *ProviderProfileQuery) AllX(ctx context.Context) []*ProviderProfile { + nodes, err := _q.All(ctx) + if err != nil { + panic(err) + } + return nodes +} + +// IDs executes the query and returns a list of ProviderProfile IDs. +func (_q *ProviderProfileQuery) 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(providerprofile.FieldID).Scan(ctx, &ids); err != nil { + return nil, err + } + return ids, nil +} + +// IDsX is like IDs, but panics if an error occurs. +func (_q *ProviderProfileQuery) 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 *ProviderProfileQuery) 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[*ProviderProfileQuery](), _q.inters) +} + +// CountX is like Count, but panics if an error occurs. +func (_q *ProviderProfileQuery) 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 *ProviderProfileQuery) 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 *ProviderProfileQuery) ExistX(ctx context.Context) bool { + exist, err := _q.Exist(ctx) + if err != nil { + panic(err) + } + return exist +} + +// Clone returns a duplicate of the ProviderProfileQuery 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 *ProviderProfileQuery) Clone() *ProviderProfileQuery { + if _q == nil { + return nil + } + return &ProviderProfileQuery{ + config: _q.config, + ctx: _q.ctx.Clone(), + order: append([]providerprofile.OrderOption{}, _q.order...), + inters: append([]Interceptor{}, _q.inters...), + predicates: append([]predicate.ProviderProfile{}, _q.predicates...), + withServiceLocalities: _q.withServiceLocalities.Clone(), + withSpokenLanguages: _q.withSpokenLanguages.Clone(), + withServiceLocalityLinks: _q.withServiceLocalityLinks.Clone(), + withSpokenLanguageLinks: _q.withSpokenLanguageLinks.Clone(), + // clone intermediate query. + sql: _q.sql.Clone(), + path: _q.path, + } +} + +// WithServiceLocalities tells the query-builder to eager-load the nodes that are connected to +// the "service_localities" edge. The optional arguments are used to configure the query builder of the edge. +func (_q *ProviderProfileQuery) WithServiceLocalities(opts ...func(*LocalityQuery)) *ProviderProfileQuery { + query := (&LocalityClient{config: _q.config}).Query() + for _, opt := range opts { + opt(query) + } + _q.withServiceLocalities = query + return _q +} + +// WithSpokenLanguages tells the query-builder to eager-load the nodes that are connected to +// the "spoken_languages" edge. The optional arguments are used to configure the query builder of the edge. +func (_q *ProviderProfileQuery) WithSpokenLanguages(opts ...func(*SpokenLanguageQuery)) *ProviderProfileQuery { + query := (&SpokenLanguageClient{config: _q.config}).Query() + for _, opt := range opts { + opt(query) + } + _q.withSpokenLanguages = query + return _q +} + +// WithServiceLocalityLinks tells the query-builder to eager-load the nodes that are connected to +// the "service_locality_links" edge. The optional arguments are used to configure the query builder of the edge. +func (_q *ProviderProfileQuery) WithServiceLocalityLinks(opts ...func(*ProviderServiceLocalityQuery)) *ProviderProfileQuery { + query := (&ProviderServiceLocalityClient{config: _q.config}).Query() + for _, opt := range opts { + opt(query) + } + _q.withServiceLocalityLinks = query + return _q +} + +// WithSpokenLanguageLinks tells the query-builder to eager-load the nodes that are connected to +// the "spoken_language_links" edge. The optional arguments are used to configure the query builder of the edge. +func (_q *ProviderProfileQuery) WithSpokenLanguageLinks(opts ...func(*ProviderSpokenLanguageQuery)) *ProviderProfileQuery { + query := (&ProviderSpokenLanguageClient{config: _q.config}).Query() + for _, opt := range opts { + opt(query) + } + _q.withSpokenLanguageLinks = 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 { +// DisplayName string `json:"display_name,omitempty"` +// Count int `json:"count,omitempty"` +// } +// +// client.ProviderProfile.Query(). +// GroupBy(providerprofile.FieldDisplayName). +// Aggregate(ent.Count()). +// Scan(ctx, &v) +func (_q *ProviderProfileQuery) GroupBy(field string, fields ...string) *ProviderProfileGroupBy { + _q.ctx.Fields = append([]string{field}, fields...) + grbuild := &ProviderProfileGroupBy{build: _q} + grbuild.flds = &_q.ctx.Fields + grbuild.label = providerprofile.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 { +// DisplayName string `json:"display_name,omitempty"` +// } +// +// client.ProviderProfile.Query(). +// Select(providerprofile.FieldDisplayName). +// Scan(ctx, &v) +func (_q *ProviderProfileQuery) Select(fields ...string) *ProviderProfileSelect { + _q.ctx.Fields = append(_q.ctx.Fields, fields...) + sbuild := &ProviderProfileSelect{ProviderProfileQuery: _q} + sbuild.label = providerprofile.Label + sbuild.flds, sbuild.scan = &_q.ctx.Fields, sbuild.Scan + return sbuild +} + +// Aggregate returns a ProviderProfileSelect configured with the given aggregations. +func (_q *ProviderProfileQuery) Aggregate(fns ...AggregateFunc) *ProviderProfileSelect { + return _q.Select().Aggregate(fns...) +} + +func (_q *ProviderProfileQuery) 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 !providerprofile.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 *ProviderProfileQuery) sqlAll(ctx context.Context, hooks ...queryHook) ([]*ProviderProfile, error) { + var ( + nodes = []*ProviderProfile{} + _spec = _q.querySpec() + loadedTypes = [4]bool{ + _q.withServiceLocalities != nil, + _q.withSpokenLanguages != nil, + _q.withServiceLocalityLinks != nil, + _q.withSpokenLanguageLinks != nil, + } + ) + _spec.ScanValues = func(columns []string) ([]any, error) { + return (*ProviderProfile).scanValues(nil, columns) + } + _spec.Assign = func(columns []string, values []any) error { + node := &ProviderProfile{config: _q.config} + nodes = append(nodes, node) + node.Edges.loadedTypes = loadedTypes + return node.assignValues(columns, values) + } + 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.withServiceLocalities; query != nil { + if err := _q.loadServiceLocalities(ctx, query, nodes, + func(n *ProviderProfile) { n.Edges.ServiceLocalities = []*Locality{} }, + func(n *ProviderProfile, e *Locality) { + n.Edges.ServiceLocalities = append(n.Edges.ServiceLocalities, e) + }); err != nil { + return nil, err + } + } + if query := _q.withSpokenLanguages; query != nil { + if err := _q.loadSpokenLanguages(ctx, query, nodes, + func(n *ProviderProfile) { n.Edges.SpokenLanguages = []*SpokenLanguage{} }, + func(n *ProviderProfile, e *SpokenLanguage) { + n.Edges.SpokenLanguages = append(n.Edges.SpokenLanguages, e) + }); err != nil { + return nil, err + } + } + if query := _q.withServiceLocalityLinks; query != nil { + if err := _q.loadServiceLocalityLinks(ctx, query, nodes, + func(n *ProviderProfile) { n.Edges.ServiceLocalityLinks = []*ProviderServiceLocality{} }, + func(n *ProviderProfile, e *ProviderServiceLocality) { + n.Edges.ServiceLocalityLinks = append(n.Edges.ServiceLocalityLinks, e) + }); err != nil { + return nil, err + } + } + if query := _q.withSpokenLanguageLinks; query != nil { + if err := _q.loadSpokenLanguageLinks(ctx, query, nodes, + func(n *ProviderProfile) { n.Edges.SpokenLanguageLinks = []*ProviderSpokenLanguage{} }, + func(n *ProviderProfile, e *ProviderSpokenLanguage) { + n.Edges.SpokenLanguageLinks = append(n.Edges.SpokenLanguageLinks, e) + }); err != nil { + return nil, err + } + } + return nodes, nil +} + +func (_q *ProviderProfileQuery) loadServiceLocalities(ctx context.Context, query *LocalityQuery, nodes []*ProviderProfile, init func(*ProviderProfile), assign func(*ProviderProfile, *Locality)) error { + edgeIDs := make([]driver.Value, len(nodes)) + byID := make(map[uuid.UUID]*ProviderProfile) + nids := make(map[uuid.UUID]map[*ProviderProfile]struct{}) + for i, node := range nodes { + edgeIDs[i] = node.ID + byID[node.ID] = node + if init != nil { + init(node) + } + } + query.Where(func(s *sql.Selector) { + joinT := sql.Table(providerprofile.ServiceLocalitiesTable) + s.Join(joinT).On(s.C(locality.FieldID), joinT.C(providerprofile.ServiceLocalitiesPrimaryKey[1])) + s.Where(sql.InValues(joinT.C(providerprofile.ServiceLocalitiesPrimaryKey[0]), edgeIDs...)) + columns := s.SelectedColumns() + s.Select(joinT.C(providerprofile.ServiceLocalitiesPrimaryKey[0])) + s.AppendSelect(columns...) + s.SetDistinct(false) + }) + if err := query.prepareQuery(ctx); err != nil { + return err + } + qr := QuerierFunc(func(ctx context.Context, q Query) (Value, error) { + return query.sqlAll(ctx, func(_ context.Context, spec *sqlgraph.QuerySpec) { + assign := spec.Assign + values := spec.ScanValues + spec.ScanValues = func(columns []string) ([]any, error) { + values, err := values(columns[1:]) + if err != nil { + return nil, err + } + return append([]any{new(uuid.UUID)}, values...), nil + } + spec.Assign = func(columns []string, values []any) error { + outValue := *values[0].(*uuid.UUID) + inValue := *values[1].(*uuid.UUID) + if nids[inValue] == nil { + nids[inValue] = map[*ProviderProfile]struct{}{byID[outValue]: {}} + return assign(columns[1:], values[1:]) + } + nids[inValue][byID[outValue]] = struct{}{} + return nil + } + }) + }) + neighbors, err := withInterceptors[[]*Locality](ctx, query, qr, query.inters) + if err != nil { + return err + } + for _, n := range neighbors { + nodes, ok := nids[n.ID] + if !ok { + return fmt.Errorf(`unexpected "service_localities" node returned %v`, n.ID) + } + for kn := range nodes { + assign(kn, n) + } + } + return nil +} +func (_q *ProviderProfileQuery) loadSpokenLanguages(ctx context.Context, query *SpokenLanguageQuery, nodes []*ProviderProfile, init func(*ProviderProfile), assign func(*ProviderProfile, *SpokenLanguage)) error { + edgeIDs := make([]driver.Value, len(nodes)) + byID := make(map[uuid.UUID]*ProviderProfile) + nids := make(map[string]map[*ProviderProfile]struct{}) + for i, node := range nodes { + edgeIDs[i] = node.ID + byID[node.ID] = node + if init != nil { + init(node) + } + } + query.Where(func(s *sql.Selector) { + joinT := sql.Table(providerprofile.SpokenLanguagesTable) + s.Join(joinT).On(s.C(spokenlanguage.FieldID), joinT.C(providerprofile.SpokenLanguagesPrimaryKey[1])) + s.Where(sql.InValues(joinT.C(providerprofile.SpokenLanguagesPrimaryKey[0]), edgeIDs...)) + columns := s.SelectedColumns() + s.Select(joinT.C(providerprofile.SpokenLanguagesPrimaryKey[0])) + s.AppendSelect(columns...) + s.SetDistinct(false) + }) + if err := query.prepareQuery(ctx); err != nil { + return err + } + qr := QuerierFunc(func(ctx context.Context, q Query) (Value, error) { + return query.sqlAll(ctx, func(_ context.Context, spec *sqlgraph.QuerySpec) { + assign := spec.Assign + values := spec.ScanValues + spec.ScanValues = func(columns []string) ([]any, error) { + values, err := values(columns[1:]) + if err != nil { + return nil, err + } + return append([]any{new(uuid.UUID)}, values...), nil + } + spec.Assign = func(columns []string, values []any) error { + outValue := *values[0].(*uuid.UUID) + inValue := values[1].(*sql.NullString).String + if nids[inValue] == nil { + nids[inValue] = map[*ProviderProfile]struct{}{byID[outValue]: {}} + return assign(columns[1:], values[1:]) + } + nids[inValue][byID[outValue]] = struct{}{} + return nil + } + }) + }) + neighbors, err := withInterceptors[[]*SpokenLanguage](ctx, query, qr, query.inters) + if err != nil { + return err + } + for _, n := range neighbors { + nodes, ok := nids[n.ID] + if !ok { + return fmt.Errorf(`unexpected "spoken_languages" node returned %v`, n.ID) + } + for kn := range nodes { + assign(kn, n) + } + } + return nil +} +func (_q *ProviderProfileQuery) loadServiceLocalityLinks(ctx context.Context, query *ProviderServiceLocalityQuery, nodes []*ProviderProfile, init func(*ProviderProfile), assign func(*ProviderProfile, *ProviderServiceLocality)) error { + fks := make([]driver.Value, 0, len(nodes)) + nodeids := make(map[uuid.UUID]*ProviderProfile) + 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(providerservicelocality.FieldInternalUserID) + } + query.Where(predicate.ProviderServiceLocality(func(s *sql.Selector) { + s.Where(sql.InValues(s.C(providerprofile.ServiceLocalityLinksColumn), fks...)) + })) + neighbors, err := query.All(ctx) + if err != nil { + return err + } + for _, n := range neighbors { + fk := n.InternalUserID + node, ok := nodeids[fk] + if !ok { + return fmt.Errorf(`unexpected referenced foreign-key "internal_user_id" returned %v for node %v`, fk, n) + } + assign(node, n) + } + return nil +} +func (_q *ProviderProfileQuery) loadSpokenLanguageLinks(ctx context.Context, query *ProviderSpokenLanguageQuery, nodes []*ProviderProfile, init func(*ProviderProfile), assign func(*ProviderProfile, *ProviderSpokenLanguage)) error { + fks := make([]driver.Value, 0, len(nodes)) + nodeids := make(map[uuid.UUID]*ProviderProfile) + 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(providerspokenlanguage.FieldInternalUserID) + } + query.Where(predicate.ProviderSpokenLanguage(func(s *sql.Selector) { + s.Where(sql.InValues(s.C(providerprofile.SpokenLanguageLinksColumn), fks...)) + })) + neighbors, err := query.All(ctx) + if err != nil { + return err + } + for _, n := range neighbors { + fk := n.InternalUserID + node, ok := nodeids[fk] + if !ok { + return fmt.Errorf(`unexpected referenced foreign-key "internal_user_id" returned %v for node %v`, fk, n) + } + assign(node, n) + } + return nil +} + +func (_q *ProviderProfileQuery) sqlCount(ctx context.Context) (int, error) { + _spec := _q.querySpec() + _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 *ProviderProfileQuery) querySpec() *sqlgraph.QuerySpec { + _spec := sqlgraph.NewQuerySpec(providerprofile.Table, providerprofile.Columns, sqlgraph.NewFieldSpec(providerprofile.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, providerprofile.FieldID) + for i := range fields { + if fields[i] != providerprofile.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 *ProviderProfileQuery) sqlQuery(ctx context.Context) *sql.Selector { + builder := sql.Dialect(_q.driver.Dialect()) + t1 := builder.Table(providerprofile.Table) + columns := _q.ctx.Fields + if len(columns) == 0 { + columns = providerprofile.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 _, 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 +} + +// ProviderProfileGroupBy is the group-by builder for ProviderProfile entities. +type ProviderProfileGroupBy struct { + selector + build *ProviderProfileQuery +} + +// Aggregate adds the given aggregation functions to the group-by query. +func (_g *ProviderProfileGroupBy) Aggregate(fns ...AggregateFunc) *ProviderProfileGroupBy { + _g.fns = append(_g.fns, fns...) + return _g +} + +// Scan applies the selector query and scans the result into the given value. +func (_g *ProviderProfileGroupBy) 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[*ProviderProfileQuery, *ProviderProfileGroupBy](ctx, _g.build, _g, _g.build.inters, v) +} + +func (_g *ProviderProfileGroupBy) sqlScan(ctx context.Context, root *ProviderProfileQuery, 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) +} + +// ProviderProfileSelect is the builder for selecting fields of ProviderProfile entities. +type ProviderProfileSelect struct { + *ProviderProfileQuery + selector +} + +// Aggregate adds the given aggregation functions to the selector query. +func (_s *ProviderProfileSelect) Aggregate(fns ...AggregateFunc) *ProviderProfileSelect { + _s.fns = append(_s.fns, fns...) + return _s +} + +// Scan applies the selector query and scans the result into the given value. +func (_s *ProviderProfileSelect) 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[*ProviderProfileQuery, *ProviderProfileSelect](ctx, _s.ProviderProfileQuery, _s, _s.inters, v) +} + +func (_s *ProviderProfileSelect) sqlScan(ctx context.Context, root *ProviderProfileQuery, 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/backend/ent/providerprofile_update.go b/backend/ent/providerprofile_update.go new file mode 100644 index 0000000..a842c31 --- /dev/null +++ b/backend/ent/providerprofile_update.go @@ -0,0 +1,887 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "context" + "errors" + "fmt" + "time" + + "entgo.io/ent/dialect/sql" + "entgo.io/ent/dialect/sql/sqlgraph" + "entgo.io/ent/schema/field" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/locality" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/predicate" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/providerprofile" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/spokenlanguage" + "github.com/google/uuid" +) + +// ProviderProfileUpdate is the builder for updating ProviderProfile entities. +type ProviderProfileUpdate struct { + config + hooks []Hook + mutation *ProviderProfileMutation +} + +// Where appends a list predicates to the ProviderProfileUpdate builder. +func (_u *ProviderProfileUpdate) Where(ps ...predicate.ProviderProfile) *ProviderProfileUpdate { + _u.mutation.Where(ps...) + return _u +} + +// SetDisplayName sets the "display_name" field. +func (_u *ProviderProfileUpdate) SetDisplayName(v string) *ProviderProfileUpdate { + _u.mutation.SetDisplayName(v) + return _u +} + +// SetNillableDisplayName sets the "display_name" field if the given value is not nil. +func (_u *ProviderProfileUpdate) SetNillableDisplayName(v *string) *ProviderProfileUpdate { + if v != nil { + _u.SetDisplayName(*v) + } + return _u +} + +// SetProviderType sets the "provider_type" field. +func (_u *ProviderProfileUpdate) SetProviderType(v string) *ProviderProfileUpdate { + _u.mutation.SetProviderType(v) + return _u +} + +// SetNillableProviderType sets the "provider_type" field if the given value is not nil. +func (_u *ProviderProfileUpdate) SetNillableProviderType(v *string) *ProviderProfileUpdate { + if v != nil { + _u.SetProviderType(*v) + } + return _u +} + +// SetBio sets the "bio" field. +func (_u *ProviderProfileUpdate) SetBio(v string) *ProviderProfileUpdate { + _u.mutation.SetBio(v) + return _u +} + +// SetNillableBio sets the "bio" field if the given value is not nil. +func (_u *ProviderProfileUpdate) SetNillableBio(v *string) *ProviderProfileUpdate { + if v != nil { + _u.SetBio(*v) + } + return _u +} + +// SetPrimaryLocalityID sets the "primary_locality_id" field. +func (_u *ProviderProfileUpdate) SetPrimaryLocalityID(v uuid.UUID) *ProviderProfileUpdate { + _u.mutation.SetPrimaryLocalityID(v) + return _u +} + +// SetNillablePrimaryLocalityID sets the "primary_locality_id" field if the given value is not nil. +func (_u *ProviderProfileUpdate) SetNillablePrimaryLocalityID(v *uuid.UUID) *ProviderProfileUpdate { + if v != nil { + _u.SetPrimaryLocalityID(*v) + } + return _u +} + +// SetMaxTravelDistanceKm sets the "max_travel_distance_km" field. +func (_u *ProviderProfileUpdate) SetMaxTravelDistanceKm(v int) *ProviderProfileUpdate { + _u.mutation.ResetMaxTravelDistanceKm() + _u.mutation.SetMaxTravelDistanceKm(v) + return _u +} + +// SetNillableMaxTravelDistanceKm sets the "max_travel_distance_km" field if the given value is not nil. +func (_u *ProviderProfileUpdate) SetNillableMaxTravelDistanceKm(v *int) *ProviderProfileUpdate { + if v != nil { + _u.SetMaxTravelDistanceKm(*v) + } + return _u +} + +// AddMaxTravelDistanceKm adds value to the "max_travel_distance_km" field. +func (_u *ProviderProfileUpdate) AddMaxTravelDistanceKm(v int) *ProviderProfileUpdate { + _u.mutation.AddMaxTravelDistanceKm(v) + return _u +} + +// SetTravelsToCustomer sets the "travels_to_customer" field. +func (_u *ProviderProfileUpdate) SetTravelsToCustomer(v bool) *ProviderProfileUpdate { + _u.mutation.SetTravelsToCustomer(v) + return _u +} + +// SetNillableTravelsToCustomer sets the "travels_to_customer" field if the given value is not nil. +func (_u *ProviderProfileUpdate) SetNillableTravelsToCustomer(v *bool) *ProviderProfileUpdate { + if v != nil { + _u.SetTravelsToCustomer(*v) + } + return _u +} + +// SetReceivesCustomer sets the "receives_customer" field. +func (_u *ProviderProfileUpdate) SetReceivesCustomer(v bool) *ProviderProfileUpdate { + _u.mutation.SetReceivesCustomer(v) + return _u +} + +// SetNillableReceivesCustomer sets the "receives_customer" field if the given value is not nil. +func (_u *ProviderProfileUpdate) SetNillableReceivesCustomer(v *bool) *ProviderProfileUpdate { + if v != nil { + _u.SetReceivesCustomer(*v) + } + return _u +} + +// SetRemoteServices sets the "remote_services" field. +func (_u *ProviderProfileUpdate) SetRemoteServices(v bool) *ProviderProfileUpdate { + _u.mutation.SetRemoteServices(v) + return _u +} + +// SetNillableRemoteServices sets the "remote_services" field if the given value is not nil. +func (_u *ProviderProfileUpdate) SetNillableRemoteServices(v *bool) *ProviderProfileUpdate { + if v != nil { + _u.SetRemoteServices(*v) + } + return _u +} + +// SetUpdatedAt sets the "updated_at" field. +func (_u *ProviderProfileUpdate) SetUpdatedAt(v time.Time) *ProviderProfileUpdate { + _u.mutation.SetUpdatedAt(v) + return _u +} + +// AddServiceLocalityIDs adds the "service_localities" edge to the Locality entity by IDs. +func (_u *ProviderProfileUpdate) AddServiceLocalityIDs(ids ...uuid.UUID) *ProviderProfileUpdate { + _u.mutation.AddServiceLocalityIDs(ids...) + return _u +} + +// AddServiceLocalities adds the "service_localities" edges to the Locality entity. +func (_u *ProviderProfileUpdate) AddServiceLocalities(v ...*Locality) *ProviderProfileUpdate { + ids := make([]uuid.UUID, len(v)) + for i := range v { + ids[i] = v[i].ID + } + return _u.AddServiceLocalityIDs(ids...) +} + +// AddSpokenLanguageIDs adds the "spoken_languages" edge to the SpokenLanguage entity by IDs. +func (_u *ProviderProfileUpdate) AddSpokenLanguageIDs(ids ...string) *ProviderProfileUpdate { + _u.mutation.AddSpokenLanguageIDs(ids...) + return _u +} + +// AddSpokenLanguages adds the "spoken_languages" edges to the SpokenLanguage entity. +func (_u *ProviderProfileUpdate) AddSpokenLanguages(v ...*SpokenLanguage) *ProviderProfileUpdate { + ids := make([]string, len(v)) + for i := range v { + ids[i] = v[i].ID + } + return _u.AddSpokenLanguageIDs(ids...) +} + +// Mutation returns the ProviderProfileMutation object of the builder. +func (_u *ProviderProfileUpdate) Mutation() *ProviderProfileMutation { + return _u.mutation +} + +// ClearServiceLocalities clears all "service_localities" edges to the Locality entity. +func (_u *ProviderProfileUpdate) ClearServiceLocalities() *ProviderProfileUpdate { + _u.mutation.ClearServiceLocalities() + return _u +} + +// RemoveServiceLocalityIDs removes the "service_localities" edge to Locality entities by IDs. +func (_u *ProviderProfileUpdate) RemoveServiceLocalityIDs(ids ...uuid.UUID) *ProviderProfileUpdate { + _u.mutation.RemoveServiceLocalityIDs(ids...) + return _u +} + +// RemoveServiceLocalities removes "service_localities" edges to Locality entities. +func (_u *ProviderProfileUpdate) RemoveServiceLocalities(v ...*Locality) *ProviderProfileUpdate { + ids := make([]uuid.UUID, len(v)) + for i := range v { + ids[i] = v[i].ID + } + return _u.RemoveServiceLocalityIDs(ids...) +} + +// ClearSpokenLanguages clears all "spoken_languages" edges to the SpokenLanguage entity. +func (_u *ProviderProfileUpdate) ClearSpokenLanguages() *ProviderProfileUpdate { + _u.mutation.ClearSpokenLanguages() + return _u +} + +// RemoveSpokenLanguageIDs removes the "spoken_languages" edge to SpokenLanguage entities by IDs. +func (_u *ProviderProfileUpdate) RemoveSpokenLanguageIDs(ids ...string) *ProviderProfileUpdate { + _u.mutation.RemoveSpokenLanguageIDs(ids...) + return _u +} + +// RemoveSpokenLanguages removes "spoken_languages" edges to SpokenLanguage entities. +func (_u *ProviderProfileUpdate) RemoveSpokenLanguages(v ...*SpokenLanguage) *ProviderProfileUpdate { + ids := make([]string, len(v)) + for i := range v { + ids[i] = v[i].ID + } + return _u.RemoveSpokenLanguageIDs(ids...) +} + +// Save executes the query and returns the number of nodes affected by the update operation. +func (_u *ProviderProfileUpdate) Save(ctx context.Context) (int, error) { + _u.defaults() + return withHooks(ctx, _u.sqlSave, _u.mutation, _u.hooks) +} + +// SaveX is like Save, but panics if an error occurs. +func (_u *ProviderProfileUpdate) SaveX(ctx context.Context) int { + affected, err := _u.Save(ctx) + if err != nil { + panic(err) + } + return affected +} + +// Exec executes the query. +func (_u *ProviderProfileUpdate) Exec(ctx context.Context) error { + _, err := _u.Save(ctx) + return err +} + +// ExecX is like Exec, but panics if an error occurs. +func (_u *ProviderProfileUpdate) ExecX(ctx context.Context) { + if err := _u.Exec(ctx); err != nil { + panic(err) + } +} + +// defaults sets the default values of the builder before save. +func (_u *ProviderProfileUpdate) defaults() { + if _, ok := _u.mutation.UpdatedAt(); !ok { + v := providerprofile.UpdateDefaultUpdatedAt() + _u.mutation.SetUpdatedAt(v) + } +} + +// check runs all checks and user-defined validators on the builder. +func (_u *ProviderProfileUpdate) check() error { + if v, ok := _u.mutation.DisplayName(); ok { + if err := providerprofile.DisplayNameValidator(v); err != nil { + return &ValidationError{Name: "display_name", err: fmt.Errorf(`ent: validator failed for field "ProviderProfile.display_name": %w`, err)} + } + } + if v, ok := _u.mutation.ProviderType(); ok { + if err := providerprofile.ProviderTypeValidator(v); err != nil { + return &ValidationError{Name: "provider_type", err: fmt.Errorf(`ent: validator failed for field "ProviderProfile.provider_type": %w`, err)} + } + } + if v, ok := _u.mutation.Bio(); ok { + if err := providerprofile.BioValidator(v); err != nil { + return &ValidationError{Name: "bio", err: fmt.Errorf(`ent: validator failed for field "ProviderProfile.bio": %w`, err)} + } + } + if v, ok := _u.mutation.MaxTravelDistanceKm(); ok { + if err := providerprofile.MaxTravelDistanceKmValidator(v); err != nil { + return &ValidationError{Name: "max_travel_distance_km", err: fmt.Errorf(`ent: validator failed for field "ProviderProfile.max_travel_distance_km": %w`, err)} + } + } + return nil +} + +func (_u *ProviderProfileUpdate) sqlSave(ctx context.Context) (_node int, err error) { + if err := _u.check(); err != nil { + return _node, err + } + _spec := sqlgraph.NewUpdateSpec(providerprofile.Table, providerprofile.Columns, sqlgraph.NewFieldSpec(providerprofile.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.DisplayName(); ok { + _spec.SetField(providerprofile.FieldDisplayName, field.TypeString, value) + } + if value, ok := _u.mutation.ProviderType(); ok { + _spec.SetField(providerprofile.FieldProviderType, field.TypeString, value) + } + if value, ok := _u.mutation.Bio(); ok { + _spec.SetField(providerprofile.FieldBio, field.TypeString, value) + } + if value, ok := _u.mutation.PrimaryLocalityID(); ok { + _spec.SetField(providerprofile.FieldPrimaryLocalityID, field.TypeUUID, value) + } + if value, ok := _u.mutation.MaxTravelDistanceKm(); ok { + _spec.SetField(providerprofile.FieldMaxTravelDistanceKm, field.TypeInt, value) + } + if value, ok := _u.mutation.AddedMaxTravelDistanceKm(); ok { + _spec.AddField(providerprofile.FieldMaxTravelDistanceKm, field.TypeInt, value) + } + if value, ok := _u.mutation.TravelsToCustomer(); ok { + _spec.SetField(providerprofile.FieldTravelsToCustomer, field.TypeBool, value) + } + if value, ok := _u.mutation.ReceivesCustomer(); ok { + _spec.SetField(providerprofile.FieldReceivesCustomer, field.TypeBool, value) + } + if value, ok := _u.mutation.RemoteServices(); ok { + _spec.SetField(providerprofile.FieldRemoteServices, field.TypeBool, value) + } + if value, ok := _u.mutation.UpdatedAt(); ok { + _spec.SetField(providerprofile.FieldUpdatedAt, field.TypeTime, value) + } + if _u.mutation.ServiceLocalitiesCleared() { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.M2M, + Inverse: false, + Table: providerprofile.ServiceLocalitiesTable, + Columns: providerprofile.ServiceLocalitiesPrimaryKey, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(locality.FieldID, field.TypeUUID), + }, + } + _spec.Edges.Clear = append(_spec.Edges.Clear, edge) + } + if nodes := _u.mutation.RemovedServiceLocalitiesIDs(); len(nodes) > 0 && !_u.mutation.ServiceLocalitiesCleared() { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.M2M, + Inverse: false, + Table: providerprofile.ServiceLocalitiesTable, + Columns: providerprofile.ServiceLocalitiesPrimaryKey, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(locality.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.ServiceLocalitiesIDs(); len(nodes) > 0 { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.M2M, + Inverse: false, + Table: providerprofile.ServiceLocalitiesTable, + Columns: providerprofile.ServiceLocalitiesPrimaryKey, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(locality.FieldID, field.TypeUUID), + }, + } + for _, k := range nodes { + edge.Target.Nodes = append(edge.Target.Nodes, k) + } + _spec.Edges.Add = append(_spec.Edges.Add, edge) + } + if _u.mutation.SpokenLanguagesCleared() { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.M2M, + Inverse: false, + Table: providerprofile.SpokenLanguagesTable, + Columns: providerprofile.SpokenLanguagesPrimaryKey, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(spokenlanguage.FieldID, field.TypeString), + }, + } + _spec.Edges.Clear = append(_spec.Edges.Clear, edge) + } + if nodes := _u.mutation.RemovedSpokenLanguagesIDs(); len(nodes) > 0 && !_u.mutation.SpokenLanguagesCleared() { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.M2M, + Inverse: false, + Table: providerprofile.SpokenLanguagesTable, + Columns: providerprofile.SpokenLanguagesPrimaryKey, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(spokenlanguage.FieldID, field.TypeString), + }, + } + for _, k := range nodes { + edge.Target.Nodes = append(edge.Target.Nodes, k) + } + _spec.Edges.Clear = append(_spec.Edges.Clear, edge) + } + if nodes := _u.mutation.SpokenLanguagesIDs(); len(nodes) > 0 { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.M2M, + Inverse: false, + Table: providerprofile.SpokenLanguagesTable, + Columns: providerprofile.SpokenLanguagesPrimaryKey, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(spokenlanguage.FieldID, field.TypeString), + }, + } + 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{providerprofile.Label} + } else if sqlgraph.IsConstraintError(err) { + err = &ConstraintError{msg: err.Error(), wrap: err} + } + return 0, err + } + _u.mutation.done = true + return _node, nil +} + +// ProviderProfileUpdateOne is the builder for updating a single ProviderProfile entity. +type ProviderProfileUpdateOne struct { + config + fields []string + hooks []Hook + mutation *ProviderProfileMutation +} + +// SetDisplayName sets the "display_name" field. +func (_u *ProviderProfileUpdateOne) SetDisplayName(v string) *ProviderProfileUpdateOne { + _u.mutation.SetDisplayName(v) + return _u +} + +// SetNillableDisplayName sets the "display_name" field if the given value is not nil. +func (_u *ProviderProfileUpdateOne) SetNillableDisplayName(v *string) *ProviderProfileUpdateOne { + if v != nil { + _u.SetDisplayName(*v) + } + return _u +} + +// SetProviderType sets the "provider_type" field. +func (_u *ProviderProfileUpdateOne) SetProviderType(v string) *ProviderProfileUpdateOne { + _u.mutation.SetProviderType(v) + return _u +} + +// SetNillableProviderType sets the "provider_type" field if the given value is not nil. +func (_u *ProviderProfileUpdateOne) SetNillableProviderType(v *string) *ProviderProfileUpdateOne { + if v != nil { + _u.SetProviderType(*v) + } + return _u +} + +// SetBio sets the "bio" field. +func (_u *ProviderProfileUpdateOne) SetBio(v string) *ProviderProfileUpdateOne { + _u.mutation.SetBio(v) + return _u +} + +// SetNillableBio sets the "bio" field if the given value is not nil. +func (_u *ProviderProfileUpdateOne) SetNillableBio(v *string) *ProviderProfileUpdateOne { + if v != nil { + _u.SetBio(*v) + } + return _u +} + +// SetPrimaryLocalityID sets the "primary_locality_id" field. +func (_u *ProviderProfileUpdateOne) SetPrimaryLocalityID(v uuid.UUID) *ProviderProfileUpdateOne { + _u.mutation.SetPrimaryLocalityID(v) + return _u +} + +// SetNillablePrimaryLocalityID sets the "primary_locality_id" field if the given value is not nil. +func (_u *ProviderProfileUpdateOne) SetNillablePrimaryLocalityID(v *uuid.UUID) *ProviderProfileUpdateOne { + if v != nil { + _u.SetPrimaryLocalityID(*v) + } + return _u +} + +// SetMaxTravelDistanceKm sets the "max_travel_distance_km" field. +func (_u *ProviderProfileUpdateOne) SetMaxTravelDistanceKm(v int) *ProviderProfileUpdateOne { + _u.mutation.ResetMaxTravelDistanceKm() + _u.mutation.SetMaxTravelDistanceKm(v) + return _u +} + +// SetNillableMaxTravelDistanceKm sets the "max_travel_distance_km" field if the given value is not nil. +func (_u *ProviderProfileUpdateOne) SetNillableMaxTravelDistanceKm(v *int) *ProviderProfileUpdateOne { + if v != nil { + _u.SetMaxTravelDistanceKm(*v) + } + return _u +} + +// AddMaxTravelDistanceKm adds value to the "max_travel_distance_km" field. +func (_u *ProviderProfileUpdateOne) AddMaxTravelDistanceKm(v int) *ProviderProfileUpdateOne { + _u.mutation.AddMaxTravelDistanceKm(v) + return _u +} + +// SetTravelsToCustomer sets the "travels_to_customer" field. +func (_u *ProviderProfileUpdateOne) SetTravelsToCustomer(v bool) *ProviderProfileUpdateOne { + _u.mutation.SetTravelsToCustomer(v) + return _u +} + +// SetNillableTravelsToCustomer sets the "travels_to_customer" field if the given value is not nil. +func (_u *ProviderProfileUpdateOne) SetNillableTravelsToCustomer(v *bool) *ProviderProfileUpdateOne { + if v != nil { + _u.SetTravelsToCustomer(*v) + } + return _u +} + +// SetReceivesCustomer sets the "receives_customer" field. +func (_u *ProviderProfileUpdateOne) SetReceivesCustomer(v bool) *ProviderProfileUpdateOne { + _u.mutation.SetReceivesCustomer(v) + return _u +} + +// SetNillableReceivesCustomer sets the "receives_customer" field if the given value is not nil. +func (_u *ProviderProfileUpdateOne) SetNillableReceivesCustomer(v *bool) *ProviderProfileUpdateOne { + if v != nil { + _u.SetReceivesCustomer(*v) + } + return _u +} + +// SetRemoteServices sets the "remote_services" field. +func (_u *ProviderProfileUpdateOne) SetRemoteServices(v bool) *ProviderProfileUpdateOne { + _u.mutation.SetRemoteServices(v) + return _u +} + +// SetNillableRemoteServices sets the "remote_services" field if the given value is not nil. +func (_u *ProviderProfileUpdateOne) SetNillableRemoteServices(v *bool) *ProviderProfileUpdateOne { + if v != nil { + _u.SetRemoteServices(*v) + } + return _u +} + +// SetUpdatedAt sets the "updated_at" field. +func (_u *ProviderProfileUpdateOne) SetUpdatedAt(v time.Time) *ProviderProfileUpdateOne { + _u.mutation.SetUpdatedAt(v) + return _u +} + +// AddServiceLocalityIDs adds the "service_localities" edge to the Locality entity by IDs. +func (_u *ProviderProfileUpdateOne) AddServiceLocalityIDs(ids ...uuid.UUID) *ProviderProfileUpdateOne { + _u.mutation.AddServiceLocalityIDs(ids...) + return _u +} + +// AddServiceLocalities adds the "service_localities" edges to the Locality entity. +func (_u *ProviderProfileUpdateOne) AddServiceLocalities(v ...*Locality) *ProviderProfileUpdateOne { + ids := make([]uuid.UUID, len(v)) + for i := range v { + ids[i] = v[i].ID + } + return _u.AddServiceLocalityIDs(ids...) +} + +// AddSpokenLanguageIDs adds the "spoken_languages" edge to the SpokenLanguage entity by IDs. +func (_u *ProviderProfileUpdateOne) AddSpokenLanguageIDs(ids ...string) *ProviderProfileUpdateOne { + _u.mutation.AddSpokenLanguageIDs(ids...) + return _u +} + +// AddSpokenLanguages adds the "spoken_languages" edges to the SpokenLanguage entity. +func (_u *ProviderProfileUpdateOne) AddSpokenLanguages(v ...*SpokenLanguage) *ProviderProfileUpdateOne { + ids := make([]string, len(v)) + for i := range v { + ids[i] = v[i].ID + } + return _u.AddSpokenLanguageIDs(ids...) +} + +// Mutation returns the ProviderProfileMutation object of the builder. +func (_u *ProviderProfileUpdateOne) Mutation() *ProviderProfileMutation { + return _u.mutation +} + +// ClearServiceLocalities clears all "service_localities" edges to the Locality entity. +func (_u *ProviderProfileUpdateOne) ClearServiceLocalities() *ProviderProfileUpdateOne { + _u.mutation.ClearServiceLocalities() + return _u +} + +// RemoveServiceLocalityIDs removes the "service_localities" edge to Locality entities by IDs. +func (_u *ProviderProfileUpdateOne) RemoveServiceLocalityIDs(ids ...uuid.UUID) *ProviderProfileUpdateOne { + _u.mutation.RemoveServiceLocalityIDs(ids...) + return _u +} + +// RemoveServiceLocalities removes "service_localities" edges to Locality entities. +func (_u *ProviderProfileUpdateOne) RemoveServiceLocalities(v ...*Locality) *ProviderProfileUpdateOne { + ids := make([]uuid.UUID, len(v)) + for i := range v { + ids[i] = v[i].ID + } + return _u.RemoveServiceLocalityIDs(ids...) +} + +// ClearSpokenLanguages clears all "spoken_languages" edges to the SpokenLanguage entity. +func (_u *ProviderProfileUpdateOne) ClearSpokenLanguages() *ProviderProfileUpdateOne { + _u.mutation.ClearSpokenLanguages() + return _u +} + +// RemoveSpokenLanguageIDs removes the "spoken_languages" edge to SpokenLanguage entities by IDs. +func (_u *ProviderProfileUpdateOne) RemoveSpokenLanguageIDs(ids ...string) *ProviderProfileUpdateOne { + _u.mutation.RemoveSpokenLanguageIDs(ids...) + return _u +} + +// RemoveSpokenLanguages removes "spoken_languages" edges to SpokenLanguage entities. +func (_u *ProviderProfileUpdateOne) RemoveSpokenLanguages(v ...*SpokenLanguage) *ProviderProfileUpdateOne { + ids := make([]string, len(v)) + for i := range v { + ids[i] = v[i].ID + } + return _u.RemoveSpokenLanguageIDs(ids...) +} + +// Where appends a list predicates to the ProviderProfileUpdate builder. +func (_u *ProviderProfileUpdateOne) Where(ps ...predicate.ProviderProfile) *ProviderProfileUpdateOne { + _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 *ProviderProfileUpdateOne) Select(field string, fields ...string) *ProviderProfileUpdateOne { + _u.fields = append([]string{field}, fields...) + return _u +} + +// Save executes the query and returns the updated ProviderProfile entity. +func (_u *ProviderProfileUpdateOne) Save(ctx context.Context) (*ProviderProfile, error) { + _u.defaults() + return withHooks(ctx, _u.sqlSave, _u.mutation, _u.hooks) +} + +// SaveX is like Save, but panics if an error occurs. +func (_u *ProviderProfileUpdateOne) SaveX(ctx context.Context) *ProviderProfile { + node, err := _u.Save(ctx) + if err != nil { + panic(err) + } + return node +} + +// Exec executes the query on the entity. +func (_u *ProviderProfileUpdateOne) Exec(ctx context.Context) error { + _, err := _u.Save(ctx) + return err +} + +// ExecX is like Exec, but panics if an error occurs. +func (_u *ProviderProfileUpdateOne) ExecX(ctx context.Context) { + if err := _u.Exec(ctx); err != nil { + panic(err) + } +} + +// defaults sets the default values of the builder before save. +func (_u *ProviderProfileUpdateOne) defaults() { + if _, ok := _u.mutation.UpdatedAt(); !ok { + v := providerprofile.UpdateDefaultUpdatedAt() + _u.mutation.SetUpdatedAt(v) + } +} + +// check runs all checks and user-defined validators on the builder. +func (_u *ProviderProfileUpdateOne) check() error { + if v, ok := _u.mutation.DisplayName(); ok { + if err := providerprofile.DisplayNameValidator(v); err != nil { + return &ValidationError{Name: "display_name", err: fmt.Errorf(`ent: validator failed for field "ProviderProfile.display_name": %w`, err)} + } + } + if v, ok := _u.mutation.ProviderType(); ok { + if err := providerprofile.ProviderTypeValidator(v); err != nil { + return &ValidationError{Name: "provider_type", err: fmt.Errorf(`ent: validator failed for field "ProviderProfile.provider_type": %w`, err)} + } + } + if v, ok := _u.mutation.Bio(); ok { + if err := providerprofile.BioValidator(v); err != nil { + return &ValidationError{Name: "bio", err: fmt.Errorf(`ent: validator failed for field "ProviderProfile.bio": %w`, err)} + } + } + if v, ok := _u.mutation.MaxTravelDistanceKm(); ok { + if err := providerprofile.MaxTravelDistanceKmValidator(v); err != nil { + return &ValidationError{Name: "max_travel_distance_km", err: fmt.Errorf(`ent: validator failed for field "ProviderProfile.max_travel_distance_km": %w`, err)} + } + } + return nil +} + +func (_u *ProviderProfileUpdateOne) sqlSave(ctx context.Context) (_node *ProviderProfile, err error) { + if err := _u.check(); err != nil { + return _node, err + } + _spec := sqlgraph.NewUpdateSpec(providerprofile.Table, providerprofile.Columns, sqlgraph.NewFieldSpec(providerprofile.FieldID, field.TypeUUID)) + id, ok := _u.mutation.ID() + if !ok { + return nil, &ValidationError{Name: "id", err: errors.New(`ent: missing "ProviderProfile.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, providerprofile.FieldID) + for _, f := range fields { + if !providerprofile.ValidColumn(f) { + return nil, &ValidationError{Name: f, err: fmt.Errorf("ent: invalid field %q for query", f)} + } + if f != providerprofile.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.DisplayName(); ok { + _spec.SetField(providerprofile.FieldDisplayName, field.TypeString, value) + } + if value, ok := _u.mutation.ProviderType(); ok { + _spec.SetField(providerprofile.FieldProviderType, field.TypeString, value) + } + if value, ok := _u.mutation.Bio(); ok { + _spec.SetField(providerprofile.FieldBio, field.TypeString, value) + } + if value, ok := _u.mutation.PrimaryLocalityID(); ok { + _spec.SetField(providerprofile.FieldPrimaryLocalityID, field.TypeUUID, value) + } + if value, ok := _u.mutation.MaxTravelDistanceKm(); ok { + _spec.SetField(providerprofile.FieldMaxTravelDistanceKm, field.TypeInt, value) + } + if value, ok := _u.mutation.AddedMaxTravelDistanceKm(); ok { + _spec.AddField(providerprofile.FieldMaxTravelDistanceKm, field.TypeInt, value) + } + if value, ok := _u.mutation.TravelsToCustomer(); ok { + _spec.SetField(providerprofile.FieldTravelsToCustomer, field.TypeBool, value) + } + if value, ok := _u.mutation.ReceivesCustomer(); ok { + _spec.SetField(providerprofile.FieldReceivesCustomer, field.TypeBool, value) + } + if value, ok := _u.mutation.RemoteServices(); ok { + _spec.SetField(providerprofile.FieldRemoteServices, field.TypeBool, value) + } + if value, ok := _u.mutation.UpdatedAt(); ok { + _spec.SetField(providerprofile.FieldUpdatedAt, field.TypeTime, value) + } + if _u.mutation.ServiceLocalitiesCleared() { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.M2M, + Inverse: false, + Table: providerprofile.ServiceLocalitiesTable, + Columns: providerprofile.ServiceLocalitiesPrimaryKey, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(locality.FieldID, field.TypeUUID), + }, + } + _spec.Edges.Clear = append(_spec.Edges.Clear, edge) + } + if nodes := _u.mutation.RemovedServiceLocalitiesIDs(); len(nodes) > 0 && !_u.mutation.ServiceLocalitiesCleared() { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.M2M, + Inverse: false, + Table: providerprofile.ServiceLocalitiesTable, + Columns: providerprofile.ServiceLocalitiesPrimaryKey, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(locality.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.ServiceLocalitiesIDs(); len(nodes) > 0 { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.M2M, + Inverse: false, + Table: providerprofile.ServiceLocalitiesTable, + Columns: providerprofile.ServiceLocalitiesPrimaryKey, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(locality.FieldID, field.TypeUUID), + }, + } + for _, k := range nodes { + edge.Target.Nodes = append(edge.Target.Nodes, k) + } + _spec.Edges.Add = append(_spec.Edges.Add, edge) + } + if _u.mutation.SpokenLanguagesCleared() { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.M2M, + Inverse: false, + Table: providerprofile.SpokenLanguagesTable, + Columns: providerprofile.SpokenLanguagesPrimaryKey, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(spokenlanguage.FieldID, field.TypeString), + }, + } + _spec.Edges.Clear = append(_spec.Edges.Clear, edge) + } + if nodes := _u.mutation.RemovedSpokenLanguagesIDs(); len(nodes) > 0 && !_u.mutation.SpokenLanguagesCleared() { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.M2M, + Inverse: false, + Table: providerprofile.SpokenLanguagesTable, + Columns: providerprofile.SpokenLanguagesPrimaryKey, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(spokenlanguage.FieldID, field.TypeString), + }, + } + for _, k := range nodes { + edge.Target.Nodes = append(edge.Target.Nodes, k) + } + _spec.Edges.Clear = append(_spec.Edges.Clear, edge) + } + if nodes := _u.mutation.SpokenLanguagesIDs(); len(nodes) > 0 { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.M2M, + Inverse: false, + Table: providerprofile.SpokenLanguagesTable, + Columns: providerprofile.SpokenLanguagesPrimaryKey, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(spokenlanguage.FieldID, field.TypeString), + }, + } + for _, k := range nodes { + edge.Target.Nodes = append(edge.Target.Nodes, k) + } + _spec.Edges.Add = append(_spec.Edges.Add, edge) + } + _node = &ProviderProfile{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{providerprofile.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/backend/ent/providerservicelocality.go b/backend/ent/providerservicelocality.go new file mode 100644 index 0000000..033a898 --- /dev/null +++ b/backend/ent/providerservicelocality.go @@ -0,0 +1,152 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "fmt" + "strings" + + "entgo.io/ent" + "entgo.io/ent/dialect/sql" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/locality" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/providerprofile" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/providerservicelocality" + "github.com/google/uuid" +) + +// ProviderServiceLocality is the model entity for the ProviderServiceLocality schema. +type ProviderServiceLocality struct { + config `json:"-"` + // InternalUserID holds the value of the "internal_user_id" field. + InternalUserID uuid.UUID `json:"internal_user_id,omitempty"` + // LocalityID holds the value of the "locality_id" field. + LocalityID uuid.UUID `json:"locality_id,omitempty"` + // Edges holds the relations/edges for other nodes in the graph. + // The values are being populated by the ProviderServiceLocalityQuery when eager-loading is set. + Edges ProviderServiceLocalityEdges `json:"edges"` + selectValues sql.SelectValues +} + +// ProviderServiceLocalityEdges holds the relations/edges for other nodes in the graph. +type ProviderServiceLocalityEdges struct { + // Profile holds the value of the profile edge. + Profile *ProviderProfile `json:"profile,omitempty"` + // Locality holds the value of the locality edge. + Locality *Locality `json:"locality,omitempty"` + // loadedTypes holds the information for reporting if a + // type was loaded (or requested) in eager-loading or not. + loadedTypes [2]bool +} + +// ProfileOrErr returns the Profile value or an error if the edge +// was not loaded in eager-loading, or loaded but was not found. +func (e ProviderServiceLocalityEdges) ProfileOrErr() (*ProviderProfile, error) { + if e.Profile != nil { + return e.Profile, nil + } else if e.loadedTypes[0] { + return nil, &NotFoundError{label: providerprofile.Label} + } + return nil, &NotLoadedError{edge: "profile"} +} + +// LocalityOrErr returns the Locality value or an error if the edge +// was not loaded in eager-loading, or loaded but was not found. +func (e ProviderServiceLocalityEdges) LocalityOrErr() (*Locality, error) { + if e.Locality != nil { + return e.Locality, nil + } else if e.loadedTypes[1] { + return nil, &NotFoundError{label: locality.Label} + } + return nil, &NotLoadedError{edge: "locality"} +} + +// scanValues returns the types for scanning values from sql.Rows. +func (*ProviderServiceLocality) scanValues(columns []string) ([]any, error) { + values := make([]any, len(columns)) + for i := range columns { + switch columns[i] { + case providerservicelocality.FieldInternalUserID, providerservicelocality.FieldLocalityID: + 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 ProviderServiceLocality fields. +func (_m *ProviderServiceLocality) 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 providerservicelocality.FieldInternalUserID: + if value, ok := values[i].(*uuid.UUID); !ok { + return fmt.Errorf("unexpected type %T for field internal_user_id", values[i]) + } else if value != nil { + _m.InternalUserID = *value + } + case providerservicelocality.FieldLocalityID: + if value, ok := values[i].(*uuid.UUID); !ok { + return fmt.Errorf("unexpected type %T for field locality_id", values[i]) + } else if value != nil { + _m.LocalityID = *value + } + default: + _m.selectValues.Set(columns[i], values[i]) + } + } + return nil +} + +// Value returns the ent.Value that was dynamically selected and assigned to the ProviderServiceLocality. +// This includes values selected through modifiers, order, etc. +func (_m *ProviderServiceLocality) Value(name string) (ent.Value, error) { + return _m.selectValues.Get(name) +} + +// QueryProfile queries the "profile" edge of the ProviderServiceLocality entity. +func (_m *ProviderServiceLocality) QueryProfile() *ProviderProfileQuery { + return NewProviderServiceLocalityClient(_m.config).QueryProfile(_m) +} + +// QueryLocality queries the "locality" edge of the ProviderServiceLocality entity. +func (_m *ProviderServiceLocality) QueryLocality() *LocalityQuery { + return NewProviderServiceLocalityClient(_m.config).QueryLocality(_m) +} + +// Update returns a builder for updating this ProviderServiceLocality. +// Note that you need to call ProviderServiceLocality.Unwrap() before calling this method if this ProviderServiceLocality +// was returned from a transaction, and the transaction was committed or rolled back. +func (_m *ProviderServiceLocality) Update() *ProviderServiceLocalityUpdateOne { + return NewProviderServiceLocalityClient(_m.config).UpdateOne(_m) +} + +// Unwrap unwraps the ProviderServiceLocality 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 *ProviderServiceLocality) Unwrap() *ProviderServiceLocality { + _tx, ok := _m.config.driver.(*txDriver) + if !ok { + panic("ent: ProviderServiceLocality is not a transactional entity") + } + _m.config.driver = _tx.drv + return _m +} + +// String implements the fmt.Stringer. +func (_m *ProviderServiceLocality) String() string { + var builder strings.Builder + builder.WriteString("ProviderServiceLocality(") + builder.WriteString("internal_user_id=") + builder.WriteString(fmt.Sprintf("%v", _m.InternalUserID)) + builder.WriteString(", ") + builder.WriteString("locality_id=") + builder.WriteString(fmt.Sprintf("%v", _m.LocalityID)) + builder.WriteByte(')') + return builder.String() +} + +// ProviderServiceLocalities is a parsable slice of ProviderServiceLocality. +type ProviderServiceLocalities []*ProviderServiceLocality diff --git a/backend/ent/providerservicelocality/providerservicelocality.go b/backend/ent/providerservicelocality/providerservicelocality.go new file mode 100644 index 0000000..aef9951 --- /dev/null +++ b/backend/ent/providerservicelocality/providerservicelocality.go @@ -0,0 +1,98 @@ +// Code generated by ent, DO NOT EDIT. + +package providerservicelocality + +import ( + "entgo.io/ent/dialect/sql" + "entgo.io/ent/dialect/sql/sqlgraph" +) + +const ( + // Label holds the string label denoting the providerservicelocality type in the database. + Label = "provider_service_locality" + // FieldInternalUserID holds the string denoting the internal_user_id field in the database. + FieldInternalUserID = "internal_user_id" + // FieldLocalityID holds the string denoting the locality_id field in the database. + FieldLocalityID = "locality_id" + // EdgeProfile holds the string denoting the profile edge name in mutations. + EdgeProfile = "profile" + // EdgeLocality holds the string denoting the locality edge name in mutations. + EdgeLocality = "locality" + // ProviderProfileFieldID holds the string denoting the ID field of the ProviderProfile. + ProviderProfileFieldID = "internal_user_id" + // LocalityFieldID holds the string denoting the ID field of the Locality. + LocalityFieldID = "id" + // Table holds the table name of the providerservicelocality in the database. + Table = "provider_service_localities" + // ProfileTable is the table that holds the profile relation/edge. + ProfileTable = "provider_service_localities" + // ProfileInverseTable is the table name for the ProviderProfile entity. + // It exists in this package in order to avoid circular dependency with the "providerprofile" package. + ProfileInverseTable = "provider_profiles" + // ProfileColumn is the table column denoting the profile relation/edge. + ProfileColumn = "internal_user_id" + // LocalityTable is the table that holds the locality relation/edge. + LocalityTable = "provider_service_localities" + // LocalityInverseTable is the table name for the Locality entity. + // It exists in this package in order to avoid circular dependency with the "locality" package. + LocalityInverseTable = "localities" + // LocalityColumn is the table column denoting the locality relation/edge. + LocalityColumn = "locality_id" +) + +// Columns holds all SQL columns for providerservicelocality fields. +var Columns = []string{ + FieldInternalUserID, + FieldLocalityID, +} + +// 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 ProviderServiceLocality queries. +type OrderOption func(*sql.Selector) + +// ByInternalUserID orders the results by the internal_user_id field. +func ByInternalUserID(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldInternalUserID, opts...).ToFunc() +} + +// ByLocalityID orders the results by the locality_id field. +func ByLocalityID(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldLocalityID, opts...).ToFunc() +} + +// ByProfileField orders the results by profile field. +func ByProfileField(field string, opts ...sql.OrderTermOption) OrderOption { + return func(s *sql.Selector) { + sqlgraph.OrderByNeighborTerms(s, newProfileStep(), sql.OrderByField(field, opts...)) + } +} + +// ByLocalityField orders the results by locality field. +func ByLocalityField(field string, opts ...sql.OrderTermOption) OrderOption { + return func(s *sql.Selector) { + sqlgraph.OrderByNeighborTerms(s, newLocalityStep(), sql.OrderByField(field, opts...)) + } +} +func newProfileStep() *sqlgraph.Step { + return sqlgraph.NewStep( + sqlgraph.From(Table, ProfileColumn), + sqlgraph.To(ProfileInverseTable, ProviderProfileFieldID), + sqlgraph.Edge(sqlgraph.M2O, false, ProfileTable, ProfileColumn), + ) +} +func newLocalityStep() *sqlgraph.Step { + return sqlgraph.NewStep( + sqlgraph.From(Table, LocalityColumn), + sqlgraph.To(LocalityInverseTable, LocalityFieldID), + sqlgraph.Edge(sqlgraph.M2O, false, LocalityTable, LocalityColumn), + ) +} diff --git a/backend/ent/providerservicelocality/where.go b/backend/ent/providerservicelocality/where.go new file mode 100644 index 0000000..ae7505d --- /dev/null +++ b/backend/ent/providerservicelocality/where.go @@ -0,0 +1,121 @@ +// Code generated by ent, DO NOT EDIT. + +package providerservicelocality + +import ( + "entgo.io/ent/dialect/sql" + "entgo.io/ent/dialect/sql/sqlgraph" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/predicate" + "github.com/google/uuid" +) + +// InternalUserID applies equality check predicate on the "internal_user_id" field. It's identical to InternalUserIDEQ. +func InternalUserID(v uuid.UUID) predicate.ProviderServiceLocality { + return predicate.ProviderServiceLocality(sql.FieldEQ(FieldInternalUserID, v)) +} + +// LocalityID applies equality check predicate on the "locality_id" field. It's identical to LocalityIDEQ. +func LocalityID(v uuid.UUID) predicate.ProviderServiceLocality { + return predicate.ProviderServiceLocality(sql.FieldEQ(FieldLocalityID, v)) +} + +// InternalUserIDEQ applies the EQ predicate on the "internal_user_id" field. +func InternalUserIDEQ(v uuid.UUID) predicate.ProviderServiceLocality { + return predicate.ProviderServiceLocality(sql.FieldEQ(FieldInternalUserID, v)) +} + +// InternalUserIDNEQ applies the NEQ predicate on the "internal_user_id" field. +func InternalUserIDNEQ(v uuid.UUID) predicate.ProviderServiceLocality { + return predicate.ProviderServiceLocality(sql.FieldNEQ(FieldInternalUserID, v)) +} + +// InternalUserIDIn applies the In predicate on the "internal_user_id" field. +func InternalUserIDIn(vs ...uuid.UUID) predicate.ProviderServiceLocality { + return predicate.ProviderServiceLocality(sql.FieldIn(FieldInternalUserID, vs...)) +} + +// InternalUserIDNotIn applies the NotIn predicate on the "internal_user_id" field. +func InternalUserIDNotIn(vs ...uuid.UUID) predicate.ProviderServiceLocality { + return predicate.ProviderServiceLocality(sql.FieldNotIn(FieldInternalUserID, vs...)) +} + +// LocalityIDEQ applies the EQ predicate on the "locality_id" field. +func LocalityIDEQ(v uuid.UUID) predicate.ProviderServiceLocality { + return predicate.ProviderServiceLocality(sql.FieldEQ(FieldLocalityID, v)) +} + +// LocalityIDNEQ applies the NEQ predicate on the "locality_id" field. +func LocalityIDNEQ(v uuid.UUID) predicate.ProviderServiceLocality { + return predicate.ProviderServiceLocality(sql.FieldNEQ(FieldLocalityID, v)) +} + +// LocalityIDIn applies the In predicate on the "locality_id" field. +func LocalityIDIn(vs ...uuid.UUID) predicate.ProviderServiceLocality { + return predicate.ProviderServiceLocality(sql.FieldIn(FieldLocalityID, vs...)) +} + +// LocalityIDNotIn applies the NotIn predicate on the "locality_id" field. +func LocalityIDNotIn(vs ...uuid.UUID) predicate.ProviderServiceLocality { + return predicate.ProviderServiceLocality(sql.FieldNotIn(FieldLocalityID, vs...)) +} + +// HasProfile applies the HasEdge predicate on the "profile" edge. +func HasProfile() predicate.ProviderServiceLocality { + return predicate.ProviderServiceLocality(func(s *sql.Selector) { + step := sqlgraph.NewStep( + sqlgraph.From(Table, ProfileColumn), + sqlgraph.Edge(sqlgraph.M2O, false, ProfileTable, ProfileColumn), + ) + sqlgraph.HasNeighbors(s, step) + }) +} + +// HasProfileWith applies the HasEdge predicate on the "profile" edge with a given conditions (other predicates). +func HasProfileWith(preds ...predicate.ProviderProfile) predicate.ProviderServiceLocality { + return predicate.ProviderServiceLocality(func(s *sql.Selector) { + step := newProfileStep() + sqlgraph.HasNeighborsWith(s, step, func(s *sql.Selector) { + for _, p := range preds { + p(s) + } + }) + }) +} + +// HasLocality applies the HasEdge predicate on the "locality" edge. +func HasLocality() predicate.ProviderServiceLocality { + return predicate.ProviderServiceLocality(func(s *sql.Selector) { + step := sqlgraph.NewStep( + sqlgraph.From(Table, LocalityColumn), + sqlgraph.Edge(sqlgraph.M2O, false, LocalityTable, LocalityColumn), + ) + sqlgraph.HasNeighbors(s, step) + }) +} + +// HasLocalityWith applies the HasEdge predicate on the "locality" edge with a given conditions (other predicates). +func HasLocalityWith(preds ...predicate.Locality) predicate.ProviderServiceLocality { + return predicate.ProviderServiceLocality(func(s *sql.Selector) { + step := newLocalityStep() + 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.ProviderServiceLocality) predicate.ProviderServiceLocality { + return predicate.ProviderServiceLocality(sql.AndPredicates(predicates...)) +} + +// Or groups predicates with the OR operator between them. +func Or(predicates ...predicate.ProviderServiceLocality) predicate.ProviderServiceLocality { + return predicate.ProviderServiceLocality(sql.OrPredicates(predicates...)) +} + +// Not applies the not operator on the given predicate. +func Not(p predicate.ProviderServiceLocality) predicate.ProviderServiceLocality { + return predicate.ProviderServiceLocality(sql.NotPredicates(p)) +} diff --git a/backend/ent/providerservicelocality_create.go b/backend/ent/providerservicelocality_create.go new file mode 100644 index 0000000..02d418f --- /dev/null +++ b/backend/ent/providerservicelocality_create.go @@ -0,0 +1,238 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "context" + "errors" + "fmt" + + "entgo.io/ent/dialect/sql/sqlgraph" + "entgo.io/ent/schema/field" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/locality" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/providerprofile" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/providerservicelocality" + "github.com/google/uuid" +) + +// ProviderServiceLocalityCreate is the builder for creating a ProviderServiceLocality entity. +type ProviderServiceLocalityCreate struct { + config + mutation *ProviderServiceLocalityMutation + hooks []Hook +} + +// SetInternalUserID sets the "internal_user_id" field. +func (_c *ProviderServiceLocalityCreate) SetInternalUserID(v uuid.UUID) *ProviderServiceLocalityCreate { + _c.mutation.SetInternalUserID(v) + return _c +} + +// SetLocalityID sets the "locality_id" field. +func (_c *ProviderServiceLocalityCreate) SetLocalityID(v uuid.UUID) *ProviderServiceLocalityCreate { + _c.mutation.SetLocalityID(v) + return _c +} + +// SetProfileID sets the "profile" edge to the ProviderProfile entity by ID. +func (_c *ProviderServiceLocalityCreate) SetProfileID(id uuid.UUID) *ProviderServiceLocalityCreate { + _c.mutation.SetProfileID(id) + return _c +} + +// SetProfile sets the "profile" edge to the ProviderProfile entity. +func (_c *ProviderServiceLocalityCreate) SetProfile(v *ProviderProfile) *ProviderServiceLocalityCreate { + return _c.SetProfileID(v.ID) +} + +// SetLocality sets the "locality" edge to the Locality entity. +func (_c *ProviderServiceLocalityCreate) SetLocality(v *Locality) *ProviderServiceLocalityCreate { + return _c.SetLocalityID(v.ID) +} + +// Mutation returns the ProviderServiceLocalityMutation object of the builder. +func (_c *ProviderServiceLocalityCreate) Mutation() *ProviderServiceLocalityMutation { + return _c.mutation +} + +// Save creates the ProviderServiceLocality in the database. +func (_c *ProviderServiceLocalityCreate) Save(ctx context.Context) (*ProviderServiceLocality, error) { + return withHooks(ctx, _c.sqlSave, _c.mutation, _c.hooks) +} + +// SaveX calls Save and panics if Save returns an error. +func (_c *ProviderServiceLocalityCreate) SaveX(ctx context.Context) *ProviderServiceLocality { + v, err := _c.Save(ctx) + if err != nil { + panic(err) + } + return v +} + +// Exec executes the query. +func (_c *ProviderServiceLocalityCreate) Exec(ctx context.Context) error { + _, err := _c.Save(ctx) + return err +} + +// ExecX is like Exec, but panics if an error occurs. +func (_c *ProviderServiceLocalityCreate) 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 *ProviderServiceLocalityCreate) check() error { + if _, ok := _c.mutation.InternalUserID(); !ok { + return &ValidationError{Name: "internal_user_id", err: errors.New(`ent: missing required field "ProviderServiceLocality.internal_user_id"`)} + } + if _, ok := _c.mutation.LocalityID(); !ok { + return &ValidationError{Name: "locality_id", err: errors.New(`ent: missing required field "ProviderServiceLocality.locality_id"`)} + } + if len(_c.mutation.ProfileIDs()) == 0 { + return &ValidationError{Name: "profile", err: errors.New(`ent: missing required edge "ProviderServiceLocality.profile"`)} + } + if len(_c.mutation.LocalityIDs()) == 0 { + return &ValidationError{Name: "locality", err: errors.New(`ent: missing required edge "ProviderServiceLocality.locality"`)} + } + return nil +} + +func (_c *ProviderServiceLocalityCreate) sqlSave(ctx context.Context) (*ProviderServiceLocality, 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 + } + return _node, nil +} + +func (_c *ProviderServiceLocalityCreate) createSpec() (*ProviderServiceLocality, *sqlgraph.CreateSpec) { + var ( + _node = &ProviderServiceLocality{config: _c.config} + _spec = sqlgraph.NewCreateSpec(providerservicelocality.Table, nil) + ) + if nodes := _c.mutation.ProfileIDs(); len(nodes) > 0 { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.M2O, + Inverse: false, + Table: providerservicelocality.ProfileTable, + Columns: []string{providerservicelocality.ProfileColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(providerprofile.FieldID, field.TypeUUID), + }, + } + for _, k := range nodes { + edge.Target.Nodes = append(edge.Target.Nodes, k) + } + _node.InternalUserID = nodes[0] + _spec.Edges = append(_spec.Edges, edge) + } + if nodes := _c.mutation.LocalityIDs(); len(nodes) > 0 { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.M2O, + Inverse: false, + Table: providerservicelocality.LocalityTable, + Columns: []string{providerservicelocality.LocalityColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(locality.FieldID, field.TypeUUID), + }, + } + for _, k := range nodes { + edge.Target.Nodes = append(edge.Target.Nodes, k) + } + _node.LocalityID = nodes[0] + _spec.Edges = append(_spec.Edges, edge) + } + return _node, _spec +} + +// ProviderServiceLocalityCreateBulk is the builder for creating many ProviderServiceLocality entities in bulk. +type ProviderServiceLocalityCreateBulk struct { + config + err error + builders []*ProviderServiceLocalityCreate +} + +// Save creates the ProviderServiceLocality entities in the database. +func (_c *ProviderServiceLocalityCreateBulk) Save(ctx context.Context) ([]*ProviderServiceLocality, error) { + if _c.err != nil { + return nil, _c.err + } + specs := make([]*sqlgraph.CreateSpec, len(_c.builders)) + nodes := make([]*ProviderServiceLocality, 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.(*ProviderServiceLocalityMutation) + 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.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 *ProviderServiceLocalityCreateBulk) SaveX(ctx context.Context) []*ProviderServiceLocality { + v, err := _c.Save(ctx) + if err != nil { + panic(err) + } + return v +} + +// Exec executes the query. +func (_c *ProviderServiceLocalityCreateBulk) Exec(ctx context.Context) error { + _, err := _c.Save(ctx) + return err +} + +// ExecX is like Exec, but panics if an error occurs. +func (_c *ProviderServiceLocalityCreateBulk) ExecX(ctx context.Context) { + if err := _c.Exec(ctx); err != nil { + panic(err) + } +} diff --git a/backend/ent/providerservicelocality_delete.go b/backend/ent/providerservicelocality_delete.go new file mode 100644 index 0000000..640f235 --- /dev/null +++ b/backend/ent/providerservicelocality_delete.go @@ -0,0 +1,87 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "context" + + "entgo.io/ent/dialect/sql" + "entgo.io/ent/dialect/sql/sqlgraph" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/predicate" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/providerservicelocality" +) + +// ProviderServiceLocalityDelete is the builder for deleting a ProviderServiceLocality entity. +type ProviderServiceLocalityDelete struct { + config + hooks []Hook + mutation *ProviderServiceLocalityMutation +} + +// Where appends a list predicates to the ProviderServiceLocalityDelete builder. +func (_d *ProviderServiceLocalityDelete) Where(ps ...predicate.ProviderServiceLocality) *ProviderServiceLocalityDelete { + _d.mutation.Where(ps...) + return _d +} + +// Exec executes the deletion query and returns how many vertices were deleted. +func (_d *ProviderServiceLocalityDelete) 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 *ProviderServiceLocalityDelete) ExecX(ctx context.Context) int { + n, err := _d.Exec(ctx) + if err != nil { + panic(err) + } + return n +} + +func (_d *ProviderServiceLocalityDelete) sqlExec(ctx context.Context) (int, error) { + _spec := sqlgraph.NewDeleteSpec(providerservicelocality.Table, nil) + 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 +} + +// ProviderServiceLocalityDeleteOne is the builder for deleting a single ProviderServiceLocality entity. +type ProviderServiceLocalityDeleteOne struct { + _d *ProviderServiceLocalityDelete +} + +// Where appends a list predicates to the ProviderServiceLocalityDelete builder. +func (_d *ProviderServiceLocalityDeleteOne) Where(ps ...predicate.ProviderServiceLocality) *ProviderServiceLocalityDeleteOne { + _d._d.mutation.Where(ps...) + return _d +} + +// Exec executes the deletion query. +func (_d *ProviderServiceLocalityDeleteOne) Exec(ctx context.Context) error { + n, err := _d._d.Exec(ctx) + switch { + case err != nil: + return err + case n == 0: + return &NotFoundError{providerservicelocality.Label} + default: + return nil + } +} + +// ExecX is like Exec, but panics if an error occurs. +func (_d *ProviderServiceLocalityDeleteOne) ExecX(ctx context.Context) { + if err := _d.Exec(ctx); err != nil { + panic(err) + } +} diff --git a/backend/ent/providerservicelocality_query.go b/backend/ent/providerservicelocality_query.go new file mode 100644 index 0000000..7f6c0ef --- /dev/null +++ b/backend/ent/providerservicelocality_query.go @@ -0,0 +1,604 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "context" + "fmt" + "math" + + "entgo.io/ent" + "entgo.io/ent/dialect/sql" + "entgo.io/ent/dialect/sql/sqlgraph" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/locality" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/predicate" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/providerprofile" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/providerservicelocality" + "github.com/google/uuid" +) + +// ProviderServiceLocalityQuery is the builder for querying ProviderServiceLocality entities. +type ProviderServiceLocalityQuery struct { + config + ctx *QueryContext + order []providerservicelocality.OrderOption + inters []Interceptor + predicates []predicate.ProviderServiceLocality + withProfile *ProviderProfileQuery + withLocality *LocalityQuery + // intermediate query (i.e. traversal path). + sql *sql.Selector + path func(context.Context) (*sql.Selector, error) +} + +// Where adds a new predicate for the ProviderServiceLocalityQuery builder. +func (_q *ProviderServiceLocalityQuery) Where(ps ...predicate.ProviderServiceLocality) *ProviderServiceLocalityQuery { + _q.predicates = append(_q.predicates, ps...) + return _q +} + +// Limit the number of records to be returned by this query. +func (_q *ProviderServiceLocalityQuery) Limit(limit int) *ProviderServiceLocalityQuery { + _q.ctx.Limit = &limit + return _q +} + +// Offset to start from. +func (_q *ProviderServiceLocalityQuery) Offset(offset int) *ProviderServiceLocalityQuery { + _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 *ProviderServiceLocalityQuery) Unique(unique bool) *ProviderServiceLocalityQuery { + _q.ctx.Unique = &unique + return _q +} + +// Order specifies how the records should be ordered. +func (_q *ProviderServiceLocalityQuery) Order(o ...providerservicelocality.OrderOption) *ProviderServiceLocalityQuery { + _q.order = append(_q.order, o...) + return _q +} + +// QueryProfile chains the current query on the "profile" edge. +func (_q *ProviderServiceLocalityQuery) QueryProfile() *ProviderProfileQuery { + query := (&ProviderProfileClient{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(providerservicelocality.Table, providerservicelocality.ProfileColumn, selector), + sqlgraph.To(providerprofile.Table, providerprofile.FieldID), + sqlgraph.Edge(sqlgraph.M2O, false, providerservicelocality.ProfileTable, providerservicelocality.ProfileColumn), + ) + fromU = sqlgraph.SetNeighbors(_q.driver.Dialect(), step) + return fromU, nil + } + return query +} + +// QueryLocality chains the current query on the "locality" edge. +func (_q *ProviderServiceLocalityQuery) QueryLocality() *LocalityQuery { + query := (&LocalityClient{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(providerservicelocality.Table, providerservicelocality.LocalityColumn, selector), + sqlgraph.To(locality.Table, locality.FieldID), + sqlgraph.Edge(sqlgraph.M2O, false, providerservicelocality.LocalityTable, providerservicelocality.LocalityColumn), + ) + fromU = sqlgraph.SetNeighbors(_q.driver.Dialect(), step) + return fromU, nil + } + return query +} + +// First returns the first ProviderServiceLocality entity from the query. +// Returns a *NotFoundError when no ProviderServiceLocality was found. +func (_q *ProviderServiceLocalityQuery) First(ctx context.Context) (*ProviderServiceLocality, 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{providerservicelocality.Label} + } + return nodes[0], nil +} + +// FirstX is like First, but panics if an error occurs. +func (_q *ProviderServiceLocalityQuery) FirstX(ctx context.Context) *ProviderServiceLocality { + node, err := _q.First(ctx) + if err != nil && !IsNotFound(err) { + panic(err) + } + return node +} + +// Only returns a single ProviderServiceLocality entity found by the query, ensuring it only returns one. +// Returns a *NotSingularError when more than one ProviderServiceLocality entity is found. +// Returns a *NotFoundError when no ProviderServiceLocality entities are found. +func (_q *ProviderServiceLocalityQuery) Only(ctx context.Context) (*ProviderServiceLocality, 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{providerservicelocality.Label} + default: + return nil, &NotSingularError{providerservicelocality.Label} + } +} + +// OnlyX is like Only, but panics if an error occurs. +func (_q *ProviderServiceLocalityQuery) OnlyX(ctx context.Context) *ProviderServiceLocality { + node, err := _q.Only(ctx) + if err != nil { + panic(err) + } + return node +} + +// All executes the query and returns a list of ProviderServiceLocalities. +func (_q *ProviderServiceLocalityQuery) All(ctx context.Context) ([]*ProviderServiceLocality, error) { + ctx = setContextOp(ctx, _q.ctx, ent.OpQueryAll) + if err := _q.prepareQuery(ctx); err != nil { + return nil, err + } + qr := querierAll[[]*ProviderServiceLocality, *ProviderServiceLocalityQuery]() + return withInterceptors[[]*ProviderServiceLocality](ctx, _q, qr, _q.inters) +} + +// AllX is like All, but panics if an error occurs. +func (_q *ProviderServiceLocalityQuery) AllX(ctx context.Context) []*ProviderServiceLocality { + nodes, err := _q.All(ctx) + if err != nil { + panic(err) + } + return nodes +} + +// Count returns the count of the given query. +func (_q *ProviderServiceLocalityQuery) 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[*ProviderServiceLocalityQuery](), _q.inters) +} + +// CountX is like Count, but panics if an error occurs. +func (_q *ProviderServiceLocalityQuery) 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 *ProviderServiceLocalityQuery) Exist(ctx context.Context) (bool, error) { + ctx = setContextOp(ctx, _q.ctx, ent.OpQueryExist) + switch _, err := _q.First(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 *ProviderServiceLocalityQuery) ExistX(ctx context.Context) bool { + exist, err := _q.Exist(ctx) + if err != nil { + panic(err) + } + return exist +} + +// Clone returns a duplicate of the ProviderServiceLocalityQuery 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 *ProviderServiceLocalityQuery) Clone() *ProviderServiceLocalityQuery { + if _q == nil { + return nil + } + return &ProviderServiceLocalityQuery{ + config: _q.config, + ctx: _q.ctx.Clone(), + order: append([]providerservicelocality.OrderOption{}, _q.order...), + inters: append([]Interceptor{}, _q.inters...), + predicates: append([]predicate.ProviderServiceLocality{}, _q.predicates...), + withProfile: _q.withProfile.Clone(), + withLocality: _q.withLocality.Clone(), + // clone intermediate query. + sql: _q.sql.Clone(), + path: _q.path, + } +} + +// WithProfile tells the query-builder to eager-load the nodes that are connected to +// the "profile" edge. The optional arguments are used to configure the query builder of the edge. +func (_q *ProviderServiceLocalityQuery) WithProfile(opts ...func(*ProviderProfileQuery)) *ProviderServiceLocalityQuery { + query := (&ProviderProfileClient{config: _q.config}).Query() + for _, opt := range opts { + opt(query) + } + _q.withProfile = query + return _q +} + +// WithLocality tells the query-builder to eager-load the nodes that are connected to +// the "locality" edge. The optional arguments are used to configure the query builder of the edge. +func (_q *ProviderServiceLocalityQuery) WithLocality(opts ...func(*LocalityQuery)) *ProviderServiceLocalityQuery { + query := (&LocalityClient{config: _q.config}).Query() + for _, opt := range opts { + opt(query) + } + _q.withLocality = 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 { +// InternalUserID uuid.UUID `json:"internal_user_id,omitempty"` +// Count int `json:"count,omitempty"` +// } +// +// client.ProviderServiceLocality.Query(). +// GroupBy(providerservicelocality.FieldInternalUserID). +// Aggregate(ent.Count()). +// Scan(ctx, &v) +func (_q *ProviderServiceLocalityQuery) GroupBy(field string, fields ...string) *ProviderServiceLocalityGroupBy { + _q.ctx.Fields = append([]string{field}, fields...) + grbuild := &ProviderServiceLocalityGroupBy{build: _q} + grbuild.flds = &_q.ctx.Fields + grbuild.label = providerservicelocality.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 { +// InternalUserID uuid.UUID `json:"internal_user_id,omitempty"` +// } +// +// client.ProviderServiceLocality.Query(). +// Select(providerservicelocality.FieldInternalUserID). +// Scan(ctx, &v) +func (_q *ProviderServiceLocalityQuery) Select(fields ...string) *ProviderServiceLocalitySelect { + _q.ctx.Fields = append(_q.ctx.Fields, fields...) + sbuild := &ProviderServiceLocalitySelect{ProviderServiceLocalityQuery: _q} + sbuild.label = providerservicelocality.Label + sbuild.flds, sbuild.scan = &_q.ctx.Fields, sbuild.Scan + return sbuild +} + +// Aggregate returns a ProviderServiceLocalitySelect configured with the given aggregations. +func (_q *ProviderServiceLocalityQuery) Aggregate(fns ...AggregateFunc) *ProviderServiceLocalitySelect { + return _q.Select().Aggregate(fns...) +} + +func (_q *ProviderServiceLocalityQuery) 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 !providerservicelocality.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 *ProviderServiceLocalityQuery) sqlAll(ctx context.Context, hooks ...queryHook) ([]*ProviderServiceLocality, error) { + var ( + nodes = []*ProviderServiceLocality{} + _spec = _q.querySpec() + loadedTypes = [2]bool{ + _q.withProfile != nil, + _q.withLocality != nil, + } + ) + _spec.ScanValues = func(columns []string) ([]any, error) { + return (*ProviderServiceLocality).scanValues(nil, columns) + } + _spec.Assign = func(columns []string, values []any) error { + node := &ProviderServiceLocality{config: _q.config} + nodes = append(nodes, node) + node.Edges.loadedTypes = loadedTypes + return node.assignValues(columns, values) + } + 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.withProfile; query != nil { + if err := _q.loadProfile(ctx, query, nodes, nil, + func(n *ProviderServiceLocality, e *ProviderProfile) { n.Edges.Profile = e }); err != nil { + return nil, err + } + } + if query := _q.withLocality; query != nil { + if err := _q.loadLocality(ctx, query, nodes, nil, + func(n *ProviderServiceLocality, e *Locality) { n.Edges.Locality = e }); err != nil { + return nil, err + } + } + return nodes, nil +} + +func (_q *ProviderServiceLocalityQuery) loadProfile(ctx context.Context, query *ProviderProfileQuery, nodes []*ProviderServiceLocality, init func(*ProviderServiceLocality), assign func(*ProviderServiceLocality, *ProviderProfile)) error { + ids := make([]uuid.UUID, 0, len(nodes)) + nodeids := make(map[uuid.UUID][]*ProviderServiceLocality) + for i := range nodes { + fk := nodes[i].InternalUserID + if _, ok := nodeids[fk]; !ok { + ids = append(ids, fk) + } + nodeids[fk] = append(nodeids[fk], nodes[i]) + } + if len(ids) == 0 { + return nil + } + query.Where(providerprofile.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 "internal_user_id" returned %v`, n.ID) + } + for i := range nodes { + assign(nodes[i], n) + } + } + return nil +} +func (_q *ProviderServiceLocalityQuery) loadLocality(ctx context.Context, query *LocalityQuery, nodes []*ProviderServiceLocality, init func(*ProviderServiceLocality), assign func(*ProviderServiceLocality, *Locality)) error { + ids := make([]uuid.UUID, 0, len(nodes)) + nodeids := make(map[uuid.UUID][]*ProviderServiceLocality) + for i := range nodes { + fk := nodes[i].LocalityID + if _, ok := nodeids[fk]; !ok { + ids = append(ids, fk) + } + nodeids[fk] = append(nodeids[fk], nodes[i]) + } + if len(ids) == 0 { + return nil + } + query.Where(locality.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 "locality_id" returned %v`, n.ID) + } + for i := range nodes { + assign(nodes[i], n) + } + } + return nil +} + +func (_q *ProviderServiceLocalityQuery) sqlCount(ctx context.Context) (int, error) { + _spec := _q.querySpec() + _spec.Unique = false + _spec.Node.Columns = nil + return sqlgraph.CountNodes(ctx, _q.driver, _spec) +} + +func (_q *ProviderServiceLocalityQuery) querySpec() *sqlgraph.QuerySpec { + _spec := sqlgraph.NewQuerySpec(providerservicelocality.Table, providerservicelocality.Columns, nil) + _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)) + for i := range fields { + _spec.Node.Columns = append(_spec.Node.Columns, fields[i]) + } + if _q.withProfile != nil { + _spec.Node.AddColumnOnce(providerservicelocality.FieldInternalUserID) + } + if _q.withLocality != nil { + _spec.Node.AddColumnOnce(providerservicelocality.FieldLocalityID) + } + } + 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 *ProviderServiceLocalityQuery) sqlQuery(ctx context.Context) *sql.Selector { + builder := sql.Dialect(_q.driver.Dialect()) + t1 := builder.Table(providerservicelocality.Table) + columns := _q.ctx.Fields + if len(columns) == 0 { + columns = providerservicelocality.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 _, 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 +} + +// ProviderServiceLocalityGroupBy is the group-by builder for ProviderServiceLocality entities. +type ProviderServiceLocalityGroupBy struct { + selector + build *ProviderServiceLocalityQuery +} + +// Aggregate adds the given aggregation functions to the group-by query. +func (_g *ProviderServiceLocalityGroupBy) Aggregate(fns ...AggregateFunc) *ProviderServiceLocalityGroupBy { + _g.fns = append(_g.fns, fns...) + return _g +} + +// Scan applies the selector query and scans the result into the given value. +func (_g *ProviderServiceLocalityGroupBy) 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[*ProviderServiceLocalityQuery, *ProviderServiceLocalityGroupBy](ctx, _g.build, _g, _g.build.inters, v) +} + +func (_g *ProviderServiceLocalityGroupBy) sqlScan(ctx context.Context, root *ProviderServiceLocalityQuery, 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) +} + +// ProviderServiceLocalitySelect is the builder for selecting fields of ProviderServiceLocality entities. +type ProviderServiceLocalitySelect struct { + *ProviderServiceLocalityQuery + selector +} + +// Aggregate adds the given aggregation functions to the selector query. +func (_s *ProviderServiceLocalitySelect) Aggregate(fns ...AggregateFunc) *ProviderServiceLocalitySelect { + _s.fns = append(_s.fns, fns...) + return _s +} + +// Scan applies the selector query and scans the result into the given value. +func (_s *ProviderServiceLocalitySelect) 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[*ProviderServiceLocalityQuery, *ProviderServiceLocalitySelect](ctx, _s.ProviderServiceLocalityQuery, _s, _s.inters, v) +} + +func (_s *ProviderServiceLocalitySelect) sqlScan(ctx context.Context, root *ProviderServiceLocalityQuery, 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/backend/ent/providerservicelocality_update.go b/backend/ent/providerservicelocality_update.go new file mode 100644 index 0000000..c7ad96e --- /dev/null +++ b/backend/ent/providerservicelocality_update.go @@ -0,0 +1,205 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "context" + "errors" + "fmt" + + "entgo.io/ent/dialect/sql" + "entgo.io/ent/dialect/sql/sqlgraph" + "entgo.io/ent/schema/field" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/predicate" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/providerservicelocality" +) + +// ProviderServiceLocalityUpdate is the builder for updating ProviderServiceLocality entities. +type ProviderServiceLocalityUpdate struct { + config + hooks []Hook + mutation *ProviderServiceLocalityMutation +} + +// Where appends a list predicates to the ProviderServiceLocalityUpdate builder. +func (_u *ProviderServiceLocalityUpdate) Where(ps ...predicate.ProviderServiceLocality) *ProviderServiceLocalityUpdate { + _u.mutation.Where(ps...) + return _u +} + +// Mutation returns the ProviderServiceLocalityMutation object of the builder. +func (_u *ProviderServiceLocalityUpdate) Mutation() *ProviderServiceLocalityMutation { + return _u.mutation +} + +// Save executes the query and returns the number of nodes affected by the update operation. +func (_u *ProviderServiceLocalityUpdate) 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 *ProviderServiceLocalityUpdate) SaveX(ctx context.Context) int { + affected, err := _u.Save(ctx) + if err != nil { + panic(err) + } + return affected +} + +// Exec executes the query. +func (_u *ProviderServiceLocalityUpdate) Exec(ctx context.Context) error { + _, err := _u.Save(ctx) + return err +} + +// ExecX is like Exec, but panics if an error occurs. +func (_u *ProviderServiceLocalityUpdate) 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 *ProviderServiceLocalityUpdate) check() error { + if _u.mutation.ProfileCleared() && len(_u.mutation.ProfileIDs()) > 0 { + return errors.New(`ent: clearing a required unique edge "ProviderServiceLocality.profile"`) + } + if _u.mutation.LocalityCleared() && len(_u.mutation.LocalityIDs()) > 0 { + return errors.New(`ent: clearing a required unique edge "ProviderServiceLocality.locality"`) + } + return nil +} + +func (_u *ProviderServiceLocalityUpdate) sqlSave(ctx context.Context) (_node int, err error) { + if err := _u.check(); err != nil { + return _node, err + } + _spec := sqlgraph.NewUpdateSpec(providerservicelocality.Table, providerservicelocality.Columns, sqlgraph.NewFieldSpec(providerservicelocality.FieldInternalUserID, field.TypeUUID), sqlgraph.NewFieldSpec(providerservicelocality.FieldLocalityID, field.TypeUUID)) + if ps := _u.mutation.predicates; len(ps) > 0 { + _spec.Predicate = func(selector *sql.Selector) { + for i := range ps { + ps[i](selector) + } + } + } + if _node, err = sqlgraph.UpdateNodes(ctx, _u.driver, _spec); err != nil { + if _, ok := err.(*sqlgraph.NotFoundError); ok { + err = &NotFoundError{providerservicelocality.Label} + } else if sqlgraph.IsConstraintError(err) { + err = &ConstraintError{msg: err.Error(), wrap: err} + } + return 0, err + } + _u.mutation.done = true + return _node, nil +} + +// ProviderServiceLocalityUpdateOne is the builder for updating a single ProviderServiceLocality entity. +type ProviderServiceLocalityUpdateOne struct { + config + fields []string + hooks []Hook + mutation *ProviderServiceLocalityMutation +} + +// Mutation returns the ProviderServiceLocalityMutation object of the builder. +func (_u *ProviderServiceLocalityUpdateOne) Mutation() *ProviderServiceLocalityMutation { + return _u.mutation +} + +// Where appends a list predicates to the ProviderServiceLocalityUpdate builder. +func (_u *ProviderServiceLocalityUpdateOne) Where(ps ...predicate.ProviderServiceLocality) *ProviderServiceLocalityUpdateOne { + _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 *ProviderServiceLocalityUpdateOne) Select(field string, fields ...string) *ProviderServiceLocalityUpdateOne { + _u.fields = append([]string{field}, fields...) + return _u +} + +// Save executes the query and returns the updated ProviderServiceLocality entity. +func (_u *ProviderServiceLocalityUpdateOne) Save(ctx context.Context) (*ProviderServiceLocality, error) { + return withHooks(ctx, _u.sqlSave, _u.mutation, _u.hooks) +} + +// SaveX is like Save, but panics if an error occurs. +func (_u *ProviderServiceLocalityUpdateOne) SaveX(ctx context.Context) *ProviderServiceLocality { + node, err := _u.Save(ctx) + if err != nil { + panic(err) + } + return node +} + +// Exec executes the query on the entity. +func (_u *ProviderServiceLocalityUpdateOne) Exec(ctx context.Context) error { + _, err := _u.Save(ctx) + return err +} + +// ExecX is like Exec, but panics if an error occurs. +func (_u *ProviderServiceLocalityUpdateOne) 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 *ProviderServiceLocalityUpdateOne) check() error { + if _u.mutation.ProfileCleared() && len(_u.mutation.ProfileIDs()) > 0 { + return errors.New(`ent: clearing a required unique edge "ProviderServiceLocality.profile"`) + } + if _u.mutation.LocalityCleared() && len(_u.mutation.LocalityIDs()) > 0 { + return errors.New(`ent: clearing a required unique edge "ProviderServiceLocality.locality"`) + } + return nil +} + +func (_u *ProviderServiceLocalityUpdateOne) sqlSave(ctx context.Context) (_node *ProviderServiceLocality, err error) { + if err := _u.check(); err != nil { + return _node, err + } + _spec := sqlgraph.NewUpdateSpec(providerservicelocality.Table, providerservicelocality.Columns, sqlgraph.NewFieldSpec(providerservicelocality.FieldInternalUserID, field.TypeUUID), sqlgraph.NewFieldSpec(providerservicelocality.FieldLocalityID, field.TypeUUID)) + if id, ok := _u.mutation.InternalUserID(); !ok { + return nil, &ValidationError{Name: "internal_user_id", err: errors.New(`ent: missing "ProviderServiceLocality.internal_user_id" for update`)} + } else { + _spec.Node.CompositeID[0].Value = id + } + if id, ok := _u.mutation.LocalityID(); !ok { + return nil, &ValidationError{Name: "locality_id", err: errors.New(`ent: missing "ProviderServiceLocality.locality_id" for update`)} + } else { + _spec.Node.CompositeID[1].Value = id + } + if fields := _u.fields; len(fields) > 0 { + _spec.Node.Columns = make([]string, len(fields)) + for i, f := range fields { + if !providerservicelocality.ValidColumn(f) { + return nil, &ValidationError{Name: f, err: fmt.Errorf("ent: invalid field %q for query", f)} + } + _spec.Node.Columns[i] = f + } + } + if ps := _u.mutation.predicates; len(ps) > 0 { + _spec.Predicate = func(selector *sql.Selector) { + for i := range ps { + ps[i](selector) + } + } + } + _node = &ProviderServiceLocality{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{providerservicelocality.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/backend/ent/providerspokenlanguage.go b/backend/ent/providerspokenlanguage.go new file mode 100644 index 0000000..f327d43 --- /dev/null +++ b/backend/ent/providerspokenlanguage.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" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/providerprofile" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/providerspokenlanguage" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/spokenlanguage" + "github.com/google/uuid" +) + +// ProviderSpokenLanguage is the model entity for the ProviderSpokenLanguage schema. +type ProviderSpokenLanguage struct { + config `json:"-"` + // InternalUserID holds the value of the "internal_user_id" field. + InternalUserID uuid.UUID `json:"internal_user_id,omitempty"` + // LanguageCode holds the value of the "language_code" field. + LanguageCode string `json:"language_code,omitempty"` + // Edges holds the relations/edges for other nodes in the graph. + // The values are being populated by the ProviderSpokenLanguageQuery when eager-loading is set. + Edges ProviderSpokenLanguageEdges `json:"edges"` + selectValues sql.SelectValues +} + +// ProviderSpokenLanguageEdges holds the relations/edges for other nodes in the graph. +type ProviderSpokenLanguageEdges struct { + // Profile holds the value of the profile edge. + Profile *ProviderProfile `json:"profile,omitempty"` + // Language holds the value of the language edge. + Language *SpokenLanguage `json:"language,omitempty"` + // loadedTypes holds the information for reporting if a + // type was loaded (or requested) in eager-loading or not. + loadedTypes [2]bool +} + +// ProfileOrErr returns the Profile value or an error if the edge +// was not loaded in eager-loading, or loaded but was not found. +func (e ProviderSpokenLanguageEdges) ProfileOrErr() (*ProviderProfile, error) { + if e.Profile != nil { + return e.Profile, nil + } else if e.loadedTypes[0] { + return nil, &NotFoundError{label: providerprofile.Label} + } + return nil, &NotLoadedError{edge: "profile"} +} + +// LanguageOrErr returns the Language value or an error if the edge +// was not loaded in eager-loading, or loaded but was not found. +func (e ProviderSpokenLanguageEdges) LanguageOrErr() (*SpokenLanguage, error) { + if e.Language != nil { + return e.Language, nil + } else if e.loadedTypes[1] { + return nil, &NotFoundError{label: spokenlanguage.Label} + } + return nil, &NotLoadedError{edge: "language"} +} + +// scanValues returns the types for scanning values from sql.Rows. +func (*ProviderSpokenLanguage) scanValues(columns []string) ([]any, error) { + values := make([]any, len(columns)) + for i := range columns { + switch columns[i] { + case providerspokenlanguage.FieldLanguageCode: + values[i] = new(sql.NullString) + case providerspokenlanguage.FieldInternalUserID: + 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 ProviderSpokenLanguage fields. +func (_m *ProviderSpokenLanguage) 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 providerspokenlanguage.FieldInternalUserID: + if value, ok := values[i].(*uuid.UUID); !ok { + return fmt.Errorf("unexpected type %T for field internal_user_id", values[i]) + } else if value != nil { + _m.InternalUserID = *value + } + case providerspokenlanguage.FieldLanguageCode: + if value, ok := values[i].(*sql.NullString); !ok { + return fmt.Errorf("unexpected type %T for field language_code", values[i]) + } else if value.Valid { + _m.LanguageCode = value.String + } + default: + _m.selectValues.Set(columns[i], values[i]) + } + } + return nil +} + +// Value returns the ent.Value that was dynamically selected and assigned to the ProviderSpokenLanguage. +// This includes values selected through modifiers, order, etc. +func (_m *ProviderSpokenLanguage) Value(name string) (ent.Value, error) { + return _m.selectValues.Get(name) +} + +// QueryProfile queries the "profile" edge of the ProviderSpokenLanguage entity. +func (_m *ProviderSpokenLanguage) QueryProfile() *ProviderProfileQuery { + return NewProviderSpokenLanguageClient(_m.config).QueryProfile(_m) +} + +// QueryLanguage queries the "language" edge of the ProviderSpokenLanguage entity. +func (_m *ProviderSpokenLanguage) QueryLanguage() *SpokenLanguageQuery { + return NewProviderSpokenLanguageClient(_m.config).QueryLanguage(_m) +} + +// Update returns a builder for updating this ProviderSpokenLanguage. +// Note that you need to call ProviderSpokenLanguage.Unwrap() before calling this method if this ProviderSpokenLanguage +// was returned from a transaction, and the transaction was committed or rolled back. +func (_m *ProviderSpokenLanguage) Update() *ProviderSpokenLanguageUpdateOne { + return NewProviderSpokenLanguageClient(_m.config).UpdateOne(_m) +} + +// Unwrap unwraps the ProviderSpokenLanguage 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 *ProviderSpokenLanguage) Unwrap() *ProviderSpokenLanguage { + _tx, ok := _m.config.driver.(*txDriver) + if !ok { + panic("ent: ProviderSpokenLanguage is not a transactional entity") + } + _m.config.driver = _tx.drv + return _m +} + +// String implements the fmt.Stringer. +func (_m *ProviderSpokenLanguage) String() string { + var builder strings.Builder + builder.WriteString("ProviderSpokenLanguage(") + builder.WriteString("internal_user_id=") + builder.WriteString(fmt.Sprintf("%v", _m.InternalUserID)) + builder.WriteString(", ") + builder.WriteString("language_code=") + builder.WriteString(_m.LanguageCode) + builder.WriteByte(')') + return builder.String() +} + +// ProviderSpokenLanguages is a parsable slice of ProviderSpokenLanguage. +type ProviderSpokenLanguages []*ProviderSpokenLanguage diff --git a/backend/ent/providerspokenlanguage/providerspokenlanguage.go b/backend/ent/providerspokenlanguage/providerspokenlanguage.go new file mode 100644 index 0000000..cac4f35 --- /dev/null +++ b/backend/ent/providerspokenlanguage/providerspokenlanguage.go @@ -0,0 +1,103 @@ +// Code generated by ent, DO NOT EDIT. + +package providerspokenlanguage + +import ( + "entgo.io/ent/dialect/sql" + "entgo.io/ent/dialect/sql/sqlgraph" +) + +const ( + // Label holds the string label denoting the providerspokenlanguage type in the database. + Label = "provider_spoken_language" + // FieldInternalUserID holds the string denoting the internal_user_id field in the database. + FieldInternalUserID = "internal_user_id" + // FieldLanguageCode holds the string denoting the language_code field in the database. + FieldLanguageCode = "language_code" + // EdgeProfile holds the string denoting the profile edge name in mutations. + EdgeProfile = "profile" + // EdgeLanguage holds the string denoting the language edge name in mutations. + EdgeLanguage = "language" + // ProviderProfileFieldID holds the string denoting the ID field of the ProviderProfile. + ProviderProfileFieldID = "internal_user_id" + // SpokenLanguageFieldID holds the string denoting the ID field of the SpokenLanguage. + SpokenLanguageFieldID = "id" + // Table holds the table name of the providerspokenlanguage in the database. + Table = "provider_spoken_languages" + // ProfileTable is the table that holds the profile relation/edge. + ProfileTable = "provider_spoken_languages" + // ProfileInverseTable is the table name for the ProviderProfile entity. + // It exists in this package in order to avoid circular dependency with the "providerprofile" package. + ProfileInverseTable = "provider_profiles" + // ProfileColumn is the table column denoting the profile relation/edge. + ProfileColumn = "internal_user_id" + // LanguageTable is the table that holds the language relation/edge. + LanguageTable = "provider_spoken_languages" + // LanguageInverseTable is the table name for the SpokenLanguage entity. + // It exists in this package in order to avoid circular dependency with the "spokenlanguage" package. + LanguageInverseTable = "spoken_languages" + // LanguageColumn is the table column denoting the language relation/edge. + LanguageColumn = "language_code" +) + +// Columns holds all SQL columns for providerspokenlanguage fields. +var Columns = []string{ + FieldInternalUserID, + FieldLanguageCode, +} + +// 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 ( + // LanguageCodeValidator is a validator for the "language_code" field. It is called by the builders before save. + LanguageCodeValidator func(string) error +) + +// OrderOption defines the ordering options for the ProviderSpokenLanguage queries. +type OrderOption func(*sql.Selector) + +// ByInternalUserID orders the results by the internal_user_id field. +func ByInternalUserID(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldInternalUserID, opts...).ToFunc() +} + +// ByLanguageCode orders the results by the language_code field. +func ByLanguageCode(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldLanguageCode, opts...).ToFunc() +} + +// ByProfileField orders the results by profile field. +func ByProfileField(field string, opts ...sql.OrderTermOption) OrderOption { + return func(s *sql.Selector) { + sqlgraph.OrderByNeighborTerms(s, newProfileStep(), sql.OrderByField(field, opts...)) + } +} + +// ByLanguageField orders the results by language field. +func ByLanguageField(field string, opts ...sql.OrderTermOption) OrderOption { + return func(s *sql.Selector) { + sqlgraph.OrderByNeighborTerms(s, newLanguageStep(), sql.OrderByField(field, opts...)) + } +} +func newProfileStep() *sqlgraph.Step { + return sqlgraph.NewStep( + sqlgraph.From(Table, ProfileColumn), + sqlgraph.To(ProfileInverseTable, ProviderProfileFieldID), + sqlgraph.Edge(sqlgraph.M2O, false, ProfileTable, ProfileColumn), + ) +} +func newLanguageStep() *sqlgraph.Step { + return sqlgraph.NewStep( + sqlgraph.From(Table, LanguageColumn), + sqlgraph.To(LanguageInverseTable, SpokenLanguageFieldID), + sqlgraph.Edge(sqlgraph.M2O, false, LanguageTable, LanguageColumn), + ) +} diff --git a/backend/ent/providerspokenlanguage/where.go b/backend/ent/providerspokenlanguage/where.go new file mode 100644 index 0000000..603315b --- /dev/null +++ b/backend/ent/providerspokenlanguage/where.go @@ -0,0 +1,166 @@ +// Code generated by ent, DO NOT EDIT. + +package providerspokenlanguage + +import ( + "entgo.io/ent/dialect/sql" + "entgo.io/ent/dialect/sql/sqlgraph" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/predicate" + "github.com/google/uuid" +) + +// InternalUserID applies equality check predicate on the "internal_user_id" field. It's identical to InternalUserIDEQ. +func InternalUserID(v uuid.UUID) predicate.ProviderSpokenLanguage { + return predicate.ProviderSpokenLanguage(sql.FieldEQ(FieldInternalUserID, v)) +} + +// LanguageCode applies equality check predicate on the "language_code" field. It's identical to LanguageCodeEQ. +func LanguageCode(v string) predicate.ProviderSpokenLanguage { + return predicate.ProviderSpokenLanguage(sql.FieldEQ(FieldLanguageCode, v)) +} + +// InternalUserIDEQ applies the EQ predicate on the "internal_user_id" field. +func InternalUserIDEQ(v uuid.UUID) predicate.ProviderSpokenLanguage { + return predicate.ProviderSpokenLanguage(sql.FieldEQ(FieldInternalUserID, v)) +} + +// InternalUserIDNEQ applies the NEQ predicate on the "internal_user_id" field. +func InternalUserIDNEQ(v uuid.UUID) predicate.ProviderSpokenLanguage { + return predicate.ProviderSpokenLanguage(sql.FieldNEQ(FieldInternalUserID, v)) +} + +// InternalUserIDIn applies the In predicate on the "internal_user_id" field. +func InternalUserIDIn(vs ...uuid.UUID) predicate.ProviderSpokenLanguage { + return predicate.ProviderSpokenLanguage(sql.FieldIn(FieldInternalUserID, vs...)) +} + +// InternalUserIDNotIn applies the NotIn predicate on the "internal_user_id" field. +func InternalUserIDNotIn(vs ...uuid.UUID) predicate.ProviderSpokenLanguage { + return predicate.ProviderSpokenLanguage(sql.FieldNotIn(FieldInternalUserID, vs...)) +} + +// LanguageCodeEQ applies the EQ predicate on the "language_code" field. +func LanguageCodeEQ(v string) predicate.ProviderSpokenLanguage { + return predicate.ProviderSpokenLanguage(sql.FieldEQ(FieldLanguageCode, v)) +} + +// LanguageCodeNEQ applies the NEQ predicate on the "language_code" field. +func LanguageCodeNEQ(v string) predicate.ProviderSpokenLanguage { + return predicate.ProviderSpokenLanguage(sql.FieldNEQ(FieldLanguageCode, v)) +} + +// LanguageCodeIn applies the In predicate on the "language_code" field. +func LanguageCodeIn(vs ...string) predicate.ProviderSpokenLanguage { + return predicate.ProviderSpokenLanguage(sql.FieldIn(FieldLanguageCode, vs...)) +} + +// LanguageCodeNotIn applies the NotIn predicate on the "language_code" field. +func LanguageCodeNotIn(vs ...string) predicate.ProviderSpokenLanguage { + return predicate.ProviderSpokenLanguage(sql.FieldNotIn(FieldLanguageCode, vs...)) +} + +// LanguageCodeGT applies the GT predicate on the "language_code" field. +func LanguageCodeGT(v string) predicate.ProviderSpokenLanguage { + return predicate.ProviderSpokenLanguage(sql.FieldGT(FieldLanguageCode, v)) +} + +// LanguageCodeGTE applies the GTE predicate on the "language_code" field. +func LanguageCodeGTE(v string) predicate.ProviderSpokenLanguage { + return predicate.ProviderSpokenLanguage(sql.FieldGTE(FieldLanguageCode, v)) +} + +// LanguageCodeLT applies the LT predicate on the "language_code" field. +func LanguageCodeLT(v string) predicate.ProviderSpokenLanguage { + return predicate.ProviderSpokenLanguage(sql.FieldLT(FieldLanguageCode, v)) +} + +// LanguageCodeLTE applies the LTE predicate on the "language_code" field. +func LanguageCodeLTE(v string) predicate.ProviderSpokenLanguage { + return predicate.ProviderSpokenLanguage(sql.FieldLTE(FieldLanguageCode, v)) +} + +// LanguageCodeContains applies the Contains predicate on the "language_code" field. +func LanguageCodeContains(v string) predicate.ProviderSpokenLanguage { + return predicate.ProviderSpokenLanguage(sql.FieldContains(FieldLanguageCode, v)) +} + +// LanguageCodeHasPrefix applies the HasPrefix predicate on the "language_code" field. +func LanguageCodeHasPrefix(v string) predicate.ProviderSpokenLanguage { + return predicate.ProviderSpokenLanguage(sql.FieldHasPrefix(FieldLanguageCode, v)) +} + +// LanguageCodeHasSuffix applies the HasSuffix predicate on the "language_code" field. +func LanguageCodeHasSuffix(v string) predicate.ProviderSpokenLanguage { + return predicate.ProviderSpokenLanguage(sql.FieldHasSuffix(FieldLanguageCode, v)) +} + +// LanguageCodeEqualFold applies the EqualFold predicate on the "language_code" field. +func LanguageCodeEqualFold(v string) predicate.ProviderSpokenLanguage { + return predicate.ProviderSpokenLanguage(sql.FieldEqualFold(FieldLanguageCode, v)) +} + +// LanguageCodeContainsFold applies the ContainsFold predicate on the "language_code" field. +func LanguageCodeContainsFold(v string) predicate.ProviderSpokenLanguage { + return predicate.ProviderSpokenLanguage(sql.FieldContainsFold(FieldLanguageCode, v)) +} + +// HasProfile applies the HasEdge predicate on the "profile" edge. +func HasProfile() predicate.ProviderSpokenLanguage { + return predicate.ProviderSpokenLanguage(func(s *sql.Selector) { + step := sqlgraph.NewStep( + sqlgraph.From(Table, ProfileColumn), + sqlgraph.Edge(sqlgraph.M2O, false, ProfileTable, ProfileColumn), + ) + sqlgraph.HasNeighbors(s, step) + }) +} + +// HasProfileWith applies the HasEdge predicate on the "profile" edge with a given conditions (other predicates). +func HasProfileWith(preds ...predicate.ProviderProfile) predicate.ProviderSpokenLanguage { + return predicate.ProviderSpokenLanguage(func(s *sql.Selector) { + step := newProfileStep() + sqlgraph.HasNeighborsWith(s, step, func(s *sql.Selector) { + for _, p := range preds { + p(s) + } + }) + }) +} + +// HasLanguage applies the HasEdge predicate on the "language" edge. +func HasLanguage() predicate.ProviderSpokenLanguage { + return predicate.ProviderSpokenLanguage(func(s *sql.Selector) { + step := sqlgraph.NewStep( + sqlgraph.From(Table, LanguageColumn), + sqlgraph.Edge(sqlgraph.M2O, false, LanguageTable, LanguageColumn), + ) + sqlgraph.HasNeighbors(s, step) + }) +} + +// HasLanguageWith applies the HasEdge predicate on the "language" edge with a given conditions (other predicates). +func HasLanguageWith(preds ...predicate.SpokenLanguage) predicate.ProviderSpokenLanguage { + return predicate.ProviderSpokenLanguage(func(s *sql.Selector) { + step := newLanguageStep() + 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.ProviderSpokenLanguage) predicate.ProviderSpokenLanguage { + return predicate.ProviderSpokenLanguage(sql.AndPredicates(predicates...)) +} + +// Or groups predicates with the OR operator between them. +func Or(predicates ...predicate.ProviderSpokenLanguage) predicate.ProviderSpokenLanguage { + return predicate.ProviderSpokenLanguage(sql.OrPredicates(predicates...)) +} + +// Not applies the not operator on the given predicate. +func Not(p predicate.ProviderSpokenLanguage) predicate.ProviderSpokenLanguage { + return predicate.ProviderSpokenLanguage(sql.NotPredicates(p)) +} diff --git a/backend/ent/providerspokenlanguage_create.go b/backend/ent/providerspokenlanguage_create.go new file mode 100644 index 0000000..5d97196 --- /dev/null +++ b/backend/ent/providerspokenlanguage_create.go @@ -0,0 +1,249 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "context" + "errors" + "fmt" + + "entgo.io/ent/dialect/sql/sqlgraph" + "entgo.io/ent/schema/field" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/providerprofile" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/providerspokenlanguage" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/spokenlanguage" + "github.com/google/uuid" +) + +// ProviderSpokenLanguageCreate is the builder for creating a ProviderSpokenLanguage entity. +type ProviderSpokenLanguageCreate struct { + config + mutation *ProviderSpokenLanguageMutation + hooks []Hook +} + +// SetInternalUserID sets the "internal_user_id" field. +func (_c *ProviderSpokenLanguageCreate) SetInternalUserID(v uuid.UUID) *ProviderSpokenLanguageCreate { + _c.mutation.SetInternalUserID(v) + return _c +} + +// SetLanguageCode sets the "language_code" field. +func (_c *ProviderSpokenLanguageCreate) SetLanguageCode(v string) *ProviderSpokenLanguageCreate { + _c.mutation.SetLanguageCode(v) + return _c +} + +// SetProfileID sets the "profile" edge to the ProviderProfile entity by ID. +func (_c *ProviderSpokenLanguageCreate) SetProfileID(id uuid.UUID) *ProviderSpokenLanguageCreate { + _c.mutation.SetProfileID(id) + return _c +} + +// SetProfile sets the "profile" edge to the ProviderProfile entity. +func (_c *ProviderSpokenLanguageCreate) SetProfile(v *ProviderProfile) *ProviderSpokenLanguageCreate { + return _c.SetProfileID(v.ID) +} + +// SetLanguageID sets the "language" edge to the SpokenLanguage entity by ID. +func (_c *ProviderSpokenLanguageCreate) SetLanguageID(id string) *ProviderSpokenLanguageCreate { + _c.mutation.SetLanguageID(id) + return _c +} + +// SetLanguage sets the "language" edge to the SpokenLanguage entity. +func (_c *ProviderSpokenLanguageCreate) SetLanguage(v *SpokenLanguage) *ProviderSpokenLanguageCreate { + return _c.SetLanguageID(v.ID) +} + +// Mutation returns the ProviderSpokenLanguageMutation object of the builder. +func (_c *ProviderSpokenLanguageCreate) Mutation() *ProviderSpokenLanguageMutation { + return _c.mutation +} + +// Save creates the ProviderSpokenLanguage in the database. +func (_c *ProviderSpokenLanguageCreate) Save(ctx context.Context) (*ProviderSpokenLanguage, error) { + return withHooks(ctx, _c.sqlSave, _c.mutation, _c.hooks) +} + +// SaveX calls Save and panics if Save returns an error. +func (_c *ProviderSpokenLanguageCreate) SaveX(ctx context.Context) *ProviderSpokenLanguage { + v, err := _c.Save(ctx) + if err != nil { + panic(err) + } + return v +} + +// Exec executes the query. +func (_c *ProviderSpokenLanguageCreate) Exec(ctx context.Context) error { + _, err := _c.Save(ctx) + return err +} + +// ExecX is like Exec, but panics if an error occurs. +func (_c *ProviderSpokenLanguageCreate) 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 *ProviderSpokenLanguageCreate) check() error { + if _, ok := _c.mutation.InternalUserID(); !ok { + return &ValidationError{Name: "internal_user_id", err: errors.New(`ent: missing required field "ProviderSpokenLanguage.internal_user_id"`)} + } + if _, ok := _c.mutation.LanguageCode(); !ok { + return &ValidationError{Name: "language_code", err: errors.New(`ent: missing required field "ProviderSpokenLanguage.language_code"`)} + } + if v, ok := _c.mutation.LanguageCode(); ok { + if err := providerspokenlanguage.LanguageCodeValidator(v); err != nil { + return &ValidationError{Name: "language_code", err: fmt.Errorf(`ent: validator failed for field "ProviderSpokenLanguage.language_code": %w`, err)} + } + } + if len(_c.mutation.ProfileIDs()) == 0 { + return &ValidationError{Name: "profile", err: errors.New(`ent: missing required edge "ProviderSpokenLanguage.profile"`)} + } + if len(_c.mutation.LanguageIDs()) == 0 { + return &ValidationError{Name: "language", err: errors.New(`ent: missing required edge "ProviderSpokenLanguage.language"`)} + } + return nil +} + +func (_c *ProviderSpokenLanguageCreate) sqlSave(ctx context.Context) (*ProviderSpokenLanguage, 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 + } + return _node, nil +} + +func (_c *ProviderSpokenLanguageCreate) createSpec() (*ProviderSpokenLanguage, *sqlgraph.CreateSpec) { + var ( + _node = &ProviderSpokenLanguage{config: _c.config} + _spec = sqlgraph.NewCreateSpec(providerspokenlanguage.Table, nil) + ) + if nodes := _c.mutation.ProfileIDs(); len(nodes) > 0 { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.M2O, + Inverse: false, + Table: providerspokenlanguage.ProfileTable, + Columns: []string{providerspokenlanguage.ProfileColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(providerprofile.FieldID, field.TypeUUID), + }, + } + for _, k := range nodes { + edge.Target.Nodes = append(edge.Target.Nodes, k) + } + _node.InternalUserID = nodes[0] + _spec.Edges = append(_spec.Edges, edge) + } + if nodes := _c.mutation.LanguageIDs(); len(nodes) > 0 { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.M2O, + Inverse: false, + Table: providerspokenlanguage.LanguageTable, + Columns: []string{providerspokenlanguage.LanguageColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(spokenlanguage.FieldID, field.TypeString), + }, + } + for _, k := range nodes { + edge.Target.Nodes = append(edge.Target.Nodes, k) + } + _node.LanguageCode = nodes[0] + _spec.Edges = append(_spec.Edges, edge) + } + return _node, _spec +} + +// ProviderSpokenLanguageCreateBulk is the builder for creating many ProviderSpokenLanguage entities in bulk. +type ProviderSpokenLanguageCreateBulk struct { + config + err error + builders []*ProviderSpokenLanguageCreate +} + +// Save creates the ProviderSpokenLanguage entities in the database. +func (_c *ProviderSpokenLanguageCreateBulk) Save(ctx context.Context) ([]*ProviderSpokenLanguage, error) { + if _c.err != nil { + return nil, _c.err + } + specs := make([]*sqlgraph.CreateSpec, len(_c.builders)) + nodes := make([]*ProviderSpokenLanguage, 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.(*ProviderSpokenLanguageMutation) + 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.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 *ProviderSpokenLanguageCreateBulk) SaveX(ctx context.Context) []*ProviderSpokenLanguage { + v, err := _c.Save(ctx) + if err != nil { + panic(err) + } + return v +} + +// Exec executes the query. +func (_c *ProviderSpokenLanguageCreateBulk) Exec(ctx context.Context) error { + _, err := _c.Save(ctx) + return err +} + +// ExecX is like Exec, but panics if an error occurs. +func (_c *ProviderSpokenLanguageCreateBulk) ExecX(ctx context.Context) { + if err := _c.Exec(ctx); err != nil { + panic(err) + } +} diff --git a/backend/ent/providerspokenlanguage_delete.go b/backend/ent/providerspokenlanguage_delete.go new file mode 100644 index 0000000..75ebae9 --- /dev/null +++ b/backend/ent/providerspokenlanguage_delete.go @@ -0,0 +1,87 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "context" + + "entgo.io/ent/dialect/sql" + "entgo.io/ent/dialect/sql/sqlgraph" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/predicate" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/providerspokenlanguage" +) + +// ProviderSpokenLanguageDelete is the builder for deleting a ProviderSpokenLanguage entity. +type ProviderSpokenLanguageDelete struct { + config + hooks []Hook + mutation *ProviderSpokenLanguageMutation +} + +// Where appends a list predicates to the ProviderSpokenLanguageDelete builder. +func (_d *ProviderSpokenLanguageDelete) Where(ps ...predicate.ProviderSpokenLanguage) *ProviderSpokenLanguageDelete { + _d.mutation.Where(ps...) + return _d +} + +// Exec executes the deletion query and returns how many vertices were deleted. +func (_d *ProviderSpokenLanguageDelete) 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 *ProviderSpokenLanguageDelete) ExecX(ctx context.Context) int { + n, err := _d.Exec(ctx) + if err != nil { + panic(err) + } + return n +} + +func (_d *ProviderSpokenLanguageDelete) sqlExec(ctx context.Context) (int, error) { + _spec := sqlgraph.NewDeleteSpec(providerspokenlanguage.Table, nil) + 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 +} + +// ProviderSpokenLanguageDeleteOne is the builder for deleting a single ProviderSpokenLanguage entity. +type ProviderSpokenLanguageDeleteOne struct { + _d *ProviderSpokenLanguageDelete +} + +// Where appends a list predicates to the ProviderSpokenLanguageDelete builder. +func (_d *ProviderSpokenLanguageDeleteOne) Where(ps ...predicate.ProviderSpokenLanguage) *ProviderSpokenLanguageDeleteOne { + _d._d.mutation.Where(ps...) + return _d +} + +// Exec executes the deletion query. +func (_d *ProviderSpokenLanguageDeleteOne) Exec(ctx context.Context) error { + n, err := _d._d.Exec(ctx) + switch { + case err != nil: + return err + case n == 0: + return &NotFoundError{providerspokenlanguage.Label} + default: + return nil + } +} + +// ExecX is like Exec, but panics if an error occurs. +func (_d *ProviderSpokenLanguageDeleteOne) ExecX(ctx context.Context) { + if err := _d.Exec(ctx); err != nil { + panic(err) + } +} diff --git a/backend/ent/providerspokenlanguage_query.go b/backend/ent/providerspokenlanguage_query.go new file mode 100644 index 0000000..590211d --- /dev/null +++ b/backend/ent/providerspokenlanguage_query.go @@ -0,0 +1,604 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "context" + "fmt" + "math" + + "entgo.io/ent" + "entgo.io/ent/dialect/sql" + "entgo.io/ent/dialect/sql/sqlgraph" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/predicate" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/providerprofile" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/providerspokenlanguage" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/spokenlanguage" + "github.com/google/uuid" +) + +// ProviderSpokenLanguageQuery is the builder for querying ProviderSpokenLanguage entities. +type ProviderSpokenLanguageQuery struct { + config + ctx *QueryContext + order []providerspokenlanguage.OrderOption + inters []Interceptor + predicates []predicate.ProviderSpokenLanguage + withProfile *ProviderProfileQuery + withLanguage *SpokenLanguageQuery + // intermediate query (i.e. traversal path). + sql *sql.Selector + path func(context.Context) (*sql.Selector, error) +} + +// Where adds a new predicate for the ProviderSpokenLanguageQuery builder. +func (_q *ProviderSpokenLanguageQuery) Where(ps ...predicate.ProviderSpokenLanguage) *ProviderSpokenLanguageQuery { + _q.predicates = append(_q.predicates, ps...) + return _q +} + +// Limit the number of records to be returned by this query. +func (_q *ProviderSpokenLanguageQuery) Limit(limit int) *ProviderSpokenLanguageQuery { + _q.ctx.Limit = &limit + return _q +} + +// Offset to start from. +func (_q *ProviderSpokenLanguageQuery) Offset(offset int) *ProviderSpokenLanguageQuery { + _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 *ProviderSpokenLanguageQuery) Unique(unique bool) *ProviderSpokenLanguageQuery { + _q.ctx.Unique = &unique + return _q +} + +// Order specifies how the records should be ordered. +func (_q *ProviderSpokenLanguageQuery) Order(o ...providerspokenlanguage.OrderOption) *ProviderSpokenLanguageQuery { + _q.order = append(_q.order, o...) + return _q +} + +// QueryProfile chains the current query on the "profile" edge. +func (_q *ProviderSpokenLanguageQuery) QueryProfile() *ProviderProfileQuery { + query := (&ProviderProfileClient{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(providerspokenlanguage.Table, providerspokenlanguage.ProfileColumn, selector), + sqlgraph.To(providerprofile.Table, providerprofile.FieldID), + sqlgraph.Edge(sqlgraph.M2O, false, providerspokenlanguage.ProfileTable, providerspokenlanguage.ProfileColumn), + ) + fromU = sqlgraph.SetNeighbors(_q.driver.Dialect(), step) + return fromU, nil + } + return query +} + +// QueryLanguage chains the current query on the "language" edge. +func (_q *ProviderSpokenLanguageQuery) QueryLanguage() *SpokenLanguageQuery { + query := (&SpokenLanguageClient{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(providerspokenlanguage.Table, providerspokenlanguage.LanguageColumn, selector), + sqlgraph.To(spokenlanguage.Table, spokenlanguage.FieldID), + sqlgraph.Edge(sqlgraph.M2O, false, providerspokenlanguage.LanguageTable, providerspokenlanguage.LanguageColumn), + ) + fromU = sqlgraph.SetNeighbors(_q.driver.Dialect(), step) + return fromU, nil + } + return query +} + +// First returns the first ProviderSpokenLanguage entity from the query. +// Returns a *NotFoundError when no ProviderSpokenLanguage was found. +func (_q *ProviderSpokenLanguageQuery) First(ctx context.Context) (*ProviderSpokenLanguage, 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{providerspokenlanguage.Label} + } + return nodes[0], nil +} + +// FirstX is like First, but panics if an error occurs. +func (_q *ProviderSpokenLanguageQuery) FirstX(ctx context.Context) *ProviderSpokenLanguage { + node, err := _q.First(ctx) + if err != nil && !IsNotFound(err) { + panic(err) + } + return node +} + +// Only returns a single ProviderSpokenLanguage entity found by the query, ensuring it only returns one. +// Returns a *NotSingularError when more than one ProviderSpokenLanguage entity is found. +// Returns a *NotFoundError when no ProviderSpokenLanguage entities are found. +func (_q *ProviderSpokenLanguageQuery) Only(ctx context.Context) (*ProviderSpokenLanguage, 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{providerspokenlanguage.Label} + default: + return nil, &NotSingularError{providerspokenlanguage.Label} + } +} + +// OnlyX is like Only, but panics if an error occurs. +func (_q *ProviderSpokenLanguageQuery) OnlyX(ctx context.Context) *ProviderSpokenLanguage { + node, err := _q.Only(ctx) + if err != nil { + panic(err) + } + return node +} + +// All executes the query and returns a list of ProviderSpokenLanguages. +func (_q *ProviderSpokenLanguageQuery) All(ctx context.Context) ([]*ProviderSpokenLanguage, error) { + ctx = setContextOp(ctx, _q.ctx, ent.OpQueryAll) + if err := _q.prepareQuery(ctx); err != nil { + return nil, err + } + qr := querierAll[[]*ProviderSpokenLanguage, *ProviderSpokenLanguageQuery]() + return withInterceptors[[]*ProviderSpokenLanguage](ctx, _q, qr, _q.inters) +} + +// AllX is like All, but panics if an error occurs. +func (_q *ProviderSpokenLanguageQuery) AllX(ctx context.Context) []*ProviderSpokenLanguage { + nodes, err := _q.All(ctx) + if err != nil { + panic(err) + } + return nodes +} + +// Count returns the count of the given query. +func (_q *ProviderSpokenLanguageQuery) 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[*ProviderSpokenLanguageQuery](), _q.inters) +} + +// CountX is like Count, but panics if an error occurs. +func (_q *ProviderSpokenLanguageQuery) 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 *ProviderSpokenLanguageQuery) Exist(ctx context.Context) (bool, error) { + ctx = setContextOp(ctx, _q.ctx, ent.OpQueryExist) + switch _, err := _q.First(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 *ProviderSpokenLanguageQuery) ExistX(ctx context.Context) bool { + exist, err := _q.Exist(ctx) + if err != nil { + panic(err) + } + return exist +} + +// Clone returns a duplicate of the ProviderSpokenLanguageQuery 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 *ProviderSpokenLanguageQuery) Clone() *ProviderSpokenLanguageQuery { + if _q == nil { + return nil + } + return &ProviderSpokenLanguageQuery{ + config: _q.config, + ctx: _q.ctx.Clone(), + order: append([]providerspokenlanguage.OrderOption{}, _q.order...), + inters: append([]Interceptor{}, _q.inters...), + predicates: append([]predicate.ProviderSpokenLanguage{}, _q.predicates...), + withProfile: _q.withProfile.Clone(), + withLanguage: _q.withLanguage.Clone(), + // clone intermediate query. + sql: _q.sql.Clone(), + path: _q.path, + } +} + +// WithProfile tells the query-builder to eager-load the nodes that are connected to +// the "profile" edge. The optional arguments are used to configure the query builder of the edge. +func (_q *ProviderSpokenLanguageQuery) WithProfile(opts ...func(*ProviderProfileQuery)) *ProviderSpokenLanguageQuery { + query := (&ProviderProfileClient{config: _q.config}).Query() + for _, opt := range opts { + opt(query) + } + _q.withProfile = query + return _q +} + +// WithLanguage tells the query-builder to eager-load the nodes that are connected to +// the "language" edge. The optional arguments are used to configure the query builder of the edge. +func (_q *ProviderSpokenLanguageQuery) WithLanguage(opts ...func(*SpokenLanguageQuery)) *ProviderSpokenLanguageQuery { + query := (&SpokenLanguageClient{config: _q.config}).Query() + for _, opt := range opts { + opt(query) + } + _q.withLanguage = 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 { +// InternalUserID uuid.UUID `json:"internal_user_id,omitempty"` +// Count int `json:"count,omitempty"` +// } +// +// client.ProviderSpokenLanguage.Query(). +// GroupBy(providerspokenlanguage.FieldInternalUserID). +// Aggregate(ent.Count()). +// Scan(ctx, &v) +func (_q *ProviderSpokenLanguageQuery) GroupBy(field string, fields ...string) *ProviderSpokenLanguageGroupBy { + _q.ctx.Fields = append([]string{field}, fields...) + grbuild := &ProviderSpokenLanguageGroupBy{build: _q} + grbuild.flds = &_q.ctx.Fields + grbuild.label = providerspokenlanguage.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 { +// InternalUserID uuid.UUID `json:"internal_user_id,omitempty"` +// } +// +// client.ProviderSpokenLanguage.Query(). +// Select(providerspokenlanguage.FieldInternalUserID). +// Scan(ctx, &v) +func (_q *ProviderSpokenLanguageQuery) Select(fields ...string) *ProviderSpokenLanguageSelect { + _q.ctx.Fields = append(_q.ctx.Fields, fields...) + sbuild := &ProviderSpokenLanguageSelect{ProviderSpokenLanguageQuery: _q} + sbuild.label = providerspokenlanguage.Label + sbuild.flds, sbuild.scan = &_q.ctx.Fields, sbuild.Scan + return sbuild +} + +// Aggregate returns a ProviderSpokenLanguageSelect configured with the given aggregations. +func (_q *ProviderSpokenLanguageQuery) Aggregate(fns ...AggregateFunc) *ProviderSpokenLanguageSelect { + return _q.Select().Aggregate(fns...) +} + +func (_q *ProviderSpokenLanguageQuery) 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 !providerspokenlanguage.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 *ProviderSpokenLanguageQuery) sqlAll(ctx context.Context, hooks ...queryHook) ([]*ProviderSpokenLanguage, error) { + var ( + nodes = []*ProviderSpokenLanguage{} + _spec = _q.querySpec() + loadedTypes = [2]bool{ + _q.withProfile != nil, + _q.withLanguage != nil, + } + ) + _spec.ScanValues = func(columns []string) ([]any, error) { + return (*ProviderSpokenLanguage).scanValues(nil, columns) + } + _spec.Assign = func(columns []string, values []any) error { + node := &ProviderSpokenLanguage{config: _q.config} + nodes = append(nodes, node) + node.Edges.loadedTypes = loadedTypes + return node.assignValues(columns, values) + } + 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.withProfile; query != nil { + if err := _q.loadProfile(ctx, query, nodes, nil, + func(n *ProviderSpokenLanguage, e *ProviderProfile) { n.Edges.Profile = e }); err != nil { + return nil, err + } + } + if query := _q.withLanguage; query != nil { + if err := _q.loadLanguage(ctx, query, nodes, nil, + func(n *ProviderSpokenLanguage, e *SpokenLanguage) { n.Edges.Language = e }); err != nil { + return nil, err + } + } + return nodes, nil +} + +func (_q *ProviderSpokenLanguageQuery) loadProfile(ctx context.Context, query *ProviderProfileQuery, nodes []*ProviderSpokenLanguage, init func(*ProviderSpokenLanguage), assign func(*ProviderSpokenLanguage, *ProviderProfile)) error { + ids := make([]uuid.UUID, 0, len(nodes)) + nodeids := make(map[uuid.UUID][]*ProviderSpokenLanguage) + for i := range nodes { + fk := nodes[i].InternalUserID + if _, ok := nodeids[fk]; !ok { + ids = append(ids, fk) + } + nodeids[fk] = append(nodeids[fk], nodes[i]) + } + if len(ids) == 0 { + return nil + } + query.Where(providerprofile.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 "internal_user_id" returned %v`, n.ID) + } + for i := range nodes { + assign(nodes[i], n) + } + } + return nil +} +func (_q *ProviderSpokenLanguageQuery) loadLanguage(ctx context.Context, query *SpokenLanguageQuery, nodes []*ProviderSpokenLanguage, init func(*ProviderSpokenLanguage), assign func(*ProviderSpokenLanguage, *SpokenLanguage)) error { + ids := make([]string, 0, len(nodes)) + nodeids := make(map[string][]*ProviderSpokenLanguage) + for i := range nodes { + fk := nodes[i].LanguageCode + if _, ok := nodeids[fk]; !ok { + ids = append(ids, fk) + } + nodeids[fk] = append(nodeids[fk], nodes[i]) + } + if len(ids) == 0 { + return nil + } + query.Where(spokenlanguage.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 "language_code" returned %v`, n.ID) + } + for i := range nodes { + assign(nodes[i], n) + } + } + return nil +} + +func (_q *ProviderSpokenLanguageQuery) sqlCount(ctx context.Context) (int, error) { + _spec := _q.querySpec() + _spec.Unique = false + _spec.Node.Columns = nil + return sqlgraph.CountNodes(ctx, _q.driver, _spec) +} + +func (_q *ProviderSpokenLanguageQuery) querySpec() *sqlgraph.QuerySpec { + _spec := sqlgraph.NewQuerySpec(providerspokenlanguage.Table, providerspokenlanguage.Columns, nil) + _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)) + for i := range fields { + _spec.Node.Columns = append(_spec.Node.Columns, fields[i]) + } + if _q.withProfile != nil { + _spec.Node.AddColumnOnce(providerspokenlanguage.FieldInternalUserID) + } + if _q.withLanguage != nil { + _spec.Node.AddColumnOnce(providerspokenlanguage.FieldLanguageCode) + } + } + 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 *ProviderSpokenLanguageQuery) sqlQuery(ctx context.Context) *sql.Selector { + builder := sql.Dialect(_q.driver.Dialect()) + t1 := builder.Table(providerspokenlanguage.Table) + columns := _q.ctx.Fields + if len(columns) == 0 { + columns = providerspokenlanguage.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 _, 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 +} + +// ProviderSpokenLanguageGroupBy is the group-by builder for ProviderSpokenLanguage entities. +type ProviderSpokenLanguageGroupBy struct { + selector + build *ProviderSpokenLanguageQuery +} + +// Aggregate adds the given aggregation functions to the group-by query. +func (_g *ProviderSpokenLanguageGroupBy) Aggregate(fns ...AggregateFunc) *ProviderSpokenLanguageGroupBy { + _g.fns = append(_g.fns, fns...) + return _g +} + +// Scan applies the selector query and scans the result into the given value. +func (_g *ProviderSpokenLanguageGroupBy) 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[*ProviderSpokenLanguageQuery, *ProviderSpokenLanguageGroupBy](ctx, _g.build, _g, _g.build.inters, v) +} + +func (_g *ProviderSpokenLanguageGroupBy) sqlScan(ctx context.Context, root *ProviderSpokenLanguageQuery, 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) +} + +// ProviderSpokenLanguageSelect is the builder for selecting fields of ProviderSpokenLanguage entities. +type ProviderSpokenLanguageSelect struct { + *ProviderSpokenLanguageQuery + selector +} + +// Aggregate adds the given aggregation functions to the selector query. +func (_s *ProviderSpokenLanguageSelect) Aggregate(fns ...AggregateFunc) *ProviderSpokenLanguageSelect { + _s.fns = append(_s.fns, fns...) + return _s +} + +// Scan applies the selector query and scans the result into the given value. +func (_s *ProviderSpokenLanguageSelect) 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[*ProviderSpokenLanguageQuery, *ProviderSpokenLanguageSelect](ctx, _s.ProviderSpokenLanguageQuery, _s, _s.inters, v) +} + +func (_s *ProviderSpokenLanguageSelect) sqlScan(ctx context.Context, root *ProviderSpokenLanguageQuery, 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/backend/ent/providerspokenlanguage_update.go b/backend/ent/providerspokenlanguage_update.go new file mode 100644 index 0000000..02836d9 --- /dev/null +++ b/backend/ent/providerspokenlanguage_update.go @@ -0,0 +1,205 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "context" + "errors" + "fmt" + + "entgo.io/ent/dialect/sql" + "entgo.io/ent/dialect/sql/sqlgraph" + "entgo.io/ent/schema/field" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/predicate" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/providerspokenlanguage" +) + +// ProviderSpokenLanguageUpdate is the builder for updating ProviderSpokenLanguage entities. +type ProviderSpokenLanguageUpdate struct { + config + hooks []Hook + mutation *ProviderSpokenLanguageMutation +} + +// Where appends a list predicates to the ProviderSpokenLanguageUpdate builder. +func (_u *ProviderSpokenLanguageUpdate) Where(ps ...predicate.ProviderSpokenLanguage) *ProviderSpokenLanguageUpdate { + _u.mutation.Where(ps...) + return _u +} + +// Mutation returns the ProviderSpokenLanguageMutation object of the builder. +func (_u *ProviderSpokenLanguageUpdate) Mutation() *ProviderSpokenLanguageMutation { + return _u.mutation +} + +// Save executes the query and returns the number of nodes affected by the update operation. +func (_u *ProviderSpokenLanguageUpdate) 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 *ProviderSpokenLanguageUpdate) SaveX(ctx context.Context) int { + affected, err := _u.Save(ctx) + if err != nil { + panic(err) + } + return affected +} + +// Exec executes the query. +func (_u *ProviderSpokenLanguageUpdate) Exec(ctx context.Context) error { + _, err := _u.Save(ctx) + return err +} + +// ExecX is like Exec, but panics if an error occurs. +func (_u *ProviderSpokenLanguageUpdate) 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 *ProviderSpokenLanguageUpdate) check() error { + if _u.mutation.ProfileCleared() && len(_u.mutation.ProfileIDs()) > 0 { + return errors.New(`ent: clearing a required unique edge "ProviderSpokenLanguage.profile"`) + } + if _u.mutation.LanguageCleared() && len(_u.mutation.LanguageIDs()) > 0 { + return errors.New(`ent: clearing a required unique edge "ProviderSpokenLanguage.language"`) + } + return nil +} + +func (_u *ProviderSpokenLanguageUpdate) sqlSave(ctx context.Context) (_node int, err error) { + if err := _u.check(); err != nil { + return _node, err + } + _spec := sqlgraph.NewUpdateSpec(providerspokenlanguage.Table, providerspokenlanguage.Columns, sqlgraph.NewFieldSpec(providerspokenlanguage.FieldInternalUserID, field.TypeUUID), sqlgraph.NewFieldSpec(providerspokenlanguage.FieldLanguageCode, field.TypeString)) + if ps := _u.mutation.predicates; len(ps) > 0 { + _spec.Predicate = func(selector *sql.Selector) { + for i := range ps { + ps[i](selector) + } + } + } + if _node, err = sqlgraph.UpdateNodes(ctx, _u.driver, _spec); err != nil { + if _, ok := err.(*sqlgraph.NotFoundError); ok { + err = &NotFoundError{providerspokenlanguage.Label} + } else if sqlgraph.IsConstraintError(err) { + err = &ConstraintError{msg: err.Error(), wrap: err} + } + return 0, err + } + _u.mutation.done = true + return _node, nil +} + +// ProviderSpokenLanguageUpdateOne is the builder for updating a single ProviderSpokenLanguage entity. +type ProviderSpokenLanguageUpdateOne struct { + config + fields []string + hooks []Hook + mutation *ProviderSpokenLanguageMutation +} + +// Mutation returns the ProviderSpokenLanguageMutation object of the builder. +func (_u *ProviderSpokenLanguageUpdateOne) Mutation() *ProviderSpokenLanguageMutation { + return _u.mutation +} + +// Where appends a list predicates to the ProviderSpokenLanguageUpdate builder. +func (_u *ProviderSpokenLanguageUpdateOne) Where(ps ...predicate.ProviderSpokenLanguage) *ProviderSpokenLanguageUpdateOne { + _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 *ProviderSpokenLanguageUpdateOne) Select(field string, fields ...string) *ProviderSpokenLanguageUpdateOne { + _u.fields = append([]string{field}, fields...) + return _u +} + +// Save executes the query and returns the updated ProviderSpokenLanguage entity. +func (_u *ProviderSpokenLanguageUpdateOne) Save(ctx context.Context) (*ProviderSpokenLanguage, error) { + return withHooks(ctx, _u.sqlSave, _u.mutation, _u.hooks) +} + +// SaveX is like Save, but panics if an error occurs. +func (_u *ProviderSpokenLanguageUpdateOne) SaveX(ctx context.Context) *ProviderSpokenLanguage { + node, err := _u.Save(ctx) + if err != nil { + panic(err) + } + return node +} + +// Exec executes the query on the entity. +func (_u *ProviderSpokenLanguageUpdateOne) Exec(ctx context.Context) error { + _, err := _u.Save(ctx) + return err +} + +// ExecX is like Exec, but panics if an error occurs. +func (_u *ProviderSpokenLanguageUpdateOne) 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 *ProviderSpokenLanguageUpdateOne) check() error { + if _u.mutation.ProfileCleared() && len(_u.mutation.ProfileIDs()) > 0 { + return errors.New(`ent: clearing a required unique edge "ProviderSpokenLanguage.profile"`) + } + if _u.mutation.LanguageCleared() && len(_u.mutation.LanguageIDs()) > 0 { + return errors.New(`ent: clearing a required unique edge "ProviderSpokenLanguage.language"`) + } + return nil +} + +func (_u *ProviderSpokenLanguageUpdateOne) sqlSave(ctx context.Context) (_node *ProviderSpokenLanguage, err error) { + if err := _u.check(); err != nil { + return _node, err + } + _spec := sqlgraph.NewUpdateSpec(providerspokenlanguage.Table, providerspokenlanguage.Columns, sqlgraph.NewFieldSpec(providerspokenlanguage.FieldInternalUserID, field.TypeUUID), sqlgraph.NewFieldSpec(providerspokenlanguage.FieldLanguageCode, field.TypeString)) + if id, ok := _u.mutation.InternalUserID(); !ok { + return nil, &ValidationError{Name: "internal_user_id", err: errors.New(`ent: missing "ProviderSpokenLanguage.internal_user_id" for update`)} + } else { + _spec.Node.CompositeID[0].Value = id + } + if id, ok := _u.mutation.LanguageCode(); !ok { + return nil, &ValidationError{Name: "language_code", err: errors.New(`ent: missing "ProviderSpokenLanguage.language_code" for update`)} + } else { + _spec.Node.CompositeID[1].Value = id + } + if fields := _u.fields; len(fields) > 0 { + _spec.Node.Columns = make([]string, len(fields)) + for i, f := range fields { + if !providerspokenlanguage.ValidColumn(f) { + return nil, &ValidationError{Name: f, err: fmt.Errorf("ent: invalid field %q for query", f)} + } + _spec.Node.Columns[i] = f + } + } + if ps := _u.mutation.predicates; len(ps) > 0 { + _spec.Predicate = func(selector *sql.Selector) { + for i := range ps { + ps[i](selector) + } + } + } + _node = &ProviderSpokenLanguage{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{providerspokenlanguage.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/backend/ent/runtime.go b/backend/ent/runtime.go new file mode 100644 index 0000000..a9552a1 --- /dev/null +++ b/backend/ent/runtime.go @@ -0,0 +1,942 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "time" + + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/administrativearea" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/contactrevealdailylimit" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/contactrevealevent" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/internaluser" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/listing" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/listingevent" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/listingmedia" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/locality" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/platformrole" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/providercontactchannel" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/providerprofile" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/providerspokenlanguage" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/schema" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/servicecategory" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/servicecategorytranslation" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/spokenlanguage" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/spokenlanguagetranslation" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/supportedlocale" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/useraccount" + "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() { + administrativeareaFields := schema.AdministrativeArea{}.Fields() + _ = administrativeareaFields + // administrativeareaDescSource is the schema descriptor for source field. + administrativeareaDescSource := administrativeareaFields[1].Descriptor() + // administrativearea.SourceValidator is a validator for the "source" field. It is called by the builders before save. + administrativearea.SourceValidator = func() func(string) error { + validators := administrativeareaDescSource.Validators + fns := [...]func(string) error{ + validators[0].(func(string) error), + validators[1].(func(string) error), + } + return func(source string) error { + for _, fn := range fns { + if err := fn(source); err != nil { + return err + } + } + return nil + } + }() + // administrativeareaDescSourceVersion is the schema descriptor for source_version field. + administrativeareaDescSourceVersion := administrativeareaFields[2].Descriptor() + // administrativearea.SourceVersionValidator is a validator for the "source_version" field. It is called by the builders before save. + administrativearea.SourceVersionValidator = func() func(string) error { + validators := administrativeareaDescSourceVersion.Validators + fns := [...]func(string) error{ + validators[0].(func(string) error), + validators[1].(func(string) error), + } + return func(source_version string) error { + for _, fn := range fns { + if err := fn(source_version); err != nil { + return err + } + } + return nil + } + }() + // administrativeareaDescExternalCode is the schema descriptor for external_code field. + administrativeareaDescExternalCode := administrativeareaFields[3].Descriptor() + // administrativearea.ExternalCodeValidator is a validator for the "external_code" field. It is called by the builders before save. + administrativearea.ExternalCodeValidator = func() func(string) error { + validators := administrativeareaDescExternalCode.Validators + fns := [...]func(string) error{ + validators[0].(func(string) error), + validators[1].(func(string) error), + } + return func(external_code string) error { + for _, fn := range fns { + if err := fn(external_code); err != nil { + return err + } + } + return nil + } + }() + // administrativeareaDescKind is the schema descriptor for kind field. + administrativeareaDescKind := administrativeareaFields[4].Descriptor() + // administrativearea.KindValidator is a validator for the "kind" field. It is called by the builders before save. + administrativearea.KindValidator = func() func(string) error { + validators := administrativeareaDescKind.Validators + fns := [...]func(string) error{ + validators[0].(func(string) error), + validators[1].(func(string) error), + } + return func(kind string) error { + for _, fn := range fns { + if err := fn(kind); err != nil { + return err + } + } + return nil + } + }() + // administrativeareaDescName is the schema descriptor for name field. + administrativeareaDescName := administrativeareaFields[5].Descriptor() + // administrativearea.NameValidator is a validator for the "name" field. It is called by the builders before save. + administrativearea.NameValidator = func() func(string) error { + validators := administrativeareaDescName.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 + } + }() + // administrativeareaDescActive is the schema descriptor for active field. + administrativeareaDescActive := administrativeareaFields[7].Descriptor() + // administrativearea.DefaultActive holds the default value on creation for the active field. + administrativearea.DefaultActive = administrativeareaDescActive.Default.(bool) + // administrativeareaDescCreatedAt is the schema descriptor for created_at field. + administrativeareaDescCreatedAt := administrativeareaFields[8].Descriptor() + // administrativearea.DefaultCreatedAt holds the default value on creation for the created_at field. + administrativearea.DefaultCreatedAt = administrativeareaDescCreatedAt.Default.(func() time.Time) + // administrativeareaDescUpdatedAt is the schema descriptor for updated_at field. + administrativeareaDescUpdatedAt := administrativeareaFields[9].Descriptor() + // administrativearea.DefaultUpdatedAt holds the default value on creation for the updated_at field. + administrativearea.DefaultUpdatedAt = administrativeareaDescUpdatedAt.Default.(func() time.Time) + // administrativearea.UpdateDefaultUpdatedAt holds the default value on update for the updated_at field. + administrativearea.UpdateDefaultUpdatedAt = administrativeareaDescUpdatedAt.UpdateDefault.(func() time.Time) + // administrativeareaDescID is the schema descriptor for id field. + administrativeareaDescID := administrativeareaFields[0].Descriptor() + // administrativearea.DefaultID holds the default value on creation for the id field. + administrativearea.DefaultID = administrativeareaDescID.Default.(func() uuid.UUID) + contactrevealdailylimitFields := schema.ContactRevealDailyLimit{}.Fields() + _ = contactrevealdailylimitFields + // contactrevealdailylimitDescSuccessfulCount is the schema descriptor for successful_count field. + contactrevealdailylimitDescSuccessfulCount := contactrevealdailylimitFields[3].Descriptor() + // contactrevealdailylimit.DefaultSuccessfulCount holds the default value on creation for the successful_count field. + contactrevealdailylimit.DefaultSuccessfulCount = contactrevealdailylimitDescSuccessfulCount.Default.(int) + // contactrevealdailylimit.SuccessfulCountValidator is a validator for the "successful_count" field. It is called by the builders before save. + contactrevealdailylimit.SuccessfulCountValidator = contactrevealdailylimitDescSuccessfulCount.Validators[0].(func(int) error) + // contactrevealdailylimitDescCreatedAt is the schema descriptor for created_at field. + contactrevealdailylimitDescCreatedAt := contactrevealdailylimitFields[4].Descriptor() + // contactrevealdailylimit.DefaultCreatedAt holds the default value on creation for the created_at field. + contactrevealdailylimit.DefaultCreatedAt = contactrevealdailylimitDescCreatedAt.Default.(func() time.Time) + // contactrevealdailylimitDescUpdatedAt is the schema descriptor for updated_at field. + contactrevealdailylimitDescUpdatedAt := contactrevealdailylimitFields[5].Descriptor() + // contactrevealdailylimit.DefaultUpdatedAt holds the default value on creation for the updated_at field. + contactrevealdailylimit.DefaultUpdatedAt = contactrevealdailylimitDescUpdatedAt.Default.(func() time.Time) + // contactrevealdailylimit.UpdateDefaultUpdatedAt holds the default value on update for the updated_at field. + contactrevealdailylimit.UpdateDefaultUpdatedAt = contactrevealdailylimitDescUpdatedAt.UpdateDefault.(func() time.Time) + // contactrevealdailylimitDescID is the schema descriptor for id field. + contactrevealdailylimitDescID := contactrevealdailylimitFields[0].Descriptor() + // contactrevealdailylimit.DefaultID holds the default value on creation for the id field. + contactrevealdailylimit.DefaultID = contactrevealdailylimitDescID.Default.(func() uuid.UUID) + contactrevealeventFields := schema.ContactRevealEvent{}.Fields() + _ = contactrevealeventFields + // contactrevealeventDescRevealedAt is the schema descriptor for revealed_at field. + contactrevealeventDescRevealedAt := contactrevealeventFields[6].Descriptor() + // contactrevealevent.DefaultRevealedAt holds the default value on creation for the revealed_at field. + contactrevealevent.DefaultRevealedAt = contactrevealeventDescRevealedAt.Default.(func() time.Time) + // contactrevealeventDescID is the schema descriptor for id field. + contactrevealeventDescID := contactrevealeventFields[0].Descriptor() + // contactrevealevent.DefaultID holds the default value on creation for the id field. + contactrevealevent.DefaultID = contactrevealeventDescID.Default.(func() uuid.UUID) + internaluserFields := schema.InternalUser{}.Fields() + _ = internaluserFields + // internaluserDescClerkSubject is the schema descriptor for clerk_subject field. + internaluserDescClerkSubject := internaluserFields[1].Descriptor() + // internaluser.ClerkSubjectValidator is a validator for the "clerk_subject" field. It is called by the builders before save. + internaluser.ClerkSubjectValidator = func() func(string) error { + validators := internaluserDescClerkSubject.Validators + fns := [...]func(string) error{ + validators[0].(func(string) error), + validators[1].(func(string) error), + } + return func(clerk_subject string) error { + for _, fn := range fns { + if err := fn(clerk_subject); err != nil { + return err + } + } + return nil + } + }() + // internaluserDescCreatedAt is the schema descriptor for created_at field. + internaluserDescCreatedAt := internaluserFields[2].Descriptor() + // internaluser.DefaultCreatedAt holds the default value on creation for the created_at field. + internaluser.DefaultCreatedAt = internaluserDescCreatedAt.Default.(func() time.Time) + // internaluserDescUpdatedAt is the schema descriptor for updated_at field. + internaluserDescUpdatedAt := internaluserFields[3].Descriptor() + // internaluser.DefaultUpdatedAt holds the default value on creation for the updated_at field. + internaluser.DefaultUpdatedAt = internaluserDescUpdatedAt.Default.(func() time.Time) + // internaluser.UpdateDefaultUpdatedAt holds the default value on update for the updated_at field. + internaluser.UpdateDefaultUpdatedAt = internaluserDescUpdatedAt.UpdateDefault.(func() time.Time) + // internaluserDescID is the schema descriptor for id field. + internaluserDescID := internaluserFields[0].Descriptor() + // internaluser.DefaultID holds the default value on creation for the id field. + internaluser.DefaultID = internaluserDescID.Default.(func() uuid.UUID) + listingFields := schema.Listing{}.Fields() + _ = listingFields + // listingDescTitle is the schema descriptor for title field. + listingDescTitle := listingFields[4].Descriptor() + // listing.TitleValidator is a validator for the "title" field. It is called by the builders before save. + listing.TitleValidator = func() func(string) error { + validators := listingDescTitle.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 + } + }() + // listingDescDescription is the schema descriptor for description field. + listingDescDescription := listingFields[5].Descriptor() + // listing.DescriptionValidator is a validator for the "description" field. It is called by the builders before save. + listing.DescriptionValidator = func() func(string) error { + validators := listingDescDescription.Validators + fns := [...]func(string) error{ + validators[0].(func(string) error), + validators[1].(func(string) error), + } + return func(description string) error { + for _, fn := range fns { + if err := fn(description); err != nil { + return err + } + } + return nil + } + }() + // listingDescPriceMinor is the schema descriptor for price_minor field. + listingDescPriceMinor := listingFields[7].Descriptor() + // listing.PriceMinorValidator is a validator for the "price_minor" field. It is called by the builders before save. + listing.PriceMinorValidator = listingDescPriceMinor.Validators[0].(func(int) error) + // listingDescCurrency is the schema descriptor for currency field. + listingDescCurrency := listingFields[8].Descriptor() + // listing.DefaultCurrency holds the default value on creation for the currency field. + listing.DefaultCurrency = listingDescCurrency.Default.(string) + // listing.CurrencyValidator is a validator for the "currency" field. It is called by the builders before save. + listing.CurrencyValidator = listingDescCurrency.Validators[0].(func(string) error) + // listingDescTravelsToCustomer is the schema descriptor for travels_to_customer field. + listingDescTravelsToCustomer := listingFields[9].Descriptor() + // listing.DefaultTravelsToCustomer holds the default value on creation for the travels_to_customer field. + listing.DefaultTravelsToCustomer = listingDescTravelsToCustomer.Default.(bool) + // listingDescReceivesCustomer is the schema descriptor for receives_customer field. + listingDescReceivesCustomer := listingFields[10].Descriptor() + // listing.DefaultReceivesCustomer holds the default value on creation for the receives_customer field. + listing.DefaultReceivesCustomer = listingDescReceivesCustomer.Default.(bool) + // listingDescRemoteServices is the schema descriptor for remote_services field. + listingDescRemoteServices := listingFields[11].Descriptor() + // listing.DefaultRemoteServices holds the default value on creation for the remote_services field. + listing.DefaultRemoteServices = listingDescRemoteServices.Default.(bool) + // listingDescRevision is the schema descriptor for revision field. + listingDescRevision := listingFields[13].Descriptor() + // listing.DefaultRevision holds the default value on creation for the revision field. + listing.DefaultRevision = listingDescRevision.Default.(int) + // listing.RevisionValidator is a validator for the "revision" field. It is called by the builders before save. + listing.RevisionValidator = listingDescRevision.Validators[0].(func(int) error) + // listingDescCreatedAt is the schema descriptor for created_at field. + listingDescCreatedAt := listingFields[14].Descriptor() + // listing.DefaultCreatedAt holds the default value on creation for the created_at field. + listing.DefaultCreatedAt = listingDescCreatedAt.Default.(func() time.Time) + // listingDescUpdatedAt is the schema descriptor for updated_at field. + listingDescUpdatedAt := listingFields[15].Descriptor() + // listing.DefaultUpdatedAt holds the default value on creation for the updated_at field. + listing.DefaultUpdatedAt = listingDescUpdatedAt.Default.(func() time.Time) + // listing.UpdateDefaultUpdatedAt holds the default value on update for the updated_at field. + listing.UpdateDefaultUpdatedAt = listingDescUpdatedAt.UpdateDefault.(func() time.Time) + // listingDescID is the schema descriptor for id field. + listingDescID := listingFields[0].Descriptor() + // listing.DefaultID holds the default value on creation for the id field. + listing.DefaultID = listingDescID.Default.(func() uuid.UUID) + listingeventFields := schema.ListingEvent{}.Fields() + _ = listingeventFields + // listingeventDescFromState is the schema descriptor for from_state field. + listingeventDescFromState := listingeventFields[4].Descriptor() + // listingevent.FromStateValidator is a validator for the "from_state" field. It is called by the builders before save. + listingevent.FromStateValidator = listingeventDescFromState.Validators[0].(func(string) error) + // listingeventDescToState is the schema descriptor for to_state field. + listingeventDescToState := listingeventFields[5].Descriptor() + // listingevent.ToStateValidator is a validator for the "to_state" field. It is called by the builders before save. + listingevent.ToStateValidator = func() func(string) error { + validators := listingeventDescToState.Validators + fns := [...]func(string) error{ + validators[0].(func(string) error), + validators[1].(func(string) error), + } + return func(to_state string) error { + for _, fn := range fns { + if err := fn(to_state); err != nil { + return err + } + } + return nil + } + }() + // listingeventDescRevision is the schema descriptor for revision field. + listingeventDescRevision := listingeventFields[6].Descriptor() + // listingevent.RevisionValidator is a validator for the "revision" field. It is called by the builders before save. + listingevent.RevisionValidator = listingeventDescRevision.Validators[0].(func(int) error) + // listingeventDescReason is the schema descriptor for reason field. + listingeventDescReason := listingeventFields[7].Descriptor() + // listingevent.ReasonValidator is a validator for the "reason" field. It is called by the builders before save. + listingevent.ReasonValidator = listingeventDescReason.Validators[0].(func(string) error) + // listingeventDescCreatedAt is the schema descriptor for created_at field. + listingeventDescCreatedAt := listingeventFields[8].Descriptor() + // listingevent.DefaultCreatedAt holds the default value on creation for the created_at field. + listingevent.DefaultCreatedAt = listingeventDescCreatedAt.Default.(func() time.Time) + // listingeventDescID is the schema descriptor for id field. + listingeventDescID := listingeventFields[0].Descriptor() + // listingevent.DefaultID holds the default value on creation for the id field. + listingevent.DefaultID = listingeventDescID.Default.(func() uuid.UUID) + listingmediaFields := schema.ListingMedia{}.Fields() + _ = listingmediaFields + // listingmediaDescOrdinal is the schema descriptor for ordinal field. + listingmediaDescOrdinal := listingmediaFields[2].Descriptor() + // listingmedia.OrdinalValidator is a validator for the "ordinal" field. It is called by the builders before save. + listingmedia.OrdinalValidator = listingmediaDescOrdinal.Validators[0].(func(int) error) + // listingmediaDescContentType is the schema descriptor for content_type field. + listingmediaDescContentType := listingmediaFields[3].Descriptor() + // listingmedia.ContentTypeValidator is a validator for the "content_type" field. It is called by the builders before save. + listingmedia.ContentTypeValidator = func() func(string) error { + validators := listingmediaDescContentType.Validators + fns := [...]func(string) error{ + validators[0].(func(string) error), + validators[1].(func(string) error), + } + return func(content_type string) error { + for _, fn := range fns { + if err := fn(content_type); err != nil { + return err + } + } + return nil + } + }() + // listingmediaDescByteSize is the schema descriptor for byte_size field. + listingmediaDescByteSize := listingmediaFields[4].Descriptor() + // listingmedia.ByteSizeValidator is a validator for the "byte_size" field. It is called by the builders before save. + listingmedia.ByteSizeValidator = listingmediaDescByteSize.Validators[0].(func(int64) error) + // listingmediaDescChecksumSha256 is the schema descriptor for checksum_sha256 field. + listingmediaDescChecksumSha256 := listingmediaFields[5].Descriptor() + // listingmedia.ChecksumSha256Validator is a validator for the "checksum_sha256" field. It is called by the builders before save. + listingmedia.ChecksumSha256Validator = func() func(string) error { + validators := listingmediaDescChecksumSha256.Validators + fns := [...]func(string) error{ + validators[0].(func(string) error), + validators[1].(func(string) error), + } + return func(checksum_sha256 string) error { + for _, fn := range fns { + if err := fn(checksum_sha256); err != nil { + return err + } + } + return nil + } + }() + // listingmediaDescObjectReference is the schema descriptor for object_reference field. + listingmediaDescObjectReference := listingmediaFields[6].Descriptor() + // listingmedia.ObjectReferenceValidator is a validator for the "object_reference" field. It is called by the builders before save. + listingmedia.ObjectReferenceValidator = func() func(string) error { + validators := listingmediaDescObjectReference.Validators + fns := [...]func(string) error{ + validators[0].(func(string) error), + validators[1].(func(string) error), + } + return func(object_reference string) error { + for _, fn := range fns { + if err := fn(object_reference); err != nil { + return err + } + } + return nil + } + }() + // listingmediaDescCreatedAt is the schema descriptor for created_at field. + listingmediaDescCreatedAt := listingmediaFields[8].Descriptor() + // listingmedia.DefaultCreatedAt holds the default value on creation for the created_at field. + listingmedia.DefaultCreatedAt = listingmediaDescCreatedAt.Default.(func() time.Time) + // listingmediaDescUpdatedAt is the schema descriptor for updated_at field. + listingmediaDescUpdatedAt := listingmediaFields[9].Descriptor() + // listingmedia.DefaultUpdatedAt holds the default value on creation for the updated_at field. + listingmedia.DefaultUpdatedAt = listingmediaDescUpdatedAt.Default.(func() time.Time) + // listingmedia.UpdateDefaultUpdatedAt holds the default value on update for the updated_at field. + listingmedia.UpdateDefaultUpdatedAt = listingmediaDescUpdatedAt.UpdateDefault.(func() time.Time) + // listingmediaDescID is the schema descriptor for id field. + listingmediaDescID := listingmediaFields[0].Descriptor() + // listingmedia.DefaultID holds the default value on creation for the id field. + listingmedia.DefaultID = listingmediaDescID.Default.(func() uuid.UUID) + localityFields := schema.Locality{}.Fields() + _ = localityFields + // localityDescSlug is the schema descriptor for slug field. + localityDescSlug := localityFields[1].Descriptor() + // locality.SlugValidator is a validator for the "slug" field. It is called by the builders before save. + locality.SlugValidator = func() func(string) error { + validators := localityDescSlug.Validators + fns := [...]func(string) error{ + validators[0].(func(string) error), + validators[1].(func(string) error), + } + return func(slug string) error { + for _, fn := range fns { + if err := fn(slug); err != nil { + return err + } + } + return nil + } + }() + // localityDescName is the schema descriptor for name field. + localityDescName := localityFields[2].Descriptor() + // locality.NameValidator is a validator for the "name" field. It is called by the builders before save. + locality.NameValidator = func() func(string) error { + validators := localityDescName.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 + } + }() + // localityDescSource is the schema descriptor for source field. + localityDescSource := localityFields[4].Descriptor() + // locality.SourceValidator is a validator for the "source" field. It is called by the builders before save. + locality.SourceValidator = func() func(string) error { + validators := localityDescSource.Validators + fns := [...]func(string) error{ + validators[0].(func(string) error), + validators[1].(func(string) error), + } + return func(source string) error { + for _, fn := range fns { + if err := fn(source); err != nil { + return err + } + } + return nil + } + }() + // localityDescSourceElementID is the schema descriptor for source_element_id field. + localityDescSourceElementID := localityFields[5].Descriptor() + // locality.SourceElementIDValidator is a validator for the "source_element_id" field. It is called by the builders before save. + locality.SourceElementIDValidator = func() func(string) error { + validators := localityDescSourceElementID.Validators + fns := [...]func(string) error{ + validators[0].(func(string) error), + validators[1].(func(string) error), + } + return func(source_element_id string) error { + for _, fn := range fns { + if err := fn(source_element_id); err != nil { + return err + } + } + return nil + } + }() + // localityDescSourceVersion is the schema descriptor for source_version field. + localityDescSourceVersion := localityFields[6].Descriptor() + // locality.SourceVersionValidator is a validator for the "source_version" field. It is called by the builders before save. + locality.SourceVersionValidator = func() func(string) error { + validators := localityDescSourceVersion.Validators + fns := [...]func(string) error{ + validators[0].(func(string) error), + validators[1].(func(string) error), + } + return func(source_version string) error { + for _, fn := range fns { + if err := fn(source_version); err != nil { + return err + } + } + return nil + } + }() + // localityDescLatitude is the schema descriptor for latitude field. + localityDescLatitude := localityFields[8].Descriptor() + // locality.LatitudeValidator is a validator for the "latitude" field. It is called by the builders before save. + locality.LatitudeValidator = func() func(float64) error { + validators := localityDescLatitude.Validators + fns := [...]func(float64) error{ + validators[0].(func(float64) error), + validators[1].(func(float64) error), + } + return func(latitude float64) error { + for _, fn := range fns { + if err := fn(latitude); err != nil { + return err + } + } + return nil + } + }() + // localityDescLongitude is the schema descriptor for longitude field. + localityDescLongitude := localityFields[9].Descriptor() + // locality.LongitudeValidator is a validator for the "longitude" field. It is called by the builders before save. + locality.LongitudeValidator = func() func(float64) error { + validators := localityDescLongitude.Validators + fns := [...]func(float64) error{ + validators[0].(func(float64) error), + validators[1].(func(float64) error), + } + return func(longitude float64) error { + for _, fn := range fns { + if err := fn(longitude); err != nil { + return err + } + } + return nil + } + }() + // localityDescActive is the schema descriptor for active field. + localityDescActive := localityFields[10].Descriptor() + // locality.DefaultActive holds the default value on creation for the active field. + locality.DefaultActive = localityDescActive.Default.(bool) + // localityDescCreatedAt is the schema descriptor for created_at field. + localityDescCreatedAt := localityFields[11].Descriptor() + // locality.DefaultCreatedAt holds the default value on creation for the created_at field. + locality.DefaultCreatedAt = localityDescCreatedAt.Default.(func() time.Time) + // localityDescUpdatedAt is the schema descriptor for updated_at field. + localityDescUpdatedAt := localityFields[12].Descriptor() + // locality.DefaultUpdatedAt holds the default value on creation for the updated_at field. + locality.DefaultUpdatedAt = localityDescUpdatedAt.Default.(func() time.Time) + // locality.UpdateDefaultUpdatedAt holds the default value on update for the updated_at field. + locality.UpdateDefaultUpdatedAt = localityDescUpdatedAt.UpdateDefault.(func() time.Time) + // localityDescID is the schema descriptor for id field. + localityDescID := localityFields[0].Descriptor() + // locality.DefaultID holds the default value on creation for the id field. + locality.DefaultID = localityDescID.Default.(func() uuid.UUID) + platformroleFields := schema.PlatformRole{}.Fields() + _ = platformroleFields + // platformroleDescRole is the schema descriptor for role field. + platformroleDescRole := platformroleFields[2].Descriptor() + // platformrole.RoleValidator is a validator for the "role" field. It is called by the builders before save. + platformrole.RoleValidator = func() func(string) error { + validators := platformroleDescRole.Validators + fns := [...]func(string) error{ + validators[0].(func(string) error), + validators[1].(func(string) error), + } + return func(role string) error { + for _, fn := range fns { + if err := fn(role); err != nil { + return err + } + } + return nil + } + }() + // platformroleDescGrantedAt is the schema descriptor for granted_at field. + platformroleDescGrantedAt := platformroleFields[3].Descriptor() + // platformrole.DefaultGrantedAt holds the default value on creation for the granted_at field. + platformrole.DefaultGrantedAt = platformroleDescGrantedAt.Default.(func() time.Time) + // platformroleDescID is the schema descriptor for id field. + platformroleDescID := platformroleFields[0].Descriptor() + // platformrole.DefaultID holds the default value on creation for the id field. + platformrole.DefaultID = platformroleDescID.Default.(func() uuid.UUID) + providercontactchannelFields := schema.ProviderContactChannel{}.Fields() + _ = providercontactchannelFields + // providercontactchannelDescKeyVersion is the schema descriptor for key_version field. + providercontactchannelDescKeyVersion := providercontactchannelFields[5].Descriptor() + // providercontactchannel.KeyVersionValidator is a validator for the "key_version" field. It is called by the builders before save. + providercontactchannel.KeyVersionValidator = func() func(string) error { + validators := providercontactchannelDescKeyVersion.Validators + fns := [...]func(string) error{ + validators[0].(func(string) error), + validators[1].(func(string) error), + } + return func(key_version string) error { + for _, fn := range fns { + if err := fn(key_version); err != nil { + return err + } + } + return nil + } + }() + // providercontactchannelDescEnabled is the schema descriptor for enabled field. + providercontactchannelDescEnabled := providercontactchannelFields[6].Descriptor() + // providercontactchannel.DefaultEnabled holds the default value on creation for the enabled field. + providercontactchannel.DefaultEnabled = providercontactchannelDescEnabled.Default.(bool) + // providercontactchannelDescRevealConsent is the schema descriptor for reveal_consent field. + providercontactchannelDescRevealConsent := providercontactchannelFields[7].Descriptor() + // providercontactchannel.DefaultRevealConsent holds the default value on creation for the reveal_consent field. + providercontactchannel.DefaultRevealConsent = providercontactchannelDescRevealConsent.Default.(bool) + // providercontactchannelDescCreatedAt is the schema descriptor for created_at field. + providercontactchannelDescCreatedAt := providercontactchannelFields[8].Descriptor() + // providercontactchannel.DefaultCreatedAt holds the default value on creation for the created_at field. + providercontactchannel.DefaultCreatedAt = providercontactchannelDescCreatedAt.Default.(func() time.Time) + // providercontactchannelDescUpdatedAt is the schema descriptor for updated_at field. + providercontactchannelDescUpdatedAt := providercontactchannelFields[9].Descriptor() + // providercontactchannel.DefaultUpdatedAt holds the default value on creation for the updated_at field. + providercontactchannel.DefaultUpdatedAt = providercontactchannelDescUpdatedAt.Default.(func() time.Time) + // providercontactchannel.UpdateDefaultUpdatedAt holds the default value on update for the updated_at field. + providercontactchannel.UpdateDefaultUpdatedAt = providercontactchannelDescUpdatedAt.UpdateDefault.(func() time.Time) + // providercontactchannelDescID is the schema descriptor for id field. + providercontactchannelDescID := providercontactchannelFields[0].Descriptor() + // providercontactchannel.DefaultID holds the default value on creation for the id field. + providercontactchannel.DefaultID = providercontactchannelDescID.Default.(func() uuid.UUID) + providerprofileFields := schema.ProviderProfile{}.Fields() + _ = providerprofileFields + // providerprofileDescDisplayName is the schema descriptor for display_name field. + providerprofileDescDisplayName := providerprofileFields[1].Descriptor() + // providerprofile.DisplayNameValidator is a validator for the "display_name" field. It is called by the builders before save. + providerprofile.DisplayNameValidator = func() func(string) error { + validators := providerprofileDescDisplayName.Validators + fns := [...]func(string) error{ + validators[0].(func(string) error), + validators[1].(func(string) error), + } + return func(display_name string) error { + for _, fn := range fns { + if err := fn(display_name); err != nil { + return err + } + } + return nil + } + }() + // providerprofileDescProviderType is the schema descriptor for provider_type field. + providerprofileDescProviderType := providerprofileFields[2].Descriptor() + // providerprofile.ProviderTypeValidator is a validator for the "provider_type" field. It is called by the builders before save. + providerprofile.ProviderTypeValidator = func() func(string) error { + validators := providerprofileDescProviderType.Validators + fns := [...]func(string) error{ + validators[0].(func(string) error), + validators[1].(func(string) error), + } + return func(provider_type string) error { + for _, fn := range fns { + if err := fn(provider_type); err != nil { + return err + } + } + return nil + } + }() + // providerprofileDescBio is the schema descriptor for bio field. + providerprofileDescBio := providerprofileFields[3].Descriptor() + // providerprofile.BioValidator is a validator for the "bio" field. It is called by the builders before save. + providerprofile.BioValidator = providerprofileDescBio.Validators[0].(func(string) error) + // providerprofileDescMaxTravelDistanceKm is the schema descriptor for max_travel_distance_km field. + providerprofileDescMaxTravelDistanceKm := providerprofileFields[5].Descriptor() + // providerprofile.MaxTravelDistanceKmValidator is a validator for the "max_travel_distance_km" field. It is called by the builders before save. + providerprofile.MaxTravelDistanceKmValidator = func() func(int) error { + validators := providerprofileDescMaxTravelDistanceKm.Validators + fns := [...]func(int) error{ + validators[0].(func(int) error), + validators[1].(func(int) error), + } + return func(max_travel_distance_km int) error { + for _, fn := range fns { + if err := fn(max_travel_distance_km); err != nil { + return err + } + } + return nil + } + }() + // providerprofileDescTravelsToCustomer is the schema descriptor for travels_to_customer field. + providerprofileDescTravelsToCustomer := providerprofileFields[6].Descriptor() + // providerprofile.DefaultTravelsToCustomer holds the default value on creation for the travels_to_customer field. + providerprofile.DefaultTravelsToCustomer = providerprofileDescTravelsToCustomer.Default.(bool) + // providerprofileDescReceivesCustomer is the schema descriptor for receives_customer field. + providerprofileDescReceivesCustomer := providerprofileFields[7].Descriptor() + // providerprofile.DefaultReceivesCustomer holds the default value on creation for the receives_customer field. + providerprofile.DefaultReceivesCustomer = providerprofileDescReceivesCustomer.Default.(bool) + // providerprofileDescRemoteServices is the schema descriptor for remote_services field. + providerprofileDescRemoteServices := providerprofileFields[8].Descriptor() + // providerprofile.DefaultRemoteServices holds the default value on creation for the remote_services field. + providerprofile.DefaultRemoteServices = providerprofileDescRemoteServices.Default.(bool) + // providerprofileDescCreatedAt is the schema descriptor for created_at field. + providerprofileDescCreatedAt := providerprofileFields[9].Descriptor() + // providerprofile.DefaultCreatedAt holds the default value on creation for the created_at field. + providerprofile.DefaultCreatedAt = providerprofileDescCreatedAt.Default.(func() time.Time) + // providerprofileDescUpdatedAt is the schema descriptor for updated_at field. + providerprofileDescUpdatedAt := providerprofileFields[10].Descriptor() + // providerprofile.DefaultUpdatedAt holds the default value on creation for the updated_at field. + providerprofile.DefaultUpdatedAt = providerprofileDescUpdatedAt.Default.(func() time.Time) + // providerprofile.UpdateDefaultUpdatedAt holds the default value on update for the updated_at field. + providerprofile.UpdateDefaultUpdatedAt = providerprofileDescUpdatedAt.UpdateDefault.(func() time.Time) + providerspokenlanguageFields := schema.ProviderSpokenLanguage{}.Fields() + _ = providerspokenlanguageFields + // providerspokenlanguageDescLanguageCode is the schema descriptor for language_code field. + providerspokenlanguageDescLanguageCode := providerspokenlanguageFields[1].Descriptor() + // providerspokenlanguage.LanguageCodeValidator is a validator for the "language_code" field. It is called by the builders before save. + providerspokenlanguage.LanguageCodeValidator = func() func(string) error { + validators := providerspokenlanguageDescLanguageCode.Validators + fns := [...]func(string) error{ + validators[0].(func(string) error), + validators[1].(func(string) error), + } + return func(language string) error { + for _, fn := range fns { + if err := fn(language); err != nil { + return err + } + } + return nil + } + }() + servicecategoryFields := schema.ServiceCategory{}.Fields() + _ = servicecategoryFields + // servicecategoryDescSlug is the schema descriptor for slug field. + servicecategoryDescSlug := servicecategoryFields[2].Descriptor() + // servicecategory.SlugValidator is a validator for the "slug" field. It is called by the builders before save. + servicecategory.SlugValidator = func() func(string) error { + validators := servicecategoryDescSlug.Validators + fns := [...]func(string) error{ + validators[0].(func(string) error), + validators[1].(func(string) error), + } + return func(slug string) error { + for _, fn := range fns { + if err := fn(slug); err != nil { + return err + } + } + return nil + } + }() + // servicecategoryDescActive is the schema descriptor for active field. + servicecategoryDescActive := servicecategoryFields[3].Descriptor() + // servicecategory.DefaultActive holds the default value on creation for the active field. + servicecategory.DefaultActive = servicecategoryDescActive.Default.(bool) + // servicecategoryDescSortOrder is the schema descriptor for sort_order field. + servicecategoryDescSortOrder := servicecategoryFields[4].Descriptor() + // servicecategory.SortOrderValidator is a validator for the "sort_order" field. It is called by the builders before save. + servicecategory.SortOrderValidator = servicecategoryDescSortOrder.Validators[0].(func(int) error) + // servicecategoryDescCreatedAt is the schema descriptor for created_at field. + servicecategoryDescCreatedAt := servicecategoryFields[5].Descriptor() + // servicecategory.DefaultCreatedAt holds the default value on creation for the created_at field. + servicecategory.DefaultCreatedAt = servicecategoryDescCreatedAt.Default.(func() time.Time) + // servicecategoryDescUpdatedAt is the schema descriptor for updated_at field. + servicecategoryDescUpdatedAt := servicecategoryFields[6].Descriptor() + // servicecategory.DefaultUpdatedAt holds the default value on creation for the updated_at field. + servicecategory.DefaultUpdatedAt = servicecategoryDescUpdatedAt.Default.(func() time.Time) + // servicecategory.UpdateDefaultUpdatedAt holds the default value on update for the updated_at field. + servicecategory.UpdateDefaultUpdatedAt = servicecategoryDescUpdatedAt.UpdateDefault.(func() time.Time) + // servicecategoryDescID is the schema descriptor for id field. + servicecategoryDescID := servicecategoryFields[0].Descriptor() + // servicecategory.DefaultID holds the default value on creation for the id field. + servicecategory.DefaultID = servicecategoryDescID.Default.(func() uuid.UUID) + servicecategorytranslationFields := schema.ServiceCategoryTranslation{}.Fields() + _ = servicecategorytranslationFields + // servicecategorytranslationDescLocale is the schema descriptor for locale field. + servicecategorytranslationDescLocale := servicecategorytranslationFields[1].Descriptor() + // servicecategorytranslation.LocaleValidator is a validator for the "locale" field. It is called by the builders before save. + servicecategorytranslation.LocaleValidator = func() func(string) error { + validators := servicecategorytranslationDescLocale.Validators + fns := [...]func(string) error{ + validators[0].(func(string) error), + validators[1].(func(string) error), + } + return func(locale_record string) error { + for _, fn := range fns { + if err := fn(locale_record); err != nil { + return err + } + } + return nil + } + }() + // servicecategorytranslationDescName is the schema descriptor for name field. + servicecategorytranslationDescName := servicecategorytranslationFields[2].Descriptor() + // servicecategorytranslation.NameValidator is a validator for the "name" field. It is called by the builders before save. + servicecategorytranslation.NameValidator = func() func(string) error { + validators := servicecategorytranslationDescName.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 + } + }() + // servicecategorytranslationDescDescription is the schema descriptor for description field. + servicecategorytranslationDescDescription := servicecategorytranslationFields[3].Descriptor() + // servicecategorytranslation.DescriptionValidator is a validator for the "description" field. It is called by the builders before save. + servicecategorytranslation.DescriptionValidator = servicecategorytranslationDescDescription.Validators[0].(func(string) error) + spokenlanguageFields := schema.SpokenLanguage{}.Fields() + _ = spokenlanguageFields + // spokenlanguageDescActive is the schema descriptor for active field. + spokenlanguageDescActive := spokenlanguageFields[1].Descriptor() + // spokenlanguage.DefaultActive holds the default value on creation for the active field. + spokenlanguage.DefaultActive = spokenlanguageDescActive.Default.(bool) + // spokenlanguageDescSortOrder is the schema descriptor for sort_order field. + spokenlanguageDescSortOrder := spokenlanguageFields[2].Descriptor() + // spokenlanguage.SortOrderValidator is a validator for the "sort_order" field. It is called by the builders before save. + spokenlanguage.SortOrderValidator = spokenlanguageDescSortOrder.Validators[0].(func(int) error) + // spokenlanguageDescID is the schema descriptor for id field. + spokenlanguageDescID := spokenlanguageFields[0].Descriptor() + // spokenlanguage.IDValidator is a validator for the "id" field. It is called by the builders before save. + spokenlanguage.IDValidator = func() func(string) error { + validators := spokenlanguageDescID.Validators + fns := [...]func(string) error{ + validators[0].(func(string) error), + validators[1].(func(string) error), + } + return func(id string) error { + for _, fn := range fns { + if err := fn(id); err != nil { + return err + } + } + return nil + } + }() + spokenlanguagetranslationFields := schema.SpokenLanguageTranslation{}.Fields() + _ = spokenlanguagetranslationFields + // spokenlanguagetranslationDescLanguageCode is the schema descriptor for language_code field. + spokenlanguagetranslationDescLanguageCode := spokenlanguagetranslationFields[0].Descriptor() + // spokenlanguagetranslation.LanguageCodeValidator is a validator for the "language_code" field. It is called by the builders before save. + spokenlanguagetranslation.LanguageCodeValidator = func() func(string) error { + validators := spokenlanguagetranslationDescLanguageCode.Validators + fns := [...]func(string) error{ + validators[0].(func(string) error), + validators[1].(func(string) error), + } + return func(language string) error { + for _, fn := range fns { + if err := fn(language); err != nil { + return err + } + } + return nil + } + }() + // spokenlanguagetranslationDescLocale is the schema descriptor for locale field. + spokenlanguagetranslationDescLocale := spokenlanguagetranslationFields[1].Descriptor() + // spokenlanguagetranslation.LocaleValidator is a validator for the "locale" field. It is called by the builders before save. + spokenlanguagetranslation.LocaleValidator = func() func(string) error { + validators := spokenlanguagetranslationDescLocale.Validators + fns := [...]func(string) error{ + validators[0].(func(string) error), + validators[1].(func(string) error), + } + return func(locale_record string) error { + for _, fn := range fns { + if err := fn(locale_record); err != nil { + return err + } + } + return nil + } + }() + // spokenlanguagetranslationDescName is the schema descriptor for name field. + spokenlanguagetranslationDescName := spokenlanguagetranslationFields[2].Descriptor() + // spokenlanguagetranslation.NameValidator is a validator for the "name" field. It is called by the builders before save. + spokenlanguagetranslation.NameValidator = func() func(string) error { + validators := spokenlanguagetranslationDescName.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 + } + }() + supportedlocaleFields := schema.SupportedLocale{}.Fields() + _ = supportedlocaleFields + // supportedlocaleDescActive is the schema descriptor for active field. + supportedlocaleDescActive := supportedlocaleFields[1].Descriptor() + // supportedlocale.DefaultActive holds the default value on creation for the active field. + supportedlocale.DefaultActive = supportedlocaleDescActive.Default.(bool) + // supportedlocaleDescSortOrder is the schema descriptor for sort_order field. + supportedlocaleDescSortOrder := supportedlocaleFields[2].Descriptor() + // supportedlocale.SortOrderValidator is a validator for the "sort_order" field. It is called by the builders before save. + supportedlocale.SortOrderValidator = supportedlocaleDescSortOrder.Validators[0].(func(int) error) + // supportedlocaleDescID is the schema descriptor for id field. + supportedlocaleDescID := supportedlocaleFields[0].Descriptor() + // supportedlocale.IDValidator is a validator for the "id" field. It is called by the builders before save. + supportedlocale.IDValidator = func() func(string) error { + validators := supportedlocaleDescID.Validators + fns := [...]func(string) error{ + validators[0].(func(string) error), + validators[1].(func(string) error), + } + return func(id string) error { + for _, fn := range fns { + if err := fn(id); err != nil { + return err + } + } + return nil + } + }() + useraccountFields := schema.UserAccount{}.Fields() + _ = useraccountFields + // useraccountDescProviderEnabled is the schema descriptor for provider_enabled field. + useraccountDescProviderEnabled := useraccountFields[1].Descriptor() + // useraccount.DefaultProviderEnabled holds the default value on creation for the provider_enabled field. + useraccount.DefaultProviderEnabled = useraccountDescProviderEnabled.Default.(bool) + // useraccountDescOnboardingCompletedAt is the schema descriptor for onboarding_completed_at field. + useraccountDescOnboardingCompletedAt := useraccountFields[2].Descriptor() + // useraccount.DefaultOnboardingCompletedAt holds the default value on creation for the onboarding_completed_at field. + useraccount.DefaultOnboardingCompletedAt = useraccountDescOnboardingCompletedAt.Default.(func() time.Time) + // useraccountDescCreatedAt is the schema descriptor for created_at field. + useraccountDescCreatedAt := useraccountFields[3].Descriptor() + // useraccount.DefaultCreatedAt holds the default value on creation for the created_at field. + useraccount.DefaultCreatedAt = useraccountDescCreatedAt.Default.(func() time.Time) + // useraccountDescUpdatedAt is the schema descriptor for updated_at field. + useraccountDescUpdatedAt := useraccountFields[4].Descriptor() + // useraccount.DefaultUpdatedAt holds the default value on creation for the updated_at field. + useraccount.DefaultUpdatedAt = useraccountDescUpdatedAt.Default.(func() time.Time) + // useraccount.UpdateDefaultUpdatedAt holds the default value on update for the updated_at field. + useraccount.UpdateDefaultUpdatedAt = useraccountDescUpdatedAt.UpdateDefault.(func() time.Time) +} diff --git a/backend/ent/runtime/runtime.go b/backend/ent/runtime/runtime.go new file mode 100644 index 0000000..9dc180d --- /dev/null +++ b/backend/ent/runtime/runtime.go @@ -0,0 +1,10 @@ +// Code generated by ent, DO NOT EDIT. + +package runtime + +// The schema-stitching logic is generated in github.com/SourceSenseiTheRealOne/juntly/backend/ent/runtime.go + +const ( + Version = "v0.14.6" // Version of ent codegen. + Sum = "h1:/f2696BpwuWAEEG6PVGWflg6+Inrpq4pRWuNlWz/Skk=" // Sum of ent codegen. +) diff --git a/backend/ent/schema/administrativearea.go b/backend/ent/schema/administrativearea.go new file mode 100644 index 0000000..9ffdfb5 --- /dev/null +++ b/backend/ent/schema/administrativearea.go @@ -0,0 +1,47 @@ +package schema + +import ( + "entgo.io/ent" + "entgo.io/ent/dialect/entsql" + entschema "entgo.io/ent/schema" + "entgo.io/ent/schema/edge" + "entgo.io/ent/schema/field" + "entgo.io/ent/schema/index" + "github.com/google/uuid" +) + +type AdministrativeArea struct{ ent.Schema } + +func (AdministrativeArea) Fields() []ent.Field { + return []ent.Field{ + field.UUID("id", uuid.UUID{}).Default(uuid.New).Immutable(), + field.String("source").NotEmpty().MaxLen(40).Immutable(), + field.String("source_version").NotEmpty().MaxLen(20).Immutable(), + field.String("external_code").NotEmpty().MaxLen(32).Immutable(), + field.String("kind").NotEmpty().MaxLen(20).Immutable(), + field.String("name").NotEmpty().MaxLen(160), + field.UUID("parent_id", uuid.UUID{}).Optional().Nillable().Immutable(), + field.Bool("active").Default(true), + field.Time("created_at").Default(utcNow).Immutable(), + field.Time("updated_at").Default(utcNow).UpdateDefault(utcNow), + } +} + +func (AdministrativeArea) Edges() []ent.Edge { + return []ent.Edge{ + edge.To("children", AdministrativeArea.Type), + edge.From("parent", AdministrativeArea.Type).Ref("children").Field("parent_id").Unique().Immutable(), + edge.To("localities", Locality.Type), + } +} + +func (AdministrativeArea) Indexes() []ent.Index { + return []ent.Index{ + index.Fields("source", "external_code").Unique(), + index.Fields("parent_id", "kind"), + } +} + +func (AdministrativeArea) Annotations() []entschema.Annotation { + return []entschema.Annotation{entsql.Annotation{Table: "administrative_areas"}} +} diff --git a/backend/ent/schema/contact_reveal_schema_test.go b/backend/ent/schema/contact_reveal_schema_test.go new file mode 100644 index 0000000..404ae1f --- /dev/null +++ b/backend/ent/schema/contact_reveal_schema_test.go @@ -0,0 +1,55 @@ +package schema_test + +import ( + "os" + "strings" + "testing" +) + +func TestContactRevealSchemasUseSeparateEncryptedVaultAndLeadState(t *testing.T) { + t.Parallel() + contracts := map[string][]string{ + "providercontactchannel.go": { + "type ProviderContactChannel struct", + "field.UUID(\"internal_user_id\"", + "field.Enum(\"channel\").Values(\"phone\", \"whatsapp\")", + "field.Bytes(\"ciphertext\")", + "field.Bytes(\"nonce\")", + "field.String(\"key_version\")", + "field.Bool(\"enabled\")", + "field.Bool(\"reveal_consent\")", + "index.Fields(\"internal_user_id\", \"channel\").Unique()", + "Table: \"provider_contact_channels\"", + }, + "contactrevealdailylimit.go": { + "type ContactRevealDailyLimit struct", + "field.UUID(\"customer_internal_user_id\"", + "field.Time(\"utc_day\")", + "field.Int(\"successful_count\").Min(0)", + "index.Fields(\"customer_internal_user_id\", \"utc_day\").Unique()", + "Table: \"contact_reveal_daily_limits\"", + }, + "contactrevealevent.go": { + "type ContactRevealEvent struct", + "field.UUID(\"customer_internal_user_id\"", + "field.UUID(\"provider_internal_user_id\"", + "field.UUID(\"listing_id\"", + "field.Enum(\"channel\").Values(\"phone\", \"whatsapp\")", + "field.Time(\"utc_day\")", + "field.Time(\"revealed_at\")", + "index.Fields(\"customer_internal_user_id\", \"listing_id\", \"channel\", \"utc_day\").Unique()", + "Table: \"contact_reveal_events\"", + }, + } + for path, required := range contracts { + contents, err := os.ReadFile(path) + if err != nil { + t.Fatalf("read %s: %v", path, err) + } + for _, value := range required { + if !strings.Contains(string(contents), value) { + t.Errorf("%s missing %q", path, value) + } + } + } +} diff --git a/backend/ent/schema/contactrevealdailylimit.go b/backend/ent/schema/contactrevealdailylimit.go new file mode 100644 index 0000000..69e60ae --- /dev/null +++ b/backend/ent/schema/contactrevealdailylimit.go @@ -0,0 +1,31 @@ +package schema + +import ( + "entgo.io/ent" + "entgo.io/ent/dialect/entsql" + entschema "entgo.io/ent/schema" + "entgo.io/ent/schema/field" + "entgo.io/ent/schema/index" + "github.com/google/uuid" +) + +type ContactRevealDailyLimit struct{ ent.Schema } + +func (ContactRevealDailyLimit) Fields() []ent.Field { + return []ent.Field{ + field.UUID("id", uuid.UUID{}).Default(uuid.New).Immutable(), + field.UUID("customer_internal_user_id", uuid.UUID{}).Immutable(), + field.Time("utc_day").Immutable(), + field.Int("successful_count").Min(0).Default(0), + field.Time("created_at").Default(utcNow).Immutable(), + field.Time("updated_at").Default(utcNow).UpdateDefault(utcNow), + } +} + +func (ContactRevealDailyLimit) Indexes() []ent.Index { + return []ent.Index{index.Fields("customer_internal_user_id", "utc_day").Unique()} +} + +func (ContactRevealDailyLimit) Annotations() []entschema.Annotation { + return []entschema.Annotation{entsql.Annotation{Table: "contact_reveal_daily_limits"}} +} diff --git a/backend/ent/schema/contactrevealevent.go b/backend/ent/schema/contactrevealevent.go new file mode 100644 index 0000000..c819d49 --- /dev/null +++ b/backend/ent/schema/contactrevealevent.go @@ -0,0 +1,32 @@ +package schema + +import ( + "entgo.io/ent" + "entgo.io/ent/dialect/entsql" + entschema "entgo.io/ent/schema" + "entgo.io/ent/schema/field" + "entgo.io/ent/schema/index" + "github.com/google/uuid" +) + +type ContactRevealEvent struct{ ent.Schema } + +func (ContactRevealEvent) Fields() []ent.Field { + return []ent.Field{ + field.UUID("id", uuid.UUID{}).Default(uuid.New).Immutable(), + field.UUID("customer_internal_user_id", uuid.UUID{}).Immutable(), + field.UUID("provider_internal_user_id", uuid.UUID{}).Immutable(), + field.UUID("listing_id", uuid.UUID{}).Immutable(), + field.Enum("channel").Values("phone", "whatsapp").Immutable(), + field.Time("utc_day").Immutable(), + field.Time("revealed_at").Default(utcNow).Immutable(), + } +} + +func (ContactRevealEvent) Indexes() []ent.Index { + return []ent.Index{index.Fields("customer_internal_user_id", "listing_id", "channel", "utc_day").Unique()} +} + +func (ContactRevealEvent) Annotations() []entschema.Annotation { + return []entschema.Annotation{entsql.Annotation{Table: "contact_reveal_events"}} +} diff --git a/backend/ent/schema/internaluser.go b/backend/ent/schema/internaluser.go new file mode 100644 index 0000000..a331b46 --- /dev/null +++ b/backend/ent/schema/internaluser.go @@ -0,0 +1,34 @@ +package schema + +import ( + "time" + + "entgo.io/ent" + "entgo.io/ent/dialect/entsql" + entschema "entgo.io/ent/schema" + "entgo.io/ent/schema/field" + "github.com/google/uuid" +) + +type InternalUser struct { + ent.Schema +} + +func (InternalUser) Fields() []ent.Field { + return []ent.Field{ + field.UUID("id", uuid.UUID{}).Default(uuid.New).Immutable(), + field.String("clerk_subject").NotEmpty().MaxLen(255).Unique().Immutable(), + field.Time("created_at").Default(utcNow).Immutable(), + field.Time("updated_at").Default(utcNow).UpdateDefault(utcNow), + } +} + +func (InternalUser) Annotations() []entschema.Annotation { + return []entschema.Annotation{ + entsql.Annotation{Table: "internal_users"}, + } +} + +func utcNow() time.Time { + return time.Now().UTC() +} diff --git a/backend/ent/schema/internaluser_test.go b/backend/ent/schema/internaluser_test.go new file mode 100644 index 0000000..a809767 --- /dev/null +++ b/backend/ent/schema/internaluser_test.go @@ -0,0 +1,40 @@ +package schema_test + +import ( + "os" + "strings" + "testing" +) + +func TestInternalUserSchemaContract(t *testing.T) { + t.Parallel() + + contents, err := os.ReadFile("internaluser.go") + if err != nil { + t.Fatalf("read InternalUser schema: %v", err) + } + schema := string(contents) + + for _, requirement := range []string{ + "type InternalUser struct", + "field.UUID(\"id\"", + "Default(uuid.New)", + "field.String(\"clerk_subject\")", + "NotEmpty()", + "MaxLen(255)", + "Unique()", + "Immutable()", + "field.Time(\"created_at\")", + "field.Time(\"updated_at\")", + } { + if !strings.Contains(schema, requirement) { + t.Errorf("schema does not contain %q", requirement) + } + } + + for _, prohibited := range []string{"email", "display_name", "profile"} { + if strings.Contains(schema, prohibited) { + t.Errorf("schema must not include %q", prohibited) + } + } +} diff --git a/backend/ent/schema/listing.go b/backend/ent/schema/listing.go new file mode 100644 index 0000000..bc7c4b9 --- /dev/null +++ b/backend/ent/schema/listing.go @@ -0,0 +1,36 @@ +package schema + +import ( + "entgo.io/ent" + "entgo.io/ent/dialect/entsql" + entschema "entgo.io/ent/schema" + "entgo.io/ent/schema/field" + "github.com/google/uuid" +) + +type Listing struct{ ent.Schema } + +func (Listing) Fields() []ent.Field { + return []ent.Field{ + field.UUID("id", uuid.UUID{}).Default(uuid.New).Immutable(), + field.UUID("internal_user_id", uuid.UUID{}).Immutable(), + field.UUID("category_id", uuid.UUID{}), + field.UUID("primary_locality_id", uuid.UUID{}), + field.String("title").NotEmpty().MaxLen(140), + field.String("description").NotEmpty().MaxLen(4000), + field.Enum("price_type").Values("fixed", "hourly", "daily", "quote", "negotiable"), + field.Int("price_minor").Optional().Nillable().Positive(), + field.String("currency").Default("EUR").Immutable().MaxLen(3), + field.Bool("travels_to_customer").Default(false), + field.Bool("receives_customer").Default(false), + field.Bool("remote_services").Default(false), + field.Enum("state").Values("draft", "pending_review", "active", "rejected", "paused", "archived").Default("draft"), + field.Int("revision").Default(1).Min(1), + field.Time("created_at").Default(utcNow).Immutable(), + field.Time("updated_at").Default(utcNow).UpdateDefault(utcNow), + } +} + +func (Listing) Annotations() []entschema.Annotation { + return []entschema.Annotation{entsql.Annotation{Table: "listings"}} +} diff --git a/backend/ent/schema/listing_moderation_schema_test.go b/backend/ent/schema/listing_moderation_schema_test.go new file mode 100644 index 0000000..01d5d65 --- /dev/null +++ b/backend/ent/schema/listing_moderation_schema_test.go @@ -0,0 +1,101 @@ +package schema_test + +import ( + "os" + "strings" + "testing" +) + +func TestListingModerationSchemas(t *testing.T) { + t.Parallel() + + contracts := map[string][]string{ + "platformrole.go": { + "type PlatformRole struct", + "field.UUID(\"id\"", + "Default(uuid.New)", + "field.UUID(\"internal_user_id\"", + "field.String(\"role\").NotEmpty().MaxLen(20)", + "field.Time(\"granted_at\")", + "index.Fields(\"internal_user_id\", \"role\").Unique()", + "Table: \"platform_roles\"", + }, + "listing.go": { + "type Listing struct", + "field.UUID(\"id\"", + "Default(uuid.New)", + "field.UUID(\"internal_user_id\"", + "field.UUID(\"category_id\"", + "field.UUID(\"primary_locality_id\"", + "field.String(\"title\")", + "field.String(\"description\")", + "field.Enum(\"price_type\").Values(\"fixed\", \"hourly\", \"daily\", \"quote\", \"negotiable\")", + "field.Int(\"price_minor\")", + "Optional()", + "Nillable()", + "field.String(\"currency\").Default(\"EUR\")", + "field.Bool(\"travels_to_customer\")", + "field.Bool(\"receives_customer\")", + "field.Bool(\"remote_services\")", + "field.Enum(\"state\").Values(\"draft\", \"pending_review\", \"active\", \"rejected\", \"paused\", \"archived\")", + "field.Int(\"revision\").Default(1)", + "Table: \"listings\"", + }, + "listingevent.go": { + "type ListingEvent struct", + "field.UUID(\"id\"", + "field.UUID(\"listing_id\"", + "field.UUID(\"actor_internal_user_id\"", + "field.Enum(\"event_type\").Values(\"created\", \"updated\", \"submitted\", \"approved\", \"rejected\", \"paused\", \"archived\")", + "field.String(\"from_state\")", + "field.String(\"to_state\")", + "field.Int(\"revision\")", + "field.String(\"reason\")", + "Table: \"listing_events\"", + }, + "listingmedia.go": { + "type ListingMedia struct", + "field.UUID(\"id\"", + "field.UUID(\"listing_id\"", + "field.Int(\"ordinal\")", + "field.String(\"content_type\")", + "field.Int64(\"byte_size\")", + "field.String(\"checksum_sha256\")", + "field.String(\"object_reference\")", + "field.Enum(\"state\").Values(\"pending_upload\", \"ready\", \"deleted\")", + "Table: \"listing_media\"", + }, + } + + for path, required := range contracts { + path, required := path, required + t.Run(path, func(t *testing.T) { + t.Parallel() + contents, err := os.ReadFile(path) + if err != nil { + t.Fatalf("read schema %s: %v", path, err) + } + for _, requirement := range required { + if !strings.Contains(string(contents), requirement) { + t.Errorf("schema %s does not contain %q", path, requirement) + } + } + }) + } +} + +func TestListingSchemasExcludePublicContactAndStorageAuthority(t *testing.T) { + t.Parallel() + for _, path := range []string{"listing.go", "listingevent.go", "listingmedia.go"} { + contents, err := os.ReadFile(path) + if err != nil { + t.Fatalf("read %s: %v", path, err) + } + value := strings.ToLower(string(contents)) + for _, prohibited := range []string{"email", "phone", "whatsapp", "address", "clerk", "token", "session", "storage_secret", "access_key", "bucket"} { + if strings.Contains(value, prohibited) { + t.Errorf("%s must not persist %q", path, prohibited) + } + } + } +} diff --git a/backend/ent/schema/listingevent.go b/backend/ent/schema/listingevent.go new file mode 100644 index 0000000..b69b86a --- /dev/null +++ b/backend/ent/schema/listingevent.go @@ -0,0 +1,29 @@ +package schema + +import ( + "entgo.io/ent" + "entgo.io/ent/dialect/entsql" + entschema "entgo.io/ent/schema" + "entgo.io/ent/schema/field" + "github.com/google/uuid" +) + +type ListingEvent struct{ ent.Schema } + +func (ListingEvent) Fields() []ent.Field { + return []ent.Field{ + field.UUID("id", uuid.UUID{}).Default(uuid.New).Immutable(), + field.UUID("listing_id", uuid.UUID{}).Immutable(), + field.UUID("actor_internal_user_id", uuid.UUID{}).Immutable(), + field.Enum("event_type").Values("created", "updated", "submitted", "approved", "rejected", "paused", "archived").Immutable(), + field.String("from_state").Optional().Nillable().MaxLen(32).Immutable(), + field.String("to_state").NotEmpty().MaxLen(32).Immutable(), + field.Int("revision").Min(1).Immutable(), + field.String("reason").Optional().Nillable().MaxLen(500).Immutable(), + field.Time("created_at").Default(utcNow).Immutable(), + } +} + +func (ListingEvent) Annotations() []entschema.Annotation { + return []entschema.Annotation{entsql.Annotation{Table: "listing_events"}} +} diff --git a/backend/ent/schema/listingmedia.go b/backend/ent/schema/listingmedia.go new file mode 100644 index 0000000..f59db3d --- /dev/null +++ b/backend/ent/schema/listingmedia.go @@ -0,0 +1,30 @@ +package schema + +import ( + "entgo.io/ent" + "entgo.io/ent/dialect/entsql" + entschema "entgo.io/ent/schema" + "entgo.io/ent/schema/field" + "github.com/google/uuid" +) + +type ListingMedia struct{ ent.Schema } + +func (ListingMedia) Fields() []ent.Field { + return []ent.Field{ + field.UUID("id", uuid.UUID{}).Default(uuid.New).Immutable(), + field.UUID("listing_id", uuid.UUID{}).Immutable(), + field.Int("ordinal").Min(1), + field.String("content_type").NotEmpty().MaxLen(100), + field.Int64("byte_size").Positive(), + field.String("checksum_sha256").NotEmpty().MaxLen(64).Immutable(), + field.String("object_reference").NotEmpty().MaxLen(512).Immutable(), + field.Enum("state").Values("pending_upload", "ready", "deleted").Default("pending_upload"), + field.Time("created_at").Default(utcNow).Immutable(), + field.Time("updated_at").Default(utcNow).UpdateDefault(utcNow), + } +} + +func (ListingMedia) Annotations() []entschema.Annotation { + return []entschema.Annotation{entsql.Annotation{Table: "listing_media"}} +} diff --git a/backend/ent/schema/locality.go b/backend/ent/schema/locality.go new file mode 100644 index 0000000..5d56fcd --- /dev/null +++ b/backend/ent/schema/locality.go @@ -0,0 +1,41 @@ +package schema + +import ( + "entgo.io/ent" + "entgo.io/ent/dialect/entsql" + entschema "entgo.io/ent/schema" + "entgo.io/ent/schema/edge" + "entgo.io/ent/schema/field" + "github.com/google/uuid" +) + +type Locality struct{ ent.Schema } + +func (Locality) Fields() []ent.Field { + return []ent.Field{ + field.UUID("id", uuid.UUID{}).Default(uuid.New).Immutable(), + field.String("slug").NotEmpty().MaxLen(100).Unique().Immutable(), + field.String("name").NotEmpty().MaxLen(160), + field.UUID("parent_parish_id", uuid.UUID{}).Immutable(), + field.String("source").NotEmpty().MaxLen(40).Immutable(), + field.String("source_element_id").NotEmpty().MaxLen(32).Unique().Immutable(), + field.String("source_version").NotEmpty().MaxLen(20).Immutable(), + field.Time("source_retrieved_at").Immutable(), + field.Float("latitude").Min(-90).Max(90).Immutable(), + field.Float("longitude").Min(-180).Max(180).Immutable(), + field.Bool("active").Default(true), + field.Time("created_at").Default(utcNow).Immutable(), + field.Time("updated_at").Default(utcNow).UpdateDefault(utcNow), + } +} + +func (Locality) Edges() []ent.Edge { + return []ent.Edge{ + edge.From("parent_parish", AdministrativeArea.Type).Ref("localities").Field("parent_parish_id").Unique().Required().Immutable(), + edge.From("provider_profiles", ProviderProfile.Type).Ref("service_localities"), + } +} + +func (Locality) Annotations() []entschema.Annotation { + return []entschema.Annotation{entsql.Annotation{Table: "localities"}} +} diff --git a/backend/ent/schema/marketplace_reference_test.go b/backend/ent/schema/marketplace_reference_test.go new file mode 100644 index 0000000..e97ba27 --- /dev/null +++ b/backend/ent/schema/marketplace_reference_test.go @@ -0,0 +1,157 @@ +package schema_test + +import ( + "os" + "strings" + "testing" +) + +func TestMarketplaceReferenceSchemas(t *testing.T) { + t.Parallel() + + contracts := map[string][]string{ + "supportedlocale.go": { + "type SupportedLocale struct", + "field.String(\"id\")", + "MaxLen(10)", + "field.Bool(\"active\")", + "field.Int(\"sort_order\")", + "Table: \"supported_locales\"", + }, + "servicecategory.go": { + "type ServiceCategory struct", + "field.UUID(\"id\"", + "Default(uuid.New)", + "field.UUID(\"parent_id\"", + "Optional()", + "Nillable()", + "field.String(\"slug\")", + "Unique()", + "field.Bool(\"active\")", + "field.Int(\"sort_order\")", + "Table: \"service_categories\"", + }, + "servicecategorytranslation.go": { + "type ServiceCategoryTranslation struct", + "field.UUID(\"category_id\"", + "field.String(\"locale\")", + "field.String(\"name\")", + "field.String(\"description\")", + "field.ID(\"category_id\", \"locale\")", + "Table: \"service_category_translations\"", + }, + "spokenlanguage.go": { + "type SpokenLanguage struct", + "field.String(\"id\")", + "field.Bool(\"active\")", + "field.Int(\"sort_order\")", + "Table: \"spoken_languages\"", + }, + "spokenlanguagetranslation.go": { + "type SpokenLanguageTranslation struct", + "field.String(\"language_code\")", + "field.String(\"locale\")", + "field.String(\"name\")", + "field.ID(\"language_code\", \"locale\")", + "Table: \"spoken_language_translations\"", + }, + "administrativearea.go": { + "type AdministrativeArea struct", + "field.UUID(\"id\"", + "field.String(\"source\")", + "field.String(\"source_version\")", + "field.String(\"external_code\")", + "field.String(\"kind\")", + "field.String(\"name\")", + "field.UUID(\"parent_id\"", + "field.Bool(\"active\")", + "Table: \"administrative_areas\"", + }, + "locality.go": { + "type Locality struct", + "field.UUID(\"id\"", + "field.String(\"slug\")", + "field.String(\"name\")", + "field.UUID(\"parent_parish_id\"", + "field.String(\"source\")", + "field.String(\"source_element_id\")", + "field.Float(\"latitude\")", + "field.Float(\"longitude\")", + "field.Bool(\"active\")", + "Table: \"localities\"", + }, + "providerprofile.go": { + "type ProviderProfile struct", + "field.UUID(\"id\"", + "StorageKey(\"internal_user_id\")", + "field.String(\"display_name\")", + "field.String(\"provider_type\")", + "field.String(\"bio\")", + "field.UUID(\"primary_locality_id\"", + "field.Int(\"max_travel_distance_km\")", + "field.Bool(\"travels_to_customer\")", + "field.Bool(\"receives_customer\")", + "field.Bool(\"remote_services\")", + "Table: \"provider_profiles\"", + }, + "providerservicelocality.go": { + "type ProviderServiceLocality struct", + "field.UUID(\"internal_user_id\"", + "field.UUID(\"locality_id\"", + "field.ID(\"internal_user_id\", \"locality_id\")", + "Table: \"provider_service_localities\"", + }, + "providerspokenlanguage.go": { + "type ProviderSpokenLanguage struct", + "field.UUID(\"internal_user_id\"", + "field.String(\"language_code\")", + "field.ID(\"internal_user_id\", \"language_code\")", + "Table: \"provider_spoken_languages\"", + }, + } + + for path, required := range contracts { + path, required := path, required + t.Run(path, func(t *testing.T) { + t.Parallel() + contents, err := os.ReadFile(path) + if err != nil { + t.Fatalf("read schema %s: %v", path, err) + } + schema := string(contents) + for _, requirement := range required { + if !strings.Contains(schema, requirement) { + t.Errorf("schema %s does not contain %q", path, requirement) + } + } + }) + } +} + +func TestProviderProfileSchemaExcludesPrivateAndDeferredFields(t *testing.T) { + t.Parallel() + + contents, err := os.ReadFile("providerprofile.go") + if err != nil { + t.Fatalf("read provider profile schema: %v", err) + } + schema := strings.ToLower(string(contents)) + for _, prohibited := range []string{ + "email", + "phone", + "whatsapp", + "address", + "clerk", + "token", + "session", + "verification", + "payment", + "listing", + "review", + "portfolio", + } { + if strings.Contains(schema, prohibited) { + t.Errorf("provider profile schema must not include %q", prohibited) + } + } +} diff --git a/backend/ent/schema/platformrole.go b/backend/ent/schema/platformrole.go new file mode 100644 index 0000000..d304269 --- /dev/null +++ b/backend/ent/schema/platformrole.go @@ -0,0 +1,29 @@ +package schema + +import ( + "entgo.io/ent" + "entgo.io/ent/dialect/entsql" + entschema "entgo.io/ent/schema" + "entgo.io/ent/schema/field" + "entgo.io/ent/schema/index" + "github.com/google/uuid" +) + +type PlatformRole struct{ ent.Schema } + +func (PlatformRole) Fields() []ent.Field { + return []ent.Field{ + field.UUID("id", uuid.UUID{}).Default(uuid.New).Immutable(), + field.UUID("internal_user_id", uuid.UUID{}).Immutable(), + field.String("role").NotEmpty().MaxLen(20).Immutable(), + field.Time("granted_at").Default(utcNow).Immutable(), + } +} + +func (PlatformRole) Indexes() []ent.Index { + return []ent.Index{index.Fields("internal_user_id", "role").Unique()} +} + +func (PlatformRole) Annotations() []entschema.Annotation { + return []entschema.Annotation{entsql.Annotation{Table: "platform_roles"}} +} diff --git a/backend/ent/schema/providercontactchannel.go b/backend/ent/schema/providercontactchannel.go new file mode 100644 index 0000000..f3b2632 --- /dev/null +++ b/backend/ent/schema/providercontactchannel.go @@ -0,0 +1,35 @@ +package schema + +import ( + "entgo.io/ent" + "entgo.io/ent/dialect/entsql" + entschema "entgo.io/ent/schema" + "entgo.io/ent/schema/field" + "entgo.io/ent/schema/index" + "github.com/google/uuid" +) + +type ProviderContactChannel struct{ ent.Schema } + +func (ProviderContactChannel) Fields() []ent.Field { + return []ent.Field{ + field.UUID("id", uuid.UUID{}).Default(uuid.New).Immutable(), + field.UUID("internal_user_id", uuid.UUID{}).Immutable(), + field.Enum("channel").Values("phone", "whatsapp").Immutable(), + field.Bytes("ciphertext").Immutable(), + field.Bytes("nonce").Immutable(), + field.String("key_version").NotEmpty().MaxLen(32).Immutable(), + field.Bool("enabled").Default(false), + field.Bool("reveal_consent").Default(false), + field.Time("created_at").Default(utcNow).Immutable(), + field.Time("updated_at").Default(utcNow).UpdateDefault(utcNow), + } +} + +func (ProviderContactChannel) Indexes() []ent.Index { + return []ent.Index{index.Fields("internal_user_id", "channel").Unique()} +} + +func (ProviderContactChannel) Annotations() []entschema.Annotation { + return []entschema.Annotation{entsql.Annotation{Table: "provider_contact_channels"}} +} diff --git a/backend/ent/schema/providerprofile.go b/backend/ent/schema/providerprofile.go new file mode 100644 index 0000000..0ddecc7 --- /dev/null +++ b/backend/ent/schema/providerprofile.go @@ -0,0 +1,39 @@ +package schema + +import ( + "entgo.io/ent" + "entgo.io/ent/dialect/entsql" + entschema "entgo.io/ent/schema" + "entgo.io/ent/schema/edge" + "entgo.io/ent/schema/field" + "github.com/google/uuid" +) + +type ProviderProfile struct{ ent.Schema } + +func (ProviderProfile) Fields() []ent.Field { + return []ent.Field{ + field.UUID("id", uuid.UUID{}).StorageKey("internal_user_id").Immutable(), + field.String("display_name").NotEmpty().MaxLen(100), + field.String("provider_type").NotEmpty().MaxLen(20), + field.String("bio").MaxLen(1000), + field.UUID("primary_locality_id", uuid.UUID{}), + field.Int("max_travel_distance_km").Min(0).Max(200), + field.Bool("travels_to_customer").Default(false), + field.Bool("receives_customer").Default(false), + field.Bool("remote_services").Default(false), + field.Time("created_at").Default(utcNow).Immutable(), + field.Time("updated_at").Default(utcNow).UpdateDefault(utcNow), + } +} + +func (ProviderProfile) Edges() []ent.Edge { + return []ent.Edge{ + edge.To("service_localities", Locality.Type).Through("service_locality_links", ProviderServiceLocality.Type), + edge.To("spoken_languages", SpokenLanguage.Type).Through("spoken_language_links", ProviderSpokenLanguage.Type), + } +} + +func (ProviderProfile) Annotations() []entschema.Annotation { + return []entschema.Annotation{entsql.Annotation{Table: "provider_profiles"}} +} diff --git a/backend/ent/schema/providerservicelocality.go b/backend/ent/schema/providerservicelocality.go new file mode 100644 index 0000000..d17c53a --- /dev/null +++ b/backend/ent/schema/providerservicelocality.go @@ -0,0 +1,33 @@ +package schema + +import ( + "entgo.io/ent" + "entgo.io/ent/dialect/entsql" + entschema "entgo.io/ent/schema" + "entgo.io/ent/schema/edge" + "entgo.io/ent/schema/field" + "github.com/google/uuid" +) + +type ProviderServiceLocality struct{ ent.Schema } + +func (ProviderServiceLocality) Fields() []ent.Field { + return []ent.Field{ + field.UUID("internal_user_id", uuid.UUID{}).Immutable(), + field.UUID("locality_id", uuid.UUID{}).Immutable(), + } +} + +func (ProviderServiceLocality) Edges() []ent.Edge { + return []ent.Edge{ + edge.To("profile", ProviderProfile.Type).Field("internal_user_id").Unique().Required().Immutable(), + edge.To("locality", Locality.Type).Field("locality_id").Unique().Required().Immutable(), + } +} + +func (ProviderServiceLocality) Annotations() []entschema.Annotation { + return []entschema.Annotation{ + field.ID("internal_user_id", "locality_id"), + entsql.Annotation{Table: "provider_service_localities"}, + } +} diff --git a/backend/ent/schema/providerspokenlanguage.go b/backend/ent/schema/providerspokenlanguage.go new file mode 100644 index 0000000..fc8a80e --- /dev/null +++ b/backend/ent/schema/providerspokenlanguage.go @@ -0,0 +1,33 @@ +package schema + +import ( + "entgo.io/ent" + "entgo.io/ent/dialect/entsql" + entschema "entgo.io/ent/schema" + "entgo.io/ent/schema/edge" + "entgo.io/ent/schema/field" + "github.com/google/uuid" +) + +type ProviderSpokenLanguage struct{ ent.Schema } + +func (ProviderSpokenLanguage) Fields() []ent.Field { + return []ent.Field{ + field.UUID("internal_user_id", uuid.UUID{}).Immutable(), + field.String("language_code").NotEmpty().MaxLen(10).Immutable(), + } +} + +func (ProviderSpokenLanguage) Edges() []ent.Edge { + return []ent.Edge{ + edge.To("profile", ProviderProfile.Type).Field("internal_user_id").Unique().Required().Immutable(), + edge.To("language", SpokenLanguage.Type).Field("language_code").Unique().Required().Immutable(), + } +} + +func (ProviderSpokenLanguage) Annotations() []entschema.Annotation { + return []entschema.Annotation{ + field.ID("internal_user_id", "language_code"), + entsql.Annotation{Table: "provider_spoken_languages"}, + } +} diff --git a/backend/ent/schema/servicecategory.go b/backend/ent/schema/servicecategory.go new file mode 100644 index 0000000..af5c788 --- /dev/null +++ b/backend/ent/schema/servicecategory.go @@ -0,0 +1,41 @@ +package schema + +import ( + "entgo.io/ent" + "entgo.io/ent/dialect/entsql" + entschema "entgo.io/ent/schema" + "entgo.io/ent/schema/edge" + "entgo.io/ent/schema/field" + "entgo.io/ent/schema/index" + "github.com/google/uuid" +) + +type ServiceCategory struct{ ent.Schema } + +func (ServiceCategory) Fields() []ent.Field { + return []ent.Field{ + field.UUID("id", uuid.UUID{}).Default(uuid.New).Immutable(), + field.UUID("parent_id", uuid.UUID{}).Optional().Nillable(), + field.String("slug").NotEmpty().MaxLen(80).Unique().Immutable(), + field.Bool("active").Default(true), + field.Int("sort_order").NonNegative(), + field.Time("created_at").Default(utcNow).Immutable(), + field.Time("updated_at").Default(utcNow).UpdateDefault(utcNow), + } +} + +func (ServiceCategory) Edges() []ent.Edge { + return []ent.Edge{ + edge.To("children", ServiceCategory.Type), + edge.From("parent", ServiceCategory.Type).Ref("children").Field("parent_id").Unique(), + edge.To("localized_in", SupportedLocale.Type).Through("translations", ServiceCategoryTranslation.Type), + } +} + +func (ServiceCategory) Indexes() []ent.Index { + return []ent.Index{index.Fields("parent_id", "sort_order")} +} + +func (ServiceCategory) Annotations() []entschema.Annotation { + return []entschema.Annotation{entsql.Annotation{Table: "service_categories"}} +} diff --git a/backend/ent/schema/servicecategorytranslation.go b/backend/ent/schema/servicecategorytranslation.go new file mode 100644 index 0000000..5be87c6 --- /dev/null +++ b/backend/ent/schema/servicecategorytranslation.go @@ -0,0 +1,35 @@ +package schema + +import ( + "entgo.io/ent" + "entgo.io/ent/dialect/entsql" + entschema "entgo.io/ent/schema" + "entgo.io/ent/schema/edge" + "entgo.io/ent/schema/field" + "github.com/google/uuid" +) + +type ServiceCategoryTranslation struct{ ent.Schema } + +func (ServiceCategoryTranslation) Fields() []ent.Field { + return []ent.Field{ + field.UUID("category_id", uuid.UUID{}).Immutable(), + field.String("locale").NotEmpty().MaxLen(10).Immutable(), + field.String("name").NotEmpty().MaxLen(120), + field.String("description").MaxLen(500).Optional().Nillable(), + } +} + +func (ServiceCategoryTranslation) Edges() []ent.Edge { + return []ent.Edge{ + edge.To("category", ServiceCategory.Type).Field("category_id").Unique().Required().Immutable(), + edge.To("locale_record", SupportedLocale.Type).Field("locale").Unique().Required().Immutable(), + } +} + +func (ServiceCategoryTranslation) Annotations() []entschema.Annotation { + return []entschema.Annotation{ + field.ID("category_id", "locale"), + entsql.Annotation{Table: "service_category_translations"}, + } +} diff --git a/backend/ent/schema/spokenlanguage.go b/backend/ent/schema/spokenlanguage.go new file mode 100644 index 0000000..d9e22cd --- /dev/null +++ b/backend/ent/schema/spokenlanguage.go @@ -0,0 +1,30 @@ +package schema + +import ( + "entgo.io/ent" + "entgo.io/ent/dialect/entsql" + entschema "entgo.io/ent/schema" + "entgo.io/ent/schema/edge" + "entgo.io/ent/schema/field" +) + +type SpokenLanguage struct{ ent.Schema } + +func (SpokenLanguage) Fields() []ent.Field { + return []ent.Field{ + field.String("id").NotEmpty().MaxLen(10).Immutable(), + field.Bool("active").Default(true), + field.Int("sort_order").NonNegative(), + } +} + +func (SpokenLanguage) Edges() []ent.Edge { + return []ent.Edge{ + edge.To("localized_in", SupportedLocale.Type).Through("translations", SpokenLanguageTranslation.Type), + edge.From("provider_profiles", ProviderProfile.Type).Ref("spoken_languages"), + } +} + +func (SpokenLanguage) Annotations() []entschema.Annotation { + return []entschema.Annotation{entsql.Annotation{Table: "spoken_languages"}} +} diff --git a/backend/ent/schema/spokenlanguagetranslation.go b/backend/ent/schema/spokenlanguagetranslation.go new file mode 100644 index 0000000..4e576d6 --- /dev/null +++ b/backend/ent/schema/spokenlanguagetranslation.go @@ -0,0 +1,33 @@ +package schema + +import ( + "entgo.io/ent" + "entgo.io/ent/dialect/entsql" + entschema "entgo.io/ent/schema" + "entgo.io/ent/schema/edge" + "entgo.io/ent/schema/field" +) + +type SpokenLanguageTranslation struct{ ent.Schema } + +func (SpokenLanguageTranslation) Fields() []ent.Field { + return []ent.Field{ + field.String("language_code").NotEmpty().MaxLen(10).Immutable(), + field.String("locale").NotEmpty().MaxLen(10).Immutable(), + field.String("name").NotEmpty().MaxLen(80), + } +} + +func (SpokenLanguageTranslation) Edges() []ent.Edge { + return []ent.Edge{ + edge.To("language", SpokenLanguage.Type).Field("language_code").Unique().Required().Immutable(), + edge.To("locale_record", SupportedLocale.Type).Field("locale").Unique().Required().Immutable(), + } +} + +func (SpokenLanguageTranslation) Annotations() []entschema.Annotation { + return []entschema.Annotation{ + field.ID("language_code", "locale"), + entsql.Annotation{Table: "spoken_language_translations"}, + } +} diff --git a/backend/ent/schema/supportedlocale.go b/backend/ent/schema/supportedlocale.go new file mode 100644 index 0000000..d0fb152 --- /dev/null +++ b/backend/ent/schema/supportedlocale.go @@ -0,0 +1,30 @@ +package schema + +import ( + "entgo.io/ent" + "entgo.io/ent/dialect/entsql" + entschema "entgo.io/ent/schema" + "entgo.io/ent/schema/edge" + "entgo.io/ent/schema/field" +) + +type SupportedLocale struct{ ent.Schema } + +func (SupportedLocale) Fields() []ent.Field { + return []ent.Field{ + field.String("id").NotEmpty().MaxLen(10).Immutable(), + field.Bool("active").Default(true), + field.Int("sort_order").NonNegative(), + } +} + +func (SupportedLocale) Edges() []ent.Edge { + return []ent.Edge{ + edge.From("translated_categories", ServiceCategory.Type).Ref("localized_in"), + edge.From("translated_languages", SpokenLanguage.Type).Ref("localized_in"), + } +} + +func (SupportedLocale) Annotations() []entschema.Annotation { + return []entschema.Annotation{entsql.Annotation{Table: "supported_locales"}} +} diff --git a/backend/ent/schema/useraccount.go b/backend/ent/schema/useraccount.go new file mode 100644 index 0000000..6cbf4ac --- /dev/null +++ b/backend/ent/schema/useraccount.go @@ -0,0 +1,29 @@ +package schema + +import ( + "entgo.io/ent" + "entgo.io/ent/dialect/entsql" + entschema "entgo.io/ent/schema" + "entgo.io/ent/schema/field" + "github.com/google/uuid" +) + +type UserAccount struct { + ent.Schema +} + +func (UserAccount) Fields() []ent.Field { + return []ent.Field{ + field.UUID("id", uuid.UUID{}).StorageKey("internal_user_id").Immutable(), + field.Bool("provider_enabled").Default(false), + field.Time("onboarding_completed_at").Default(utcNow).Immutable(), + field.Time("created_at").Default(utcNow).Immutable(), + field.Time("updated_at").Default(utcNow).UpdateDefault(utcNow), + } +} + +func (UserAccount) Annotations() []entschema.Annotation { + return []entschema.Annotation{ + entsql.Annotation{Table: "user_accounts"}, + } +} diff --git a/backend/ent/schema/useraccount_test.go b/backend/ent/schema/useraccount_test.go new file mode 100644 index 0000000..144a493 --- /dev/null +++ b/backend/ent/schema/useraccount_test.go @@ -0,0 +1,48 @@ +package schema_test + +import ( + "os" + "strings" + "testing" +) + +func TestUserAccountSchemaContract(t *testing.T) { + t.Parallel() + + contents, err := os.ReadFile("useraccount.go") + if err != nil { + t.Fatalf("read UserAccount schema: %v", err) + } + schema := string(contents) + + for _, requirement := range []string{ + "type UserAccount struct", + "field.UUID(\"id\"", + "StorageKey(\"internal_user_id\")", + "Immutable()", + "field.Bool(\"provider_enabled\")", + "Default(false)", + "field.Time(\"onboarding_completed_at\")", + "field.Time(\"created_at\")", + "field.Time(\"updated_at\")", + "Table: \"user_accounts\"", + } { + if !strings.Contains(schema, requirement) { + t.Errorf("schema does not contain %q", requirement) + } + } + + for _, prohibited := range []string{ + "clerk_subject", + "email", + "phone", + "display_name", + "profile", + "role", + "contact", + } { + if strings.Contains(schema, prohibited) { + t.Errorf("schema must not include %q", prohibited) + } + } +} diff --git a/backend/ent/servicecategory.go b/backend/ent/servicecategory.go new file mode 100644 index 0000000..2b010ab --- /dev/null +++ b/backend/ent/servicecategory.go @@ -0,0 +1,247 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "fmt" + "strings" + "time" + + "entgo.io/ent" + "entgo.io/ent/dialect/sql" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/servicecategory" + "github.com/google/uuid" +) + +// ServiceCategory is the model entity for the ServiceCategory schema. +type ServiceCategory struct { + config `json:"-"` + // ID of the ent. + ID uuid.UUID `json:"id,omitempty"` + // ParentID holds the value of the "parent_id" field. + ParentID *uuid.UUID `json:"parent_id,omitempty"` + // Slug holds the value of the "slug" field. + Slug string `json:"slug,omitempty"` + // Active holds the value of the "active" field. + Active bool `json:"active,omitempty"` + // SortOrder holds the value of the "sort_order" field. + SortOrder int `json:"sort_order,omitempty"` + // CreatedAt holds the value of the "created_at" field. + CreatedAt time.Time `json:"created_at,omitempty"` + // UpdatedAt holds the value of the "updated_at" field. + UpdatedAt time.Time `json:"updated_at,omitempty"` + // Edges holds the relations/edges for other nodes in the graph. + // The values are being populated by the ServiceCategoryQuery when eager-loading is set. + Edges ServiceCategoryEdges `json:"edges"` + selectValues sql.SelectValues +} + +// ServiceCategoryEdges holds the relations/edges for other nodes in the graph. +type ServiceCategoryEdges struct { + // Children holds the value of the children edge. + Children []*ServiceCategory `json:"children,omitempty"` + // Parent holds the value of the parent edge. + Parent *ServiceCategory `json:"parent,omitempty"` + // LocalizedIn holds the value of the localized_in edge. + LocalizedIn []*SupportedLocale `json:"localized_in,omitempty"` + // Translations holds the value of the translations edge. + Translations []*ServiceCategoryTranslation `json:"translations,omitempty"` + // loadedTypes holds the information for reporting if a + // type was loaded (or requested) in eager-loading or not. + loadedTypes [4]bool +} + +// ChildrenOrErr returns the Children value or an error if the edge +// was not loaded in eager-loading. +func (e ServiceCategoryEdges) ChildrenOrErr() ([]*ServiceCategory, error) { + if e.loadedTypes[0] { + return e.Children, nil + } + return nil, &NotLoadedError{edge: "children"} +} + +// ParentOrErr returns the Parent value or an error if the edge +// was not loaded in eager-loading, or loaded but was not found. +func (e ServiceCategoryEdges) ParentOrErr() (*ServiceCategory, error) { + if e.Parent != nil { + return e.Parent, nil + } else if e.loadedTypes[1] { + return nil, &NotFoundError{label: servicecategory.Label} + } + return nil, &NotLoadedError{edge: "parent"} +} + +// LocalizedInOrErr returns the LocalizedIn value or an error if the edge +// was not loaded in eager-loading. +func (e ServiceCategoryEdges) LocalizedInOrErr() ([]*SupportedLocale, error) { + if e.loadedTypes[2] { + return e.LocalizedIn, nil + } + return nil, &NotLoadedError{edge: "localized_in"} +} + +// TranslationsOrErr returns the Translations value or an error if the edge +// was not loaded in eager-loading. +func (e ServiceCategoryEdges) TranslationsOrErr() ([]*ServiceCategoryTranslation, error) { + if e.loadedTypes[3] { + return e.Translations, nil + } + return nil, &NotLoadedError{edge: "translations"} +} + +// scanValues returns the types for scanning values from sql.Rows. +func (*ServiceCategory) scanValues(columns []string) ([]any, error) { + values := make([]any, len(columns)) + for i := range columns { + switch columns[i] { + case servicecategory.FieldParentID: + values[i] = &sql.NullScanner{S: new(uuid.UUID)} + case servicecategory.FieldActive: + values[i] = new(sql.NullBool) + case servicecategory.FieldSortOrder: + values[i] = new(sql.NullInt64) + case servicecategory.FieldSlug: + values[i] = new(sql.NullString) + case servicecategory.FieldCreatedAt, servicecategory.FieldUpdatedAt: + values[i] = new(sql.NullTime) + case servicecategory.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 ServiceCategory fields. +func (_m *ServiceCategory) 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 servicecategory.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 servicecategory.FieldParentID: + if value, ok := values[i].(*sql.NullScanner); !ok { + return fmt.Errorf("unexpected type %T for field parent_id", values[i]) + } else if value.Valid { + _m.ParentID = new(uuid.UUID) + *_m.ParentID = *value.S.(*uuid.UUID) + } + case servicecategory.FieldSlug: + if value, ok := values[i].(*sql.NullString); !ok { + return fmt.Errorf("unexpected type %T for field slug", values[i]) + } else if value.Valid { + _m.Slug = value.String + } + case servicecategory.FieldActive: + if value, ok := values[i].(*sql.NullBool); !ok { + return fmt.Errorf("unexpected type %T for field active", values[i]) + } else if value.Valid { + _m.Active = value.Bool + } + case servicecategory.FieldSortOrder: + if value, ok := values[i].(*sql.NullInt64); !ok { + return fmt.Errorf("unexpected type %T for field sort_order", values[i]) + } else if value.Valid { + _m.SortOrder = int(value.Int64) + } + case servicecategory.FieldCreatedAt: + if value, ok := values[i].(*sql.NullTime); !ok { + return fmt.Errorf("unexpected type %T for field created_at", values[i]) + } else if value.Valid { + _m.CreatedAt = value.Time + } + case servicecategory.FieldUpdatedAt: + if value, ok := values[i].(*sql.NullTime); !ok { + return fmt.Errorf("unexpected type %T for field updated_at", values[i]) + } else if value.Valid { + _m.UpdatedAt = value.Time + } + default: + _m.selectValues.Set(columns[i], values[i]) + } + } + return nil +} + +// Value returns the ent.Value that was dynamically selected and assigned to the ServiceCategory. +// This includes values selected through modifiers, order, etc. +func (_m *ServiceCategory) Value(name string) (ent.Value, error) { + return _m.selectValues.Get(name) +} + +// QueryChildren queries the "children" edge of the ServiceCategory entity. +func (_m *ServiceCategory) QueryChildren() *ServiceCategoryQuery { + return NewServiceCategoryClient(_m.config).QueryChildren(_m) +} + +// QueryParent queries the "parent" edge of the ServiceCategory entity. +func (_m *ServiceCategory) QueryParent() *ServiceCategoryQuery { + return NewServiceCategoryClient(_m.config).QueryParent(_m) +} + +// QueryLocalizedIn queries the "localized_in" edge of the ServiceCategory entity. +func (_m *ServiceCategory) QueryLocalizedIn() *SupportedLocaleQuery { + return NewServiceCategoryClient(_m.config).QueryLocalizedIn(_m) +} + +// QueryTranslations queries the "translations" edge of the ServiceCategory entity. +func (_m *ServiceCategory) QueryTranslations() *ServiceCategoryTranslationQuery { + return NewServiceCategoryClient(_m.config).QueryTranslations(_m) +} + +// Update returns a builder for updating this ServiceCategory. +// Note that you need to call ServiceCategory.Unwrap() before calling this method if this ServiceCategory +// was returned from a transaction, and the transaction was committed or rolled back. +func (_m *ServiceCategory) Update() *ServiceCategoryUpdateOne { + return NewServiceCategoryClient(_m.config).UpdateOne(_m) +} + +// Unwrap unwraps the ServiceCategory 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 *ServiceCategory) Unwrap() *ServiceCategory { + _tx, ok := _m.config.driver.(*txDriver) + if !ok { + panic("ent: ServiceCategory is not a transactional entity") + } + _m.config.driver = _tx.drv + return _m +} + +// String implements the fmt.Stringer. +func (_m *ServiceCategory) String() string { + var builder strings.Builder + builder.WriteString("ServiceCategory(") + builder.WriteString(fmt.Sprintf("id=%v, ", _m.ID)) + if v := _m.ParentID; v != nil { + builder.WriteString("parent_id=") + builder.WriteString(fmt.Sprintf("%v", *v)) + } + builder.WriteString(", ") + builder.WriteString("slug=") + builder.WriteString(_m.Slug) + builder.WriteString(", ") + builder.WriteString("active=") + builder.WriteString(fmt.Sprintf("%v", _m.Active)) + builder.WriteString(", ") + builder.WriteString("sort_order=") + builder.WriteString(fmt.Sprintf("%v", _m.SortOrder)) + builder.WriteString(", ") + builder.WriteString("created_at=") + builder.WriteString(_m.CreatedAt.Format(time.ANSIC)) + builder.WriteString(", ") + builder.WriteString("updated_at=") + builder.WriteString(_m.UpdatedAt.Format(time.ANSIC)) + builder.WriteByte(')') + return builder.String() +} + +// ServiceCategories is a parsable slice of ServiceCategory. +type ServiceCategories []*ServiceCategory diff --git a/backend/ent/servicecategory/servicecategory.go b/backend/ent/servicecategory/servicecategory.go new file mode 100644 index 0000000..1660e34 --- /dev/null +++ b/backend/ent/servicecategory/servicecategory.go @@ -0,0 +1,219 @@ +// Code generated by ent, DO NOT EDIT. + +package servicecategory + +import ( + "time" + + "entgo.io/ent/dialect/sql" + "entgo.io/ent/dialect/sql/sqlgraph" + "github.com/google/uuid" +) + +const ( + // Label holds the string label denoting the servicecategory type in the database. + Label = "service_category" + // FieldID holds the string denoting the id field in the database. + FieldID = "id" + // FieldParentID holds the string denoting the parent_id field in the database. + FieldParentID = "parent_id" + // FieldSlug holds the string denoting the slug field in the database. + FieldSlug = "slug" + // FieldActive holds the string denoting the active field in the database. + FieldActive = "active" + // FieldSortOrder holds the string denoting the sort_order field in the database. + FieldSortOrder = "sort_order" + // FieldCreatedAt holds the string denoting the created_at field in the database. + FieldCreatedAt = "created_at" + // FieldUpdatedAt holds the string denoting the updated_at field in the database. + FieldUpdatedAt = "updated_at" + // EdgeChildren holds the string denoting the children edge name in mutations. + EdgeChildren = "children" + // EdgeParent holds the string denoting the parent edge name in mutations. + EdgeParent = "parent" + // EdgeLocalizedIn holds the string denoting the localized_in edge name in mutations. + EdgeLocalizedIn = "localized_in" + // EdgeTranslations holds the string denoting the translations edge name in mutations. + EdgeTranslations = "translations" + // Table holds the table name of the servicecategory in the database. + Table = "service_categories" + // ChildrenTable is the table that holds the children relation/edge. + ChildrenTable = "service_categories" + // ChildrenColumn is the table column denoting the children relation/edge. + ChildrenColumn = "parent_id" + // ParentTable is the table that holds the parent relation/edge. + ParentTable = "service_categories" + // ParentColumn is the table column denoting the parent relation/edge. + ParentColumn = "parent_id" + // LocalizedInTable is the table that holds the localized_in relation/edge. The primary key declared below. + LocalizedInTable = "service_category_translations" + // LocalizedInInverseTable is the table name for the SupportedLocale entity. + // It exists in this package in order to avoid circular dependency with the "supportedlocale" package. + LocalizedInInverseTable = "supported_locales" + // TranslationsTable is the table that holds the translations relation/edge. + TranslationsTable = "service_category_translations" + // TranslationsInverseTable is the table name for the ServiceCategoryTranslation entity. + // It exists in this package in order to avoid circular dependency with the "servicecategorytranslation" package. + TranslationsInverseTable = "service_category_translations" + // TranslationsColumn is the table column denoting the translations relation/edge. + TranslationsColumn = "category_id" +) + +// Columns holds all SQL columns for servicecategory fields. +var Columns = []string{ + FieldID, + FieldParentID, + FieldSlug, + FieldActive, + FieldSortOrder, + FieldCreatedAt, + FieldUpdatedAt, +} + +var ( + // LocalizedInPrimaryKey and LocalizedInColumn2 are the table columns denoting the + // primary key for the localized_in relation (M2M). + LocalizedInPrimaryKey = []string{"category_id", "locale"} +) + +// 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 ( + // SlugValidator is a validator for the "slug" field. It is called by the builders before save. + SlugValidator func(string) error + // DefaultActive holds the default value on creation for the "active" field. + DefaultActive bool + // SortOrderValidator is a validator for the "sort_order" field. It is called by the builders before save. + SortOrderValidator func(int) error + // DefaultCreatedAt holds the default value on creation for the "created_at" field. + DefaultCreatedAt func() time.Time + // DefaultUpdatedAt holds the default value on creation for the "updated_at" field. + DefaultUpdatedAt func() time.Time + // UpdateDefaultUpdatedAt holds the default value on update for the "updated_at" field. + UpdateDefaultUpdatedAt func() time.Time + // DefaultID holds the default value on creation for the "id" field. + DefaultID func() uuid.UUID +) + +// OrderOption defines the ordering options for the ServiceCategory 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() +} + +// ByParentID orders the results by the parent_id field. +func ByParentID(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldParentID, opts...).ToFunc() +} + +// BySlug orders the results by the slug field. +func BySlug(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldSlug, opts...).ToFunc() +} + +// ByActive orders the results by the active field. +func ByActive(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldActive, opts...).ToFunc() +} + +// BySortOrder orders the results by the sort_order field. +func BySortOrder(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldSortOrder, opts...).ToFunc() +} + +// ByCreatedAt orders the results by the created_at field. +func ByCreatedAt(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldCreatedAt, opts...).ToFunc() +} + +// ByUpdatedAt orders the results by the updated_at field. +func ByUpdatedAt(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldUpdatedAt, opts...).ToFunc() +} + +// ByChildrenCount orders the results by children count. +func ByChildrenCount(opts ...sql.OrderTermOption) OrderOption { + return func(s *sql.Selector) { + sqlgraph.OrderByNeighborsCount(s, newChildrenStep(), opts...) + } +} + +// ByChildren orders the results by children terms. +func ByChildren(term sql.OrderTerm, terms ...sql.OrderTerm) OrderOption { + return func(s *sql.Selector) { + sqlgraph.OrderByNeighborTerms(s, newChildrenStep(), append([]sql.OrderTerm{term}, terms...)...) + } +} + +// ByParentField orders the results by parent field. +func ByParentField(field string, opts ...sql.OrderTermOption) OrderOption { + return func(s *sql.Selector) { + sqlgraph.OrderByNeighborTerms(s, newParentStep(), sql.OrderByField(field, opts...)) + } +} + +// ByLocalizedInCount orders the results by localized_in count. +func ByLocalizedInCount(opts ...sql.OrderTermOption) OrderOption { + return func(s *sql.Selector) { + sqlgraph.OrderByNeighborsCount(s, newLocalizedInStep(), opts...) + } +} + +// ByLocalizedIn orders the results by localized_in terms. +func ByLocalizedIn(term sql.OrderTerm, terms ...sql.OrderTerm) OrderOption { + return func(s *sql.Selector) { + sqlgraph.OrderByNeighborTerms(s, newLocalizedInStep(), append([]sql.OrderTerm{term}, terms...)...) + } +} + +// ByTranslationsCount orders the results by translations count. +func ByTranslationsCount(opts ...sql.OrderTermOption) OrderOption { + return func(s *sql.Selector) { + sqlgraph.OrderByNeighborsCount(s, newTranslationsStep(), opts...) + } +} + +// ByTranslations orders the results by translations terms. +func ByTranslations(term sql.OrderTerm, terms ...sql.OrderTerm) OrderOption { + return func(s *sql.Selector) { + sqlgraph.OrderByNeighborTerms(s, newTranslationsStep(), append([]sql.OrderTerm{term}, terms...)...) + } +} +func newChildrenStep() *sqlgraph.Step { + return sqlgraph.NewStep( + sqlgraph.From(Table, FieldID), + sqlgraph.To(Table, FieldID), + sqlgraph.Edge(sqlgraph.O2M, false, ChildrenTable, ChildrenColumn), + ) +} +func newParentStep() *sqlgraph.Step { + return sqlgraph.NewStep( + sqlgraph.From(Table, FieldID), + sqlgraph.To(Table, FieldID), + sqlgraph.Edge(sqlgraph.M2O, true, ParentTable, ParentColumn), + ) +} +func newLocalizedInStep() *sqlgraph.Step { + return sqlgraph.NewStep( + sqlgraph.From(Table, FieldID), + sqlgraph.To(LocalizedInInverseTable, FieldID), + sqlgraph.Edge(sqlgraph.M2M, false, LocalizedInTable, LocalizedInPrimaryKey...), + ) +} +func newTranslationsStep() *sqlgraph.Step { + return sqlgraph.NewStep( + sqlgraph.From(Table, FieldID), + sqlgraph.To(TranslationsInverseTable, TranslationsColumn), + sqlgraph.Edge(sqlgraph.O2M, true, TranslationsTable, TranslationsColumn), + ) +} diff --git a/backend/ent/servicecategory/where.go b/backend/ent/servicecategory/where.go new file mode 100644 index 0000000..bf77828 --- /dev/null +++ b/backend/ent/servicecategory/where.go @@ -0,0 +1,419 @@ +// Code generated by ent, DO NOT EDIT. + +package servicecategory + +import ( + "time" + + "entgo.io/ent/dialect/sql" + "entgo.io/ent/dialect/sql/sqlgraph" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/predicate" + "github.com/google/uuid" +) + +// ID filters vertices based on their ID field. +func ID(id uuid.UUID) predicate.ServiceCategory { + return predicate.ServiceCategory(sql.FieldEQ(FieldID, id)) +} + +// IDEQ applies the EQ predicate on the ID field. +func IDEQ(id uuid.UUID) predicate.ServiceCategory { + return predicate.ServiceCategory(sql.FieldEQ(FieldID, id)) +} + +// IDNEQ applies the NEQ predicate on the ID field. +func IDNEQ(id uuid.UUID) predicate.ServiceCategory { + return predicate.ServiceCategory(sql.FieldNEQ(FieldID, id)) +} + +// IDIn applies the In predicate on the ID field. +func IDIn(ids ...uuid.UUID) predicate.ServiceCategory { + return predicate.ServiceCategory(sql.FieldIn(FieldID, ids...)) +} + +// IDNotIn applies the NotIn predicate on the ID field. +func IDNotIn(ids ...uuid.UUID) predicate.ServiceCategory { + return predicate.ServiceCategory(sql.FieldNotIn(FieldID, ids...)) +} + +// IDGT applies the GT predicate on the ID field. +func IDGT(id uuid.UUID) predicate.ServiceCategory { + return predicate.ServiceCategory(sql.FieldGT(FieldID, id)) +} + +// IDGTE applies the GTE predicate on the ID field. +func IDGTE(id uuid.UUID) predicate.ServiceCategory { + return predicate.ServiceCategory(sql.FieldGTE(FieldID, id)) +} + +// IDLT applies the LT predicate on the ID field. +func IDLT(id uuid.UUID) predicate.ServiceCategory { + return predicate.ServiceCategory(sql.FieldLT(FieldID, id)) +} + +// IDLTE applies the LTE predicate on the ID field. +func IDLTE(id uuid.UUID) predicate.ServiceCategory { + return predicate.ServiceCategory(sql.FieldLTE(FieldID, id)) +} + +// ParentID applies equality check predicate on the "parent_id" field. It's identical to ParentIDEQ. +func ParentID(v uuid.UUID) predicate.ServiceCategory { + return predicate.ServiceCategory(sql.FieldEQ(FieldParentID, v)) +} + +// Slug applies equality check predicate on the "slug" field. It's identical to SlugEQ. +func Slug(v string) predicate.ServiceCategory { + return predicate.ServiceCategory(sql.FieldEQ(FieldSlug, v)) +} + +// Active applies equality check predicate on the "active" field. It's identical to ActiveEQ. +func Active(v bool) predicate.ServiceCategory { + return predicate.ServiceCategory(sql.FieldEQ(FieldActive, v)) +} + +// SortOrder applies equality check predicate on the "sort_order" field. It's identical to SortOrderEQ. +func SortOrder(v int) predicate.ServiceCategory { + return predicate.ServiceCategory(sql.FieldEQ(FieldSortOrder, v)) +} + +// CreatedAt applies equality check predicate on the "created_at" field. It's identical to CreatedAtEQ. +func CreatedAt(v time.Time) predicate.ServiceCategory { + return predicate.ServiceCategory(sql.FieldEQ(FieldCreatedAt, v)) +} + +// UpdatedAt applies equality check predicate on the "updated_at" field. It's identical to UpdatedAtEQ. +func UpdatedAt(v time.Time) predicate.ServiceCategory { + return predicate.ServiceCategory(sql.FieldEQ(FieldUpdatedAt, v)) +} + +// ParentIDEQ applies the EQ predicate on the "parent_id" field. +func ParentIDEQ(v uuid.UUID) predicate.ServiceCategory { + return predicate.ServiceCategory(sql.FieldEQ(FieldParentID, v)) +} + +// ParentIDNEQ applies the NEQ predicate on the "parent_id" field. +func ParentIDNEQ(v uuid.UUID) predicate.ServiceCategory { + return predicate.ServiceCategory(sql.FieldNEQ(FieldParentID, v)) +} + +// ParentIDIn applies the In predicate on the "parent_id" field. +func ParentIDIn(vs ...uuid.UUID) predicate.ServiceCategory { + return predicate.ServiceCategory(sql.FieldIn(FieldParentID, vs...)) +} + +// ParentIDNotIn applies the NotIn predicate on the "parent_id" field. +func ParentIDNotIn(vs ...uuid.UUID) predicate.ServiceCategory { + return predicate.ServiceCategory(sql.FieldNotIn(FieldParentID, vs...)) +} + +// ParentIDIsNil applies the IsNil predicate on the "parent_id" field. +func ParentIDIsNil() predicate.ServiceCategory { + return predicate.ServiceCategory(sql.FieldIsNull(FieldParentID)) +} + +// ParentIDNotNil applies the NotNil predicate on the "parent_id" field. +func ParentIDNotNil() predicate.ServiceCategory { + return predicate.ServiceCategory(sql.FieldNotNull(FieldParentID)) +} + +// SlugEQ applies the EQ predicate on the "slug" field. +func SlugEQ(v string) predicate.ServiceCategory { + return predicate.ServiceCategory(sql.FieldEQ(FieldSlug, v)) +} + +// SlugNEQ applies the NEQ predicate on the "slug" field. +func SlugNEQ(v string) predicate.ServiceCategory { + return predicate.ServiceCategory(sql.FieldNEQ(FieldSlug, v)) +} + +// SlugIn applies the In predicate on the "slug" field. +func SlugIn(vs ...string) predicate.ServiceCategory { + return predicate.ServiceCategory(sql.FieldIn(FieldSlug, vs...)) +} + +// SlugNotIn applies the NotIn predicate on the "slug" field. +func SlugNotIn(vs ...string) predicate.ServiceCategory { + return predicate.ServiceCategory(sql.FieldNotIn(FieldSlug, vs...)) +} + +// SlugGT applies the GT predicate on the "slug" field. +func SlugGT(v string) predicate.ServiceCategory { + return predicate.ServiceCategory(sql.FieldGT(FieldSlug, v)) +} + +// SlugGTE applies the GTE predicate on the "slug" field. +func SlugGTE(v string) predicate.ServiceCategory { + return predicate.ServiceCategory(sql.FieldGTE(FieldSlug, v)) +} + +// SlugLT applies the LT predicate on the "slug" field. +func SlugLT(v string) predicate.ServiceCategory { + return predicate.ServiceCategory(sql.FieldLT(FieldSlug, v)) +} + +// SlugLTE applies the LTE predicate on the "slug" field. +func SlugLTE(v string) predicate.ServiceCategory { + return predicate.ServiceCategory(sql.FieldLTE(FieldSlug, v)) +} + +// SlugContains applies the Contains predicate on the "slug" field. +func SlugContains(v string) predicate.ServiceCategory { + return predicate.ServiceCategory(sql.FieldContains(FieldSlug, v)) +} + +// SlugHasPrefix applies the HasPrefix predicate on the "slug" field. +func SlugHasPrefix(v string) predicate.ServiceCategory { + return predicate.ServiceCategory(sql.FieldHasPrefix(FieldSlug, v)) +} + +// SlugHasSuffix applies the HasSuffix predicate on the "slug" field. +func SlugHasSuffix(v string) predicate.ServiceCategory { + return predicate.ServiceCategory(sql.FieldHasSuffix(FieldSlug, v)) +} + +// SlugEqualFold applies the EqualFold predicate on the "slug" field. +func SlugEqualFold(v string) predicate.ServiceCategory { + return predicate.ServiceCategory(sql.FieldEqualFold(FieldSlug, v)) +} + +// SlugContainsFold applies the ContainsFold predicate on the "slug" field. +func SlugContainsFold(v string) predicate.ServiceCategory { + return predicate.ServiceCategory(sql.FieldContainsFold(FieldSlug, v)) +} + +// ActiveEQ applies the EQ predicate on the "active" field. +func ActiveEQ(v bool) predicate.ServiceCategory { + return predicate.ServiceCategory(sql.FieldEQ(FieldActive, v)) +} + +// ActiveNEQ applies the NEQ predicate on the "active" field. +func ActiveNEQ(v bool) predicate.ServiceCategory { + return predicate.ServiceCategory(sql.FieldNEQ(FieldActive, v)) +} + +// SortOrderEQ applies the EQ predicate on the "sort_order" field. +func SortOrderEQ(v int) predicate.ServiceCategory { + return predicate.ServiceCategory(sql.FieldEQ(FieldSortOrder, v)) +} + +// SortOrderNEQ applies the NEQ predicate on the "sort_order" field. +func SortOrderNEQ(v int) predicate.ServiceCategory { + return predicate.ServiceCategory(sql.FieldNEQ(FieldSortOrder, v)) +} + +// SortOrderIn applies the In predicate on the "sort_order" field. +func SortOrderIn(vs ...int) predicate.ServiceCategory { + return predicate.ServiceCategory(sql.FieldIn(FieldSortOrder, vs...)) +} + +// SortOrderNotIn applies the NotIn predicate on the "sort_order" field. +func SortOrderNotIn(vs ...int) predicate.ServiceCategory { + return predicate.ServiceCategory(sql.FieldNotIn(FieldSortOrder, vs...)) +} + +// SortOrderGT applies the GT predicate on the "sort_order" field. +func SortOrderGT(v int) predicate.ServiceCategory { + return predicate.ServiceCategory(sql.FieldGT(FieldSortOrder, v)) +} + +// SortOrderGTE applies the GTE predicate on the "sort_order" field. +func SortOrderGTE(v int) predicate.ServiceCategory { + return predicate.ServiceCategory(sql.FieldGTE(FieldSortOrder, v)) +} + +// SortOrderLT applies the LT predicate on the "sort_order" field. +func SortOrderLT(v int) predicate.ServiceCategory { + return predicate.ServiceCategory(sql.FieldLT(FieldSortOrder, v)) +} + +// SortOrderLTE applies the LTE predicate on the "sort_order" field. +func SortOrderLTE(v int) predicate.ServiceCategory { + return predicate.ServiceCategory(sql.FieldLTE(FieldSortOrder, v)) +} + +// CreatedAtEQ applies the EQ predicate on the "created_at" field. +func CreatedAtEQ(v time.Time) predicate.ServiceCategory { + return predicate.ServiceCategory(sql.FieldEQ(FieldCreatedAt, v)) +} + +// CreatedAtNEQ applies the NEQ predicate on the "created_at" field. +func CreatedAtNEQ(v time.Time) predicate.ServiceCategory { + return predicate.ServiceCategory(sql.FieldNEQ(FieldCreatedAt, v)) +} + +// CreatedAtIn applies the In predicate on the "created_at" field. +func CreatedAtIn(vs ...time.Time) predicate.ServiceCategory { + return predicate.ServiceCategory(sql.FieldIn(FieldCreatedAt, vs...)) +} + +// CreatedAtNotIn applies the NotIn predicate on the "created_at" field. +func CreatedAtNotIn(vs ...time.Time) predicate.ServiceCategory { + return predicate.ServiceCategory(sql.FieldNotIn(FieldCreatedAt, vs...)) +} + +// CreatedAtGT applies the GT predicate on the "created_at" field. +func CreatedAtGT(v time.Time) predicate.ServiceCategory { + return predicate.ServiceCategory(sql.FieldGT(FieldCreatedAt, v)) +} + +// CreatedAtGTE applies the GTE predicate on the "created_at" field. +func CreatedAtGTE(v time.Time) predicate.ServiceCategory { + return predicate.ServiceCategory(sql.FieldGTE(FieldCreatedAt, v)) +} + +// CreatedAtLT applies the LT predicate on the "created_at" field. +func CreatedAtLT(v time.Time) predicate.ServiceCategory { + return predicate.ServiceCategory(sql.FieldLT(FieldCreatedAt, v)) +} + +// CreatedAtLTE applies the LTE predicate on the "created_at" field. +func CreatedAtLTE(v time.Time) predicate.ServiceCategory { + return predicate.ServiceCategory(sql.FieldLTE(FieldCreatedAt, v)) +} + +// UpdatedAtEQ applies the EQ predicate on the "updated_at" field. +func UpdatedAtEQ(v time.Time) predicate.ServiceCategory { + return predicate.ServiceCategory(sql.FieldEQ(FieldUpdatedAt, v)) +} + +// UpdatedAtNEQ applies the NEQ predicate on the "updated_at" field. +func UpdatedAtNEQ(v time.Time) predicate.ServiceCategory { + return predicate.ServiceCategory(sql.FieldNEQ(FieldUpdatedAt, v)) +} + +// UpdatedAtIn applies the In predicate on the "updated_at" field. +func UpdatedAtIn(vs ...time.Time) predicate.ServiceCategory { + return predicate.ServiceCategory(sql.FieldIn(FieldUpdatedAt, vs...)) +} + +// UpdatedAtNotIn applies the NotIn predicate on the "updated_at" field. +func UpdatedAtNotIn(vs ...time.Time) predicate.ServiceCategory { + return predicate.ServiceCategory(sql.FieldNotIn(FieldUpdatedAt, vs...)) +} + +// UpdatedAtGT applies the GT predicate on the "updated_at" field. +func UpdatedAtGT(v time.Time) predicate.ServiceCategory { + return predicate.ServiceCategory(sql.FieldGT(FieldUpdatedAt, v)) +} + +// UpdatedAtGTE applies the GTE predicate on the "updated_at" field. +func UpdatedAtGTE(v time.Time) predicate.ServiceCategory { + return predicate.ServiceCategory(sql.FieldGTE(FieldUpdatedAt, v)) +} + +// UpdatedAtLT applies the LT predicate on the "updated_at" field. +func UpdatedAtLT(v time.Time) predicate.ServiceCategory { + return predicate.ServiceCategory(sql.FieldLT(FieldUpdatedAt, v)) +} + +// UpdatedAtLTE applies the LTE predicate on the "updated_at" field. +func UpdatedAtLTE(v time.Time) predicate.ServiceCategory { + return predicate.ServiceCategory(sql.FieldLTE(FieldUpdatedAt, v)) +} + +// HasChildren applies the HasEdge predicate on the "children" edge. +func HasChildren() predicate.ServiceCategory { + return predicate.ServiceCategory(func(s *sql.Selector) { + step := sqlgraph.NewStep( + sqlgraph.From(Table, FieldID), + sqlgraph.Edge(sqlgraph.O2M, false, ChildrenTable, ChildrenColumn), + ) + sqlgraph.HasNeighbors(s, step) + }) +} + +// HasChildrenWith applies the HasEdge predicate on the "children" edge with a given conditions (other predicates). +func HasChildrenWith(preds ...predicate.ServiceCategory) predicate.ServiceCategory { + return predicate.ServiceCategory(func(s *sql.Selector) { + step := newChildrenStep() + sqlgraph.HasNeighborsWith(s, step, func(s *sql.Selector) { + for _, p := range preds { + p(s) + } + }) + }) +} + +// HasParent applies the HasEdge predicate on the "parent" edge. +func HasParent() predicate.ServiceCategory { + return predicate.ServiceCategory(func(s *sql.Selector) { + step := sqlgraph.NewStep( + sqlgraph.From(Table, FieldID), + sqlgraph.Edge(sqlgraph.M2O, true, ParentTable, ParentColumn), + ) + sqlgraph.HasNeighbors(s, step) + }) +} + +// HasParentWith applies the HasEdge predicate on the "parent" edge with a given conditions (other predicates). +func HasParentWith(preds ...predicate.ServiceCategory) predicate.ServiceCategory { + return predicate.ServiceCategory(func(s *sql.Selector) { + step := newParentStep() + sqlgraph.HasNeighborsWith(s, step, func(s *sql.Selector) { + for _, p := range preds { + p(s) + } + }) + }) +} + +// HasLocalizedIn applies the HasEdge predicate on the "localized_in" edge. +func HasLocalizedIn() predicate.ServiceCategory { + return predicate.ServiceCategory(func(s *sql.Selector) { + step := sqlgraph.NewStep( + sqlgraph.From(Table, FieldID), + sqlgraph.Edge(sqlgraph.M2M, false, LocalizedInTable, LocalizedInPrimaryKey...), + ) + sqlgraph.HasNeighbors(s, step) + }) +} + +// HasLocalizedInWith applies the HasEdge predicate on the "localized_in" edge with a given conditions (other predicates). +func HasLocalizedInWith(preds ...predicate.SupportedLocale) predicate.ServiceCategory { + return predicate.ServiceCategory(func(s *sql.Selector) { + step := newLocalizedInStep() + sqlgraph.HasNeighborsWith(s, step, func(s *sql.Selector) { + for _, p := range preds { + p(s) + } + }) + }) +} + +// HasTranslations applies the HasEdge predicate on the "translations" edge. +func HasTranslations() predicate.ServiceCategory { + return predicate.ServiceCategory(func(s *sql.Selector) { + step := sqlgraph.NewStep( + sqlgraph.From(Table, FieldID), + sqlgraph.Edge(sqlgraph.O2M, true, TranslationsTable, TranslationsColumn), + ) + sqlgraph.HasNeighbors(s, step) + }) +} + +// HasTranslationsWith applies the HasEdge predicate on the "translations" edge with a given conditions (other predicates). +func HasTranslationsWith(preds ...predicate.ServiceCategoryTranslation) predicate.ServiceCategory { + return predicate.ServiceCategory(func(s *sql.Selector) { + step := newTranslationsStep() + 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.ServiceCategory) predicate.ServiceCategory { + return predicate.ServiceCategory(sql.AndPredicates(predicates...)) +} + +// Or groups predicates with the OR operator between them. +func Or(predicates ...predicate.ServiceCategory) predicate.ServiceCategory { + return predicate.ServiceCategory(sql.OrPredicates(predicates...)) +} + +// Not applies the not operator on the given predicate. +func Not(p predicate.ServiceCategory) predicate.ServiceCategory { + return predicate.ServiceCategory(sql.NotPredicates(p)) +} diff --git a/backend/ent/servicecategory_create.go b/backend/ent/servicecategory_create.go new file mode 100644 index 0000000..fc9e49b --- /dev/null +++ b/backend/ent/servicecategory_create.go @@ -0,0 +1,411 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "context" + "errors" + "fmt" + "time" + + "entgo.io/ent/dialect/sql/sqlgraph" + "entgo.io/ent/schema/field" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/servicecategory" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/supportedlocale" + "github.com/google/uuid" +) + +// ServiceCategoryCreate is the builder for creating a ServiceCategory entity. +type ServiceCategoryCreate struct { + config + mutation *ServiceCategoryMutation + hooks []Hook +} + +// SetParentID sets the "parent_id" field. +func (_c *ServiceCategoryCreate) SetParentID(v uuid.UUID) *ServiceCategoryCreate { + _c.mutation.SetParentID(v) + return _c +} + +// SetNillableParentID sets the "parent_id" field if the given value is not nil. +func (_c *ServiceCategoryCreate) SetNillableParentID(v *uuid.UUID) *ServiceCategoryCreate { + if v != nil { + _c.SetParentID(*v) + } + return _c +} + +// SetSlug sets the "slug" field. +func (_c *ServiceCategoryCreate) SetSlug(v string) *ServiceCategoryCreate { + _c.mutation.SetSlug(v) + return _c +} + +// SetActive sets the "active" field. +func (_c *ServiceCategoryCreate) SetActive(v bool) *ServiceCategoryCreate { + _c.mutation.SetActive(v) + return _c +} + +// SetNillableActive sets the "active" field if the given value is not nil. +func (_c *ServiceCategoryCreate) SetNillableActive(v *bool) *ServiceCategoryCreate { + if v != nil { + _c.SetActive(*v) + } + return _c +} + +// SetSortOrder sets the "sort_order" field. +func (_c *ServiceCategoryCreate) SetSortOrder(v int) *ServiceCategoryCreate { + _c.mutation.SetSortOrder(v) + return _c +} + +// SetCreatedAt sets the "created_at" field. +func (_c *ServiceCategoryCreate) SetCreatedAt(v time.Time) *ServiceCategoryCreate { + _c.mutation.SetCreatedAt(v) + return _c +} + +// SetNillableCreatedAt sets the "created_at" field if the given value is not nil. +func (_c *ServiceCategoryCreate) SetNillableCreatedAt(v *time.Time) *ServiceCategoryCreate { + if v != nil { + _c.SetCreatedAt(*v) + } + return _c +} + +// SetUpdatedAt sets the "updated_at" field. +func (_c *ServiceCategoryCreate) SetUpdatedAt(v time.Time) *ServiceCategoryCreate { + _c.mutation.SetUpdatedAt(v) + return _c +} + +// SetNillableUpdatedAt sets the "updated_at" field if the given value is not nil. +func (_c *ServiceCategoryCreate) SetNillableUpdatedAt(v *time.Time) *ServiceCategoryCreate { + if v != nil { + _c.SetUpdatedAt(*v) + } + return _c +} + +// SetID sets the "id" field. +func (_c *ServiceCategoryCreate) SetID(v uuid.UUID) *ServiceCategoryCreate { + _c.mutation.SetID(v) + return _c +} + +// SetNillableID sets the "id" field if the given value is not nil. +func (_c *ServiceCategoryCreate) SetNillableID(v *uuid.UUID) *ServiceCategoryCreate { + if v != nil { + _c.SetID(*v) + } + return _c +} + +// AddChildIDs adds the "children" edge to the ServiceCategory entity by IDs. +func (_c *ServiceCategoryCreate) AddChildIDs(ids ...uuid.UUID) *ServiceCategoryCreate { + _c.mutation.AddChildIDs(ids...) + return _c +} + +// AddChildren adds the "children" edges to the ServiceCategory entity. +func (_c *ServiceCategoryCreate) AddChildren(v ...*ServiceCategory) *ServiceCategoryCreate { + ids := make([]uuid.UUID, len(v)) + for i := range v { + ids[i] = v[i].ID + } + return _c.AddChildIDs(ids...) +} + +// SetParent sets the "parent" edge to the ServiceCategory entity. +func (_c *ServiceCategoryCreate) SetParent(v *ServiceCategory) *ServiceCategoryCreate { + return _c.SetParentID(v.ID) +} + +// AddLocalizedInIDs adds the "localized_in" edge to the SupportedLocale entity by IDs. +func (_c *ServiceCategoryCreate) AddLocalizedInIDs(ids ...string) *ServiceCategoryCreate { + _c.mutation.AddLocalizedInIDs(ids...) + return _c +} + +// AddLocalizedIn adds the "localized_in" edges to the SupportedLocale entity. +func (_c *ServiceCategoryCreate) AddLocalizedIn(v ...*SupportedLocale) *ServiceCategoryCreate { + ids := make([]string, len(v)) + for i := range v { + ids[i] = v[i].ID + } + return _c.AddLocalizedInIDs(ids...) +} + +// Mutation returns the ServiceCategoryMutation object of the builder. +func (_c *ServiceCategoryCreate) Mutation() *ServiceCategoryMutation { + return _c.mutation +} + +// Save creates the ServiceCategory in the database. +func (_c *ServiceCategoryCreate) Save(ctx context.Context) (*ServiceCategory, error) { + _c.defaults() + return withHooks(ctx, _c.sqlSave, _c.mutation, _c.hooks) +} + +// SaveX calls Save and panics if Save returns an error. +func (_c *ServiceCategoryCreate) SaveX(ctx context.Context) *ServiceCategory { + v, err := _c.Save(ctx) + if err != nil { + panic(err) + } + return v +} + +// Exec executes the query. +func (_c *ServiceCategoryCreate) Exec(ctx context.Context) error { + _, err := _c.Save(ctx) + return err +} + +// ExecX is like Exec, but panics if an error occurs. +func (_c *ServiceCategoryCreate) 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 *ServiceCategoryCreate) defaults() { + if _, ok := _c.mutation.Active(); !ok { + v := servicecategory.DefaultActive + _c.mutation.SetActive(v) + } + if _, ok := _c.mutation.CreatedAt(); !ok { + v := servicecategory.DefaultCreatedAt() + _c.mutation.SetCreatedAt(v) + } + if _, ok := _c.mutation.UpdatedAt(); !ok { + v := servicecategory.DefaultUpdatedAt() + _c.mutation.SetUpdatedAt(v) + } + if _, ok := _c.mutation.ID(); !ok { + v := servicecategory.DefaultID() + _c.mutation.SetID(v) + } +} + +// check runs all checks and user-defined validators on the builder. +func (_c *ServiceCategoryCreate) check() error { + if _, ok := _c.mutation.Slug(); !ok { + return &ValidationError{Name: "slug", err: errors.New(`ent: missing required field "ServiceCategory.slug"`)} + } + if v, ok := _c.mutation.Slug(); ok { + if err := servicecategory.SlugValidator(v); err != nil { + return &ValidationError{Name: "slug", err: fmt.Errorf(`ent: validator failed for field "ServiceCategory.slug": %w`, err)} + } + } + if _, ok := _c.mutation.Active(); !ok { + return &ValidationError{Name: "active", err: errors.New(`ent: missing required field "ServiceCategory.active"`)} + } + if _, ok := _c.mutation.SortOrder(); !ok { + return &ValidationError{Name: "sort_order", err: errors.New(`ent: missing required field "ServiceCategory.sort_order"`)} + } + if v, ok := _c.mutation.SortOrder(); ok { + if err := servicecategory.SortOrderValidator(v); err != nil { + return &ValidationError{Name: "sort_order", err: fmt.Errorf(`ent: validator failed for field "ServiceCategory.sort_order": %w`, err)} + } + } + if _, ok := _c.mutation.CreatedAt(); !ok { + return &ValidationError{Name: "created_at", err: errors.New(`ent: missing required field "ServiceCategory.created_at"`)} + } + if _, ok := _c.mutation.UpdatedAt(); !ok { + return &ValidationError{Name: "updated_at", err: errors.New(`ent: missing required field "ServiceCategory.updated_at"`)} + } + return nil +} + +func (_c *ServiceCategoryCreate) sqlSave(ctx context.Context) (*ServiceCategory, 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 *ServiceCategoryCreate) createSpec() (*ServiceCategory, *sqlgraph.CreateSpec) { + var ( + _node = &ServiceCategory{config: _c.config} + _spec = sqlgraph.NewCreateSpec(servicecategory.Table, sqlgraph.NewFieldSpec(servicecategory.FieldID, field.TypeUUID)) + ) + if id, ok := _c.mutation.ID(); ok { + _node.ID = id + _spec.ID.Value = &id + } + if value, ok := _c.mutation.Slug(); ok { + _spec.SetField(servicecategory.FieldSlug, field.TypeString, value) + _node.Slug = value + } + if value, ok := _c.mutation.Active(); ok { + _spec.SetField(servicecategory.FieldActive, field.TypeBool, value) + _node.Active = value + } + if value, ok := _c.mutation.SortOrder(); ok { + _spec.SetField(servicecategory.FieldSortOrder, field.TypeInt, value) + _node.SortOrder = value + } + if value, ok := _c.mutation.CreatedAt(); ok { + _spec.SetField(servicecategory.FieldCreatedAt, field.TypeTime, value) + _node.CreatedAt = value + } + if value, ok := _c.mutation.UpdatedAt(); ok { + _spec.SetField(servicecategory.FieldUpdatedAt, field.TypeTime, value) + _node.UpdatedAt = value + } + if nodes := _c.mutation.ChildrenIDs(); len(nodes) > 0 { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.O2M, + Inverse: false, + Table: servicecategory.ChildrenTable, + Columns: []string{servicecategory.ChildrenColumn}, + Bidi: true, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(servicecategory.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.ParentIDs(); len(nodes) > 0 { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.M2O, + Inverse: true, + Table: servicecategory.ParentTable, + Columns: []string{servicecategory.ParentColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(servicecategory.FieldID, field.TypeUUID), + }, + } + for _, k := range nodes { + edge.Target.Nodes = append(edge.Target.Nodes, k) + } + _node.ParentID = &nodes[0] + _spec.Edges = append(_spec.Edges, edge) + } + if nodes := _c.mutation.LocalizedInIDs(); len(nodes) > 0 { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.M2M, + Inverse: false, + Table: servicecategory.LocalizedInTable, + Columns: servicecategory.LocalizedInPrimaryKey, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(supportedlocale.FieldID, field.TypeString), + }, + } + for _, k := range nodes { + edge.Target.Nodes = append(edge.Target.Nodes, k) + } + _spec.Edges = append(_spec.Edges, edge) + } + return _node, _spec +} + +// ServiceCategoryCreateBulk is the builder for creating many ServiceCategory entities in bulk. +type ServiceCategoryCreateBulk struct { + config + err error + builders []*ServiceCategoryCreate +} + +// Save creates the ServiceCategory entities in the database. +func (_c *ServiceCategoryCreateBulk) Save(ctx context.Context) ([]*ServiceCategory, error) { + if _c.err != nil { + return nil, _c.err + } + specs := make([]*sqlgraph.CreateSpec, len(_c.builders)) + nodes := make([]*ServiceCategory, 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.(*ServiceCategoryMutation) + 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 *ServiceCategoryCreateBulk) SaveX(ctx context.Context) []*ServiceCategory { + v, err := _c.Save(ctx) + if err != nil { + panic(err) + } + return v +} + +// Exec executes the query. +func (_c *ServiceCategoryCreateBulk) Exec(ctx context.Context) error { + _, err := _c.Save(ctx) + return err +} + +// ExecX is like Exec, but panics if an error occurs. +func (_c *ServiceCategoryCreateBulk) ExecX(ctx context.Context) { + if err := _c.Exec(ctx); err != nil { + panic(err) + } +} diff --git a/backend/ent/servicecategory_delete.go b/backend/ent/servicecategory_delete.go new file mode 100644 index 0000000..c573593 --- /dev/null +++ b/backend/ent/servicecategory_delete.go @@ -0,0 +1,88 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "context" + + "entgo.io/ent/dialect/sql" + "entgo.io/ent/dialect/sql/sqlgraph" + "entgo.io/ent/schema/field" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/predicate" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/servicecategory" +) + +// ServiceCategoryDelete is the builder for deleting a ServiceCategory entity. +type ServiceCategoryDelete struct { + config + hooks []Hook + mutation *ServiceCategoryMutation +} + +// Where appends a list predicates to the ServiceCategoryDelete builder. +func (_d *ServiceCategoryDelete) Where(ps ...predicate.ServiceCategory) *ServiceCategoryDelete { + _d.mutation.Where(ps...) + return _d +} + +// Exec executes the deletion query and returns how many vertices were deleted. +func (_d *ServiceCategoryDelete) 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 *ServiceCategoryDelete) ExecX(ctx context.Context) int { + n, err := _d.Exec(ctx) + if err != nil { + panic(err) + } + return n +} + +func (_d *ServiceCategoryDelete) sqlExec(ctx context.Context) (int, error) { + _spec := sqlgraph.NewDeleteSpec(servicecategory.Table, sqlgraph.NewFieldSpec(servicecategory.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 +} + +// ServiceCategoryDeleteOne is the builder for deleting a single ServiceCategory entity. +type ServiceCategoryDeleteOne struct { + _d *ServiceCategoryDelete +} + +// Where appends a list predicates to the ServiceCategoryDelete builder. +func (_d *ServiceCategoryDeleteOne) Where(ps ...predicate.ServiceCategory) *ServiceCategoryDeleteOne { + _d._d.mutation.Where(ps...) + return _d +} + +// Exec executes the deletion query. +func (_d *ServiceCategoryDeleteOne) Exec(ctx context.Context) error { + n, err := _d._d.Exec(ctx) + switch { + case err != nil: + return err + case n == 0: + return &NotFoundError{servicecategory.Label} + default: + return nil + } +} + +// ExecX is like Exec, but panics if an error occurs. +func (_d *ServiceCategoryDeleteOne) ExecX(ctx context.Context) { + if err := _d.Exec(ctx); err != nil { + panic(err) + } +} diff --git a/backend/ent/servicecategory_query.go b/backend/ent/servicecategory_query.go new file mode 100644 index 0000000..9a33778 --- /dev/null +++ b/backend/ent/servicecategory_query.go @@ -0,0 +1,867 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "context" + "database/sql/driver" + "fmt" + "math" + + "entgo.io/ent" + "entgo.io/ent/dialect/sql" + "entgo.io/ent/dialect/sql/sqlgraph" + "entgo.io/ent/schema/field" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/predicate" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/servicecategory" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/servicecategorytranslation" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/supportedlocale" + "github.com/google/uuid" +) + +// ServiceCategoryQuery is the builder for querying ServiceCategory entities. +type ServiceCategoryQuery struct { + config + ctx *QueryContext + order []servicecategory.OrderOption + inters []Interceptor + predicates []predicate.ServiceCategory + withChildren *ServiceCategoryQuery + withParent *ServiceCategoryQuery + withLocalizedIn *SupportedLocaleQuery + withTranslations *ServiceCategoryTranslationQuery + // intermediate query (i.e. traversal path). + sql *sql.Selector + path func(context.Context) (*sql.Selector, error) +} + +// Where adds a new predicate for the ServiceCategoryQuery builder. +func (_q *ServiceCategoryQuery) Where(ps ...predicate.ServiceCategory) *ServiceCategoryQuery { + _q.predicates = append(_q.predicates, ps...) + return _q +} + +// Limit the number of records to be returned by this query. +func (_q *ServiceCategoryQuery) Limit(limit int) *ServiceCategoryQuery { + _q.ctx.Limit = &limit + return _q +} + +// Offset to start from. +func (_q *ServiceCategoryQuery) Offset(offset int) *ServiceCategoryQuery { + _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 *ServiceCategoryQuery) Unique(unique bool) *ServiceCategoryQuery { + _q.ctx.Unique = &unique + return _q +} + +// Order specifies how the records should be ordered. +func (_q *ServiceCategoryQuery) Order(o ...servicecategory.OrderOption) *ServiceCategoryQuery { + _q.order = append(_q.order, o...) + return _q +} + +// QueryChildren chains the current query on the "children" edge. +func (_q *ServiceCategoryQuery) QueryChildren() *ServiceCategoryQuery { + query := (&ServiceCategoryClient{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(servicecategory.Table, servicecategory.FieldID, selector), + sqlgraph.To(servicecategory.Table, servicecategory.FieldID), + sqlgraph.Edge(sqlgraph.O2M, false, servicecategory.ChildrenTable, servicecategory.ChildrenColumn), + ) + fromU = sqlgraph.SetNeighbors(_q.driver.Dialect(), step) + return fromU, nil + } + return query +} + +// QueryParent chains the current query on the "parent" edge. +func (_q *ServiceCategoryQuery) QueryParent() *ServiceCategoryQuery { + query := (&ServiceCategoryClient{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(servicecategory.Table, servicecategory.FieldID, selector), + sqlgraph.To(servicecategory.Table, servicecategory.FieldID), + sqlgraph.Edge(sqlgraph.M2O, true, servicecategory.ParentTable, servicecategory.ParentColumn), + ) + fromU = sqlgraph.SetNeighbors(_q.driver.Dialect(), step) + return fromU, nil + } + return query +} + +// QueryLocalizedIn chains the current query on the "localized_in" edge. +func (_q *ServiceCategoryQuery) QueryLocalizedIn() *SupportedLocaleQuery { + query := (&SupportedLocaleClient{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(servicecategory.Table, servicecategory.FieldID, selector), + sqlgraph.To(supportedlocale.Table, supportedlocale.FieldID), + sqlgraph.Edge(sqlgraph.M2M, false, servicecategory.LocalizedInTable, servicecategory.LocalizedInPrimaryKey...), + ) + fromU = sqlgraph.SetNeighbors(_q.driver.Dialect(), step) + return fromU, nil + } + return query +} + +// QueryTranslations chains the current query on the "translations" edge. +func (_q *ServiceCategoryQuery) QueryTranslations() *ServiceCategoryTranslationQuery { + query := (&ServiceCategoryTranslationClient{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(servicecategory.Table, servicecategory.FieldID, selector), + sqlgraph.To(servicecategorytranslation.Table, servicecategorytranslation.CategoryColumn), + sqlgraph.Edge(sqlgraph.O2M, true, servicecategory.TranslationsTable, servicecategory.TranslationsColumn), + ) + fromU = sqlgraph.SetNeighbors(_q.driver.Dialect(), step) + return fromU, nil + } + return query +} + +// First returns the first ServiceCategory entity from the query. +// Returns a *NotFoundError when no ServiceCategory was found. +func (_q *ServiceCategoryQuery) First(ctx context.Context) (*ServiceCategory, 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{servicecategory.Label} + } + return nodes[0], nil +} + +// FirstX is like First, but panics if an error occurs. +func (_q *ServiceCategoryQuery) FirstX(ctx context.Context) *ServiceCategory { + node, err := _q.First(ctx) + if err != nil && !IsNotFound(err) { + panic(err) + } + return node +} + +// FirstID returns the first ServiceCategory ID from the query. +// Returns a *NotFoundError when no ServiceCategory ID was found. +func (_q *ServiceCategoryQuery) 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{servicecategory.Label} + return + } + return ids[0], nil +} + +// FirstIDX is like FirstID, but panics if an error occurs. +func (_q *ServiceCategoryQuery) FirstIDX(ctx context.Context) uuid.UUID { + id, err := _q.FirstID(ctx) + if err != nil && !IsNotFound(err) { + panic(err) + } + return id +} + +// Only returns a single ServiceCategory entity found by the query, ensuring it only returns one. +// Returns a *NotSingularError when more than one ServiceCategory entity is found. +// Returns a *NotFoundError when no ServiceCategory entities are found. +func (_q *ServiceCategoryQuery) Only(ctx context.Context) (*ServiceCategory, 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{servicecategory.Label} + default: + return nil, &NotSingularError{servicecategory.Label} + } +} + +// OnlyX is like Only, but panics if an error occurs. +func (_q *ServiceCategoryQuery) OnlyX(ctx context.Context) *ServiceCategory { + node, err := _q.Only(ctx) + if err != nil { + panic(err) + } + return node +} + +// OnlyID is like Only, but returns the only ServiceCategory ID in the query. +// Returns a *NotSingularError when more than one ServiceCategory ID is found. +// Returns a *NotFoundError when no entities are found. +func (_q *ServiceCategoryQuery) 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{servicecategory.Label} + default: + err = &NotSingularError{servicecategory.Label} + } + return +} + +// OnlyIDX is like OnlyID, but panics if an error occurs. +func (_q *ServiceCategoryQuery) 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 ServiceCategories. +func (_q *ServiceCategoryQuery) All(ctx context.Context) ([]*ServiceCategory, error) { + ctx = setContextOp(ctx, _q.ctx, ent.OpQueryAll) + if err := _q.prepareQuery(ctx); err != nil { + return nil, err + } + qr := querierAll[[]*ServiceCategory, *ServiceCategoryQuery]() + return withInterceptors[[]*ServiceCategory](ctx, _q, qr, _q.inters) +} + +// AllX is like All, but panics if an error occurs. +func (_q *ServiceCategoryQuery) AllX(ctx context.Context) []*ServiceCategory { + nodes, err := _q.All(ctx) + if err != nil { + panic(err) + } + return nodes +} + +// IDs executes the query and returns a list of ServiceCategory IDs. +func (_q *ServiceCategoryQuery) 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(servicecategory.FieldID).Scan(ctx, &ids); err != nil { + return nil, err + } + return ids, nil +} + +// IDsX is like IDs, but panics if an error occurs. +func (_q *ServiceCategoryQuery) 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 *ServiceCategoryQuery) 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[*ServiceCategoryQuery](), _q.inters) +} + +// CountX is like Count, but panics if an error occurs. +func (_q *ServiceCategoryQuery) 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 *ServiceCategoryQuery) 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 *ServiceCategoryQuery) ExistX(ctx context.Context) bool { + exist, err := _q.Exist(ctx) + if err != nil { + panic(err) + } + return exist +} + +// Clone returns a duplicate of the ServiceCategoryQuery 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 *ServiceCategoryQuery) Clone() *ServiceCategoryQuery { + if _q == nil { + return nil + } + return &ServiceCategoryQuery{ + config: _q.config, + ctx: _q.ctx.Clone(), + order: append([]servicecategory.OrderOption{}, _q.order...), + inters: append([]Interceptor{}, _q.inters...), + predicates: append([]predicate.ServiceCategory{}, _q.predicates...), + withChildren: _q.withChildren.Clone(), + withParent: _q.withParent.Clone(), + withLocalizedIn: _q.withLocalizedIn.Clone(), + withTranslations: _q.withTranslations.Clone(), + // clone intermediate query. + sql: _q.sql.Clone(), + path: _q.path, + } +} + +// WithChildren tells the query-builder to eager-load the nodes that are connected to +// the "children" edge. The optional arguments are used to configure the query builder of the edge. +func (_q *ServiceCategoryQuery) WithChildren(opts ...func(*ServiceCategoryQuery)) *ServiceCategoryQuery { + query := (&ServiceCategoryClient{config: _q.config}).Query() + for _, opt := range opts { + opt(query) + } + _q.withChildren = query + return _q +} + +// WithParent tells the query-builder to eager-load the nodes that are connected to +// the "parent" edge. The optional arguments are used to configure the query builder of the edge. +func (_q *ServiceCategoryQuery) WithParent(opts ...func(*ServiceCategoryQuery)) *ServiceCategoryQuery { + query := (&ServiceCategoryClient{config: _q.config}).Query() + for _, opt := range opts { + opt(query) + } + _q.withParent = query + return _q +} + +// WithLocalizedIn tells the query-builder to eager-load the nodes that are connected to +// the "localized_in" edge. The optional arguments are used to configure the query builder of the edge. +func (_q *ServiceCategoryQuery) WithLocalizedIn(opts ...func(*SupportedLocaleQuery)) *ServiceCategoryQuery { + query := (&SupportedLocaleClient{config: _q.config}).Query() + for _, opt := range opts { + opt(query) + } + _q.withLocalizedIn = query + return _q +} + +// WithTranslations tells the query-builder to eager-load the nodes that are connected to +// the "translations" edge. The optional arguments are used to configure the query builder of the edge. +func (_q *ServiceCategoryQuery) WithTranslations(opts ...func(*ServiceCategoryTranslationQuery)) *ServiceCategoryQuery { + query := (&ServiceCategoryTranslationClient{config: _q.config}).Query() + for _, opt := range opts { + opt(query) + } + _q.withTranslations = 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 { +// ParentID uuid.UUID `json:"parent_id,omitempty"` +// Count int `json:"count,omitempty"` +// } +// +// client.ServiceCategory.Query(). +// GroupBy(servicecategory.FieldParentID). +// Aggregate(ent.Count()). +// Scan(ctx, &v) +func (_q *ServiceCategoryQuery) GroupBy(field string, fields ...string) *ServiceCategoryGroupBy { + _q.ctx.Fields = append([]string{field}, fields...) + grbuild := &ServiceCategoryGroupBy{build: _q} + grbuild.flds = &_q.ctx.Fields + grbuild.label = servicecategory.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 { +// ParentID uuid.UUID `json:"parent_id,omitempty"` +// } +// +// client.ServiceCategory.Query(). +// Select(servicecategory.FieldParentID). +// Scan(ctx, &v) +func (_q *ServiceCategoryQuery) Select(fields ...string) *ServiceCategorySelect { + _q.ctx.Fields = append(_q.ctx.Fields, fields...) + sbuild := &ServiceCategorySelect{ServiceCategoryQuery: _q} + sbuild.label = servicecategory.Label + sbuild.flds, sbuild.scan = &_q.ctx.Fields, sbuild.Scan + return sbuild +} + +// Aggregate returns a ServiceCategorySelect configured with the given aggregations. +func (_q *ServiceCategoryQuery) Aggregate(fns ...AggregateFunc) *ServiceCategorySelect { + return _q.Select().Aggregate(fns...) +} + +func (_q *ServiceCategoryQuery) 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 !servicecategory.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 *ServiceCategoryQuery) sqlAll(ctx context.Context, hooks ...queryHook) ([]*ServiceCategory, error) { + var ( + nodes = []*ServiceCategory{} + _spec = _q.querySpec() + loadedTypes = [4]bool{ + _q.withChildren != nil, + _q.withParent != nil, + _q.withLocalizedIn != nil, + _q.withTranslations != nil, + } + ) + _spec.ScanValues = func(columns []string) ([]any, error) { + return (*ServiceCategory).scanValues(nil, columns) + } + _spec.Assign = func(columns []string, values []any) error { + node := &ServiceCategory{config: _q.config} + nodes = append(nodes, node) + node.Edges.loadedTypes = loadedTypes + return node.assignValues(columns, values) + } + 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.withChildren; query != nil { + if err := _q.loadChildren(ctx, query, nodes, + func(n *ServiceCategory) { n.Edges.Children = []*ServiceCategory{} }, + func(n *ServiceCategory, e *ServiceCategory) { n.Edges.Children = append(n.Edges.Children, e) }); err != nil { + return nil, err + } + } + if query := _q.withParent; query != nil { + if err := _q.loadParent(ctx, query, nodes, nil, + func(n *ServiceCategory, e *ServiceCategory) { n.Edges.Parent = e }); err != nil { + return nil, err + } + } + if query := _q.withLocalizedIn; query != nil { + if err := _q.loadLocalizedIn(ctx, query, nodes, + func(n *ServiceCategory) { n.Edges.LocalizedIn = []*SupportedLocale{} }, + func(n *ServiceCategory, e *SupportedLocale) { n.Edges.LocalizedIn = append(n.Edges.LocalizedIn, e) }); err != nil { + return nil, err + } + } + if query := _q.withTranslations; query != nil { + if err := _q.loadTranslations(ctx, query, nodes, + func(n *ServiceCategory) { n.Edges.Translations = []*ServiceCategoryTranslation{} }, + func(n *ServiceCategory, e *ServiceCategoryTranslation) { + n.Edges.Translations = append(n.Edges.Translations, e) + }); err != nil { + return nil, err + } + } + return nodes, nil +} + +func (_q *ServiceCategoryQuery) loadChildren(ctx context.Context, query *ServiceCategoryQuery, nodes []*ServiceCategory, init func(*ServiceCategory), assign func(*ServiceCategory, *ServiceCategory)) error { + fks := make([]driver.Value, 0, len(nodes)) + nodeids := make(map[uuid.UUID]*ServiceCategory) + 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(servicecategory.FieldParentID) + } + query.Where(predicate.ServiceCategory(func(s *sql.Selector) { + s.Where(sql.InValues(s.C(servicecategory.ChildrenColumn), fks...)) + })) + neighbors, err := query.All(ctx) + if err != nil { + return err + } + for _, n := range neighbors { + fk := n.ParentID + if fk == nil { + return fmt.Errorf(`foreign-key "parent_id" is nil for node %v`, n.ID) + } + node, ok := nodeids[*fk] + if !ok { + return fmt.Errorf(`unexpected referenced foreign-key "parent_id" returned %v for node %v`, *fk, n.ID) + } + assign(node, n) + } + return nil +} +func (_q *ServiceCategoryQuery) loadParent(ctx context.Context, query *ServiceCategoryQuery, nodes []*ServiceCategory, init func(*ServiceCategory), assign func(*ServiceCategory, *ServiceCategory)) error { + ids := make([]uuid.UUID, 0, len(nodes)) + nodeids := make(map[uuid.UUID][]*ServiceCategory) + for i := range nodes { + if nodes[i].ParentID == nil { + continue + } + fk := *nodes[i].ParentID + if _, ok := nodeids[fk]; !ok { + ids = append(ids, fk) + } + nodeids[fk] = append(nodeids[fk], nodes[i]) + } + if len(ids) == 0 { + return nil + } + query.Where(servicecategory.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 "parent_id" returned %v`, n.ID) + } + for i := range nodes { + assign(nodes[i], n) + } + } + return nil +} +func (_q *ServiceCategoryQuery) loadLocalizedIn(ctx context.Context, query *SupportedLocaleQuery, nodes []*ServiceCategory, init func(*ServiceCategory), assign func(*ServiceCategory, *SupportedLocale)) error { + edgeIDs := make([]driver.Value, len(nodes)) + byID := make(map[uuid.UUID]*ServiceCategory) + nids := make(map[string]map[*ServiceCategory]struct{}) + for i, node := range nodes { + edgeIDs[i] = node.ID + byID[node.ID] = node + if init != nil { + init(node) + } + } + query.Where(func(s *sql.Selector) { + joinT := sql.Table(servicecategory.LocalizedInTable) + s.Join(joinT).On(s.C(supportedlocale.FieldID), joinT.C(servicecategory.LocalizedInPrimaryKey[1])) + s.Where(sql.InValues(joinT.C(servicecategory.LocalizedInPrimaryKey[0]), edgeIDs...)) + columns := s.SelectedColumns() + s.Select(joinT.C(servicecategory.LocalizedInPrimaryKey[0])) + s.AppendSelect(columns...) + s.SetDistinct(false) + }) + if err := query.prepareQuery(ctx); err != nil { + return err + } + qr := QuerierFunc(func(ctx context.Context, q Query) (Value, error) { + return query.sqlAll(ctx, func(_ context.Context, spec *sqlgraph.QuerySpec) { + assign := spec.Assign + values := spec.ScanValues + spec.ScanValues = func(columns []string) ([]any, error) { + values, err := values(columns[1:]) + if err != nil { + return nil, err + } + return append([]any{new(uuid.UUID)}, values...), nil + } + spec.Assign = func(columns []string, values []any) error { + outValue := *values[0].(*uuid.UUID) + inValue := values[1].(*sql.NullString).String + if nids[inValue] == nil { + nids[inValue] = map[*ServiceCategory]struct{}{byID[outValue]: {}} + return assign(columns[1:], values[1:]) + } + nids[inValue][byID[outValue]] = struct{}{} + return nil + } + }) + }) + neighbors, err := withInterceptors[[]*SupportedLocale](ctx, query, qr, query.inters) + if err != nil { + return err + } + for _, n := range neighbors { + nodes, ok := nids[n.ID] + if !ok { + return fmt.Errorf(`unexpected "localized_in" node returned %v`, n.ID) + } + for kn := range nodes { + assign(kn, n) + } + } + return nil +} +func (_q *ServiceCategoryQuery) loadTranslations(ctx context.Context, query *ServiceCategoryTranslationQuery, nodes []*ServiceCategory, init func(*ServiceCategory), assign func(*ServiceCategory, *ServiceCategoryTranslation)) error { + fks := make([]driver.Value, 0, len(nodes)) + nodeids := make(map[uuid.UUID]*ServiceCategory) + 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(servicecategorytranslation.FieldCategoryID) + } + query.Where(predicate.ServiceCategoryTranslation(func(s *sql.Selector) { + s.Where(sql.InValues(s.C(servicecategory.TranslationsColumn), fks...)) + })) + neighbors, err := query.All(ctx) + if err != nil { + return err + } + for _, n := range neighbors { + fk := n.CategoryID + node, ok := nodeids[fk] + if !ok { + return fmt.Errorf(`unexpected referenced foreign-key "category_id" returned %v for node %v`, fk, n) + } + assign(node, n) + } + return nil +} + +func (_q *ServiceCategoryQuery) sqlCount(ctx context.Context) (int, error) { + _spec := _q.querySpec() + _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 *ServiceCategoryQuery) querySpec() *sqlgraph.QuerySpec { + _spec := sqlgraph.NewQuerySpec(servicecategory.Table, servicecategory.Columns, sqlgraph.NewFieldSpec(servicecategory.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, servicecategory.FieldID) + for i := range fields { + if fields[i] != servicecategory.FieldID { + _spec.Node.Columns = append(_spec.Node.Columns, fields[i]) + } + } + if _q.withParent != nil { + _spec.Node.AddColumnOnce(servicecategory.FieldParentID) + } + } + 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 *ServiceCategoryQuery) sqlQuery(ctx context.Context) *sql.Selector { + builder := sql.Dialect(_q.driver.Dialect()) + t1 := builder.Table(servicecategory.Table) + columns := _q.ctx.Fields + if len(columns) == 0 { + columns = servicecategory.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 _, 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 +} + +// ServiceCategoryGroupBy is the group-by builder for ServiceCategory entities. +type ServiceCategoryGroupBy struct { + selector + build *ServiceCategoryQuery +} + +// Aggregate adds the given aggregation functions to the group-by query. +func (_g *ServiceCategoryGroupBy) Aggregate(fns ...AggregateFunc) *ServiceCategoryGroupBy { + _g.fns = append(_g.fns, fns...) + return _g +} + +// Scan applies the selector query and scans the result into the given value. +func (_g *ServiceCategoryGroupBy) 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[*ServiceCategoryQuery, *ServiceCategoryGroupBy](ctx, _g.build, _g, _g.build.inters, v) +} + +func (_g *ServiceCategoryGroupBy) sqlScan(ctx context.Context, root *ServiceCategoryQuery, 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) +} + +// ServiceCategorySelect is the builder for selecting fields of ServiceCategory entities. +type ServiceCategorySelect struct { + *ServiceCategoryQuery + selector +} + +// Aggregate adds the given aggregation functions to the selector query. +func (_s *ServiceCategorySelect) Aggregate(fns ...AggregateFunc) *ServiceCategorySelect { + _s.fns = append(_s.fns, fns...) + return _s +} + +// Scan applies the selector query and scans the result into the given value. +func (_s *ServiceCategorySelect) 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[*ServiceCategoryQuery, *ServiceCategorySelect](ctx, _s.ServiceCategoryQuery, _s, _s.inters, v) +} + +func (_s *ServiceCategorySelect) sqlScan(ctx context.Context, root *ServiceCategoryQuery, 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/backend/ent/servicecategory_update.go b/backend/ent/servicecategory_update.go new file mode 100644 index 0000000..a7453c5 --- /dev/null +++ b/backend/ent/servicecategory_update.go @@ -0,0 +1,772 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "context" + "errors" + "fmt" + "time" + + "entgo.io/ent/dialect/sql" + "entgo.io/ent/dialect/sql/sqlgraph" + "entgo.io/ent/schema/field" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/predicate" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/servicecategory" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/supportedlocale" + "github.com/google/uuid" +) + +// ServiceCategoryUpdate is the builder for updating ServiceCategory entities. +type ServiceCategoryUpdate struct { + config + hooks []Hook + mutation *ServiceCategoryMutation +} + +// Where appends a list predicates to the ServiceCategoryUpdate builder. +func (_u *ServiceCategoryUpdate) Where(ps ...predicate.ServiceCategory) *ServiceCategoryUpdate { + _u.mutation.Where(ps...) + return _u +} + +// SetParentID sets the "parent_id" field. +func (_u *ServiceCategoryUpdate) SetParentID(v uuid.UUID) *ServiceCategoryUpdate { + _u.mutation.SetParentID(v) + return _u +} + +// SetNillableParentID sets the "parent_id" field if the given value is not nil. +func (_u *ServiceCategoryUpdate) SetNillableParentID(v *uuid.UUID) *ServiceCategoryUpdate { + if v != nil { + _u.SetParentID(*v) + } + return _u +} + +// ClearParentID clears the value of the "parent_id" field. +func (_u *ServiceCategoryUpdate) ClearParentID() *ServiceCategoryUpdate { + _u.mutation.ClearParentID() + return _u +} + +// SetActive sets the "active" field. +func (_u *ServiceCategoryUpdate) SetActive(v bool) *ServiceCategoryUpdate { + _u.mutation.SetActive(v) + return _u +} + +// SetNillableActive sets the "active" field if the given value is not nil. +func (_u *ServiceCategoryUpdate) SetNillableActive(v *bool) *ServiceCategoryUpdate { + if v != nil { + _u.SetActive(*v) + } + return _u +} + +// SetSortOrder sets the "sort_order" field. +func (_u *ServiceCategoryUpdate) SetSortOrder(v int) *ServiceCategoryUpdate { + _u.mutation.ResetSortOrder() + _u.mutation.SetSortOrder(v) + return _u +} + +// SetNillableSortOrder sets the "sort_order" field if the given value is not nil. +func (_u *ServiceCategoryUpdate) SetNillableSortOrder(v *int) *ServiceCategoryUpdate { + if v != nil { + _u.SetSortOrder(*v) + } + return _u +} + +// AddSortOrder adds value to the "sort_order" field. +func (_u *ServiceCategoryUpdate) AddSortOrder(v int) *ServiceCategoryUpdate { + _u.mutation.AddSortOrder(v) + return _u +} + +// SetUpdatedAt sets the "updated_at" field. +func (_u *ServiceCategoryUpdate) SetUpdatedAt(v time.Time) *ServiceCategoryUpdate { + _u.mutation.SetUpdatedAt(v) + return _u +} + +// AddChildIDs adds the "children" edge to the ServiceCategory entity by IDs. +func (_u *ServiceCategoryUpdate) AddChildIDs(ids ...uuid.UUID) *ServiceCategoryUpdate { + _u.mutation.AddChildIDs(ids...) + return _u +} + +// AddChildren adds the "children" edges to the ServiceCategory entity. +func (_u *ServiceCategoryUpdate) AddChildren(v ...*ServiceCategory) *ServiceCategoryUpdate { + ids := make([]uuid.UUID, len(v)) + for i := range v { + ids[i] = v[i].ID + } + return _u.AddChildIDs(ids...) +} + +// SetParent sets the "parent" edge to the ServiceCategory entity. +func (_u *ServiceCategoryUpdate) SetParent(v *ServiceCategory) *ServiceCategoryUpdate { + return _u.SetParentID(v.ID) +} + +// AddLocalizedInIDs adds the "localized_in" edge to the SupportedLocale entity by IDs. +func (_u *ServiceCategoryUpdate) AddLocalizedInIDs(ids ...string) *ServiceCategoryUpdate { + _u.mutation.AddLocalizedInIDs(ids...) + return _u +} + +// AddLocalizedIn adds the "localized_in" edges to the SupportedLocale entity. +func (_u *ServiceCategoryUpdate) AddLocalizedIn(v ...*SupportedLocale) *ServiceCategoryUpdate { + ids := make([]string, len(v)) + for i := range v { + ids[i] = v[i].ID + } + return _u.AddLocalizedInIDs(ids...) +} + +// Mutation returns the ServiceCategoryMutation object of the builder. +func (_u *ServiceCategoryUpdate) Mutation() *ServiceCategoryMutation { + return _u.mutation +} + +// ClearChildren clears all "children" edges to the ServiceCategory entity. +func (_u *ServiceCategoryUpdate) ClearChildren() *ServiceCategoryUpdate { + _u.mutation.ClearChildren() + return _u +} + +// RemoveChildIDs removes the "children" edge to ServiceCategory entities by IDs. +func (_u *ServiceCategoryUpdate) RemoveChildIDs(ids ...uuid.UUID) *ServiceCategoryUpdate { + _u.mutation.RemoveChildIDs(ids...) + return _u +} + +// RemoveChildren removes "children" edges to ServiceCategory entities. +func (_u *ServiceCategoryUpdate) RemoveChildren(v ...*ServiceCategory) *ServiceCategoryUpdate { + ids := make([]uuid.UUID, len(v)) + for i := range v { + ids[i] = v[i].ID + } + return _u.RemoveChildIDs(ids...) +} + +// ClearParent clears the "parent" edge to the ServiceCategory entity. +func (_u *ServiceCategoryUpdate) ClearParent() *ServiceCategoryUpdate { + _u.mutation.ClearParent() + return _u +} + +// ClearLocalizedIn clears all "localized_in" edges to the SupportedLocale entity. +func (_u *ServiceCategoryUpdate) ClearLocalizedIn() *ServiceCategoryUpdate { + _u.mutation.ClearLocalizedIn() + return _u +} + +// RemoveLocalizedInIDs removes the "localized_in" edge to SupportedLocale entities by IDs. +func (_u *ServiceCategoryUpdate) RemoveLocalizedInIDs(ids ...string) *ServiceCategoryUpdate { + _u.mutation.RemoveLocalizedInIDs(ids...) + return _u +} + +// RemoveLocalizedIn removes "localized_in" edges to SupportedLocale entities. +func (_u *ServiceCategoryUpdate) RemoveLocalizedIn(v ...*SupportedLocale) *ServiceCategoryUpdate { + ids := make([]string, len(v)) + for i := range v { + ids[i] = v[i].ID + } + return _u.RemoveLocalizedInIDs(ids...) +} + +// Save executes the query and returns the number of nodes affected by the update operation. +func (_u *ServiceCategoryUpdate) Save(ctx context.Context) (int, error) { + _u.defaults() + return withHooks(ctx, _u.sqlSave, _u.mutation, _u.hooks) +} + +// SaveX is like Save, but panics if an error occurs. +func (_u *ServiceCategoryUpdate) SaveX(ctx context.Context) int { + affected, err := _u.Save(ctx) + if err != nil { + panic(err) + } + return affected +} + +// Exec executes the query. +func (_u *ServiceCategoryUpdate) Exec(ctx context.Context) error { + _, err := _u.Save(ctx) + return err +} + +// ExecX is like Exec, but panics if an error occurs. +func (_u *ServiceCategoryUpdate) ExecX(ctx context.Context) { + if err := _u.Exec(ctx); err != nil { + panic(err) + } +} + +// defaults sets the default values of the builder before save. +func (_u *ServiceCategoryUpdate) defaults() { + if _, ok := _u.mutation.UpdatedAt(); !ok { + v := servicecategory.UpdateDefaultUpdatedAt() + _u.mutation.SetUpdatedAt(v) + } +} + +// check runs all checks and user-defined validators on the builder. +func (_u *ServiceCategoryUpdate) check() error { + if v, ok := _u.mutation.SortOrder(); ok { + if err := servicecategory.SortOrderValidator(v); err != nil { + return &ValidationError{Name: "sort_order", err: fmt.Errorf(`ent: validator failed for field "ServiceCategory.sort_order": %w`, err)} + } + } + return nil +} + +func (_u *ServiceCategoryUpdate) sqlSave(ctx context.Context) (_node int, err error) { + if err := _u.check(); err != nil { + return _node, err + } + _spec := sqlgraph.NewUpdateSpec(servicecategory.Table, servicecategory.Columns, sqlgraph.NewFieldSpec(servicecategory.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.Active(); ok { + _spec.SetField(servicecategory.FieldActive, field.TypeBool, value) + } + if value, ok := _u.mutation.SortOrder(); ok { + _spec.SetField(servicecategory.FieldSortOrder, field.TypeInt, value) + } + if value, ok := _u.mutation.AddedSortOrder(); ok { + _spec.AddField(servicecategory.FieldSortOrder, field.TypeInt, value) + } + if value, ok := _u.mutation.UpdatedAt(); ok { + _spec.SetField(servicecategory.FieldUpdatedAt, field.TypeTime, value) + } + if _u.mutation.ChildrenCleared() { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.O2M, + Inverse: false, + Table: servicecategory.ChildrenTable, + Columns: []string{servicecategory.ChildrenColumn}, + Bidi: true, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(servicecategory.FieldID, field.TypeUUID), + }, + } + _spec.Edges.Clear = append(_spec.Edges.Clear, edge) + } + if nodes := _u.mutation.RemovedChildrenIDs(); len(nodes) > 0 && !_u.mutation.ChildrenCleared() { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.O2M, + Inverse: false, + Table: servicecategory.ChildrenTable, + Columns: []string{servicecategory.ChildrenColumn}, + Bidi: true, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(servicecategory.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.ChildrenIDs(); len(nodes) > 0 { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.O2M, + Inverse: false, + Table: servicecategory.ChildrenTable, + Columns: []string{servicecategory.ChildrenColumn}, + Bidi: true, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(servicecategory.FieldID, field.TypeUUID), + }, + } + for _, k := range nodes { + edge.Target.Nodes = append(edge.Target.Nodes, k) + } + _spec.Edges.Add = append(_spec.Edges.Add, edge) + } + if _u.mutation.ParentCleared() { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.M2O, + Inverse: true, + Table: servicecategory.ParentTable, + Columns: []string{servicecategory.ParentColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(servicecategory.FieldID, field.TypeUUID), + }, + } + _spec.Edges.Clear = append(_spec.Edges.Clear, edge) + } + if nodes := _u.mutation.ParentIDs(); len(nodes) > 0 { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.M2O, + Inverse: true, + Table: servicecategory.ParentTable, + Columns: []string{servicecategory.ParentColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(servicecategory.FieldID, field.TypeUUID), + }, + } + for _, k := range nodes { + edge.Target.Nodes = append(edge.Target.Nodes, k) + } + _spec.Edges.Add = append(_spec.Edges.Add, edge) + } + if _u.mutation.LocalizedInCleared() { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.M2M, + Inverse: false, + Table: servicecategory.LocalizedInTable, + Columns: servicecategory.LocalizedInPrimaryKey, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(supportedlocale.FieldID, field.TypeString), + }, + } + _spec.Edges.Clear = append(_spec.Edges.Clear, edge) + } + if nodes := _u.mutation.RemovedLocalizedInIDs(); len(nodes) > 0 && !_u.mutation.LocalizedInCleared() { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.M2M, + Inverse: false, + Table: servicecategory.LocalizedInTable, + Columns: servicecategory.LocalizedInPrimaryKey, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(supportedlocale.FieldID, field.TypeString), + }, + } + for _, k := range nodes { + edge.Target.Nodes = append(edge.Target.Nodes, k) + } + _spec.Edges.Clear = append(_spec.Edges.Clear, edge) + } + if nodes := _u.mutation.LocalizedInIDs(); len(nodes) > 0 { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.M2M, + Inverse: false, + Table: servicecategory.LocalizedInTable, + Columns: servicecategory.LocalizedInPrimaryKey, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(supportedlocale.FieldID, field.TypeString), + }, + } + 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{servicecategory.Label} + } else if sqlgraph.IsConstraintError(err) { + err = &ConstraintError{msg: err.Error(), wrap: err} + } + return 0, err + } + _u.mutation.done = true + return _node, nil +} + +// ServiceCategoryUpdateOne is the builder for updating a single ServiceCategory entity. +type ServiceCategoryUpdateOne struct { + config + fields []string + hooks []Hook + mutation *ServiceCategoryMutation +} + +// SetParentID sets the "parent_id" field. +func (_u *ServiceCategoryUpdateOne) SetParentID(v uuid.UUID) *ServiceCategoryUpdateOne { + _u.mutation.SetParentID(v) + return _u +} + +// SetNillableParentID sets the "parent_id" field if the given value is not nil. +func (_u *ServiceCategoryUpdateOne) SetNillableParentID(v *uuid.UUID) *ServiceCategoryUpdateOne { + if v != nil { + _u.SetParentID(*v) + } + return _u +} + +// ClearParentID clears the value of the "parent_id" field. +func (_u *ServiceCategoryUpdateOne) ClearParentID() *ServiceCategoryUpdateOne { + _u.mutation.ClearParentID() + return _u +} + +// SetActive sets the "active" field. +func (_u *ServiceCategoryUpdateOne) SetActive(v bool) *ServiceCategoryUpdateOne { + _u.mutation.SetActive(v) + return _u +} + +// SetNillableActive sets the "active" field if the given value is not nil. +func (_u *ServiceCategoryUpdateOne) SetNillableActive(v *bool) *ServiceCategoryUpdateOne { + if v != nil { + _u.SetActive(*v) + } + return _u +} + +// SetSortOrder sets the "sort_order" field. +func (_u *ServiceCategoryUpdateOne) SetSortOrder(v int) *ServiceCategoryUpdateOne { + _u.mutation.ResetSortOrder() + _u.mutation.SetSortOrder(v) + return _u +} + +// SetNillableSortOrder sets the "sort_order" field if the given value is not nil. +func (_u *ServiceCategoryUpdateOne) SetNillableSortOrder(v *int) *ServiceCategoryUpdateOne { + if v != nil { + _u.SetSortOrder(*v) + } + return _u +} + +// AddSortOrder adds value to the "sort_order" field. +func (_u *ServiceCategoryUpdateOne) AddSortOrder(v int) *ServiceCategoryUpdateOne { + _u.mutation.AddSortOrder(v) + return _u +} + +// SetUpdatedAt sets the "updated_at" field. +func (_u *ServiceCategoryUpdateOne) SetUpdatedAt(v time.Time) *ServiceCategoryUpdateOne { + _u.mutation.SetUpdatedAt(v) + return _u +} + +// AddChildIDs adds the "children" edge to the ServiceCategory entity by IDs. +func (_u *ServiceCategoryUpdateOne) AddChildIDs(ids ...uuid.UUID) *ServiceCategoryUpdateOne { + _u.mutation.AddChildIDs(ids...) + return _u +} + +// AddChildren adds the "children" edges to the ServiceCategory entity. +func (_u *ServiceCategoryUpdateOne) AddChildren(v ...*ServiceCategory) *ServiceCategoryUpdateOne { + ids := make([]uuid.UUID, len(v)) + for i := range v { + ids[i] = v[i].ID + } + return _u.AddChildIDs(ids...) +} + +// SetParent sets the "parent" edge to the ServiceCategory entity. +func (_u *ServiceCategoryUpdateOne) SetParent(v *ServiceCategory) *ServiceCategoryUpdateOne { + return _u.SetParentID(v.ID) +} + +// AddLocalizedInIDs adds the "localized_in" edge to the SupportedLocale entity by IDs. +func (_u *ServiceCategoryUpdateOne) AddLocalizedInIDs(ids ...string) *ServiceCategoryUpdateOne { + _u.mutation.AddLocalizedInIDs(ids...) + return _u +} + +// AddLocalizedIn adds the "localized_in" edges to the SupportedLocale entity. +func (_u *ServiceCategoryUpdateOne) AddLocalizedIn(v ...*SupportedLocale) *ServiceCategoryUpdateOne { + ids := make([]string, len(v)) + for i := range v { + ids[i] = v[i].ID + } + return _u.AddLocalizedInIDs(ids...) +} + +// Mutation returns the ServiceCategoryMutation object of the builder. +func (_u *ServiceCategoryUpdateOne) Mutation() *ServiceCategoryMutation { + return _u.mutation +} + +// ClearChildren clears all "children" edges to the ServiceCategory entity. +func (_u *ServiceCategoryUpdateOne) ClearChildren() *ServiceCategoryUpdateOne { + _u.mutation.ClearChildren() + return _u +} + +// RemoveChildIDs removes the "children" edge to ServiceCategory entities by IDs. +func (_u *ServiceCategoryUpdateOne) RemoveChildIDs(ids ...uuid.UUID) *ServiceCategoryUpdateOne { + _u.mutation.RemoveChildIDs(ids...) + return _u +} + +// RemoveChildren removes "children" edges to ServiceCategory entities. +func (_u *ServiceCategoryUpdateOne) RemoveChildren(v ...*ServiceCategory) *ServiceCategoryUpdateOne { + ids := make([]uuid.UUID, len(v)) + for i := range v { + ids[i] = v[i].ID + } + return _u.RemoveChildIDs(ids...) +} + +// ClearParent clears the "parent" edge to the ServiceCategory entity. +func (_u *ServiceCategoryUpdateOne) ClearParent() *ServiceCategoryUpdateOne { + _u.mutation.ClearParent() + return _u +} + +// ClearLocalizedIn clears all "localized_in" edges to the SupportedLocale entity. +func (_u *ServiceCategoryUpdateOne) ClearLocalizedIn() *ServiceCategoryUpdateOne { + _u.mutation.ClearLocalizedIn() + return _u +} + +// RemoveLocalizedInIDs removes the "localized_in" edge to SupportedLocale entities by IDs. +func (_u *ServiceCategoryUpdateOne) RemoveLocalizedInIDs(ids ...string) *ServiceCategoryUpdateOne { + _u.mutation.RemoveLocalizedInIDs(ids...) + return _u +} + +// RemoveLocalizedIn removes "localized_in" edges to SupportedLocale entities. +func (_u *ServiceCategoryUpdateOne) RemoveLocalizedIn(v ...*SupportedLocale) *ServiceCategoryUpdateOne { + ids := make([]string, len(v)) + for i := range v { + ids[i] = v[i].ID + } + return _u.RemoveLocalizedInIDs(ids...) +} + +// Where appends a list predicates to the ServiceCategoryUpdate builder. +func (_u *ServiceCategoryUpdateOne) Where(ps ...predicate.ServiceCategory) *ServiceCategoryUpdateOne { + _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 *ServiceCategoryUpdateOne) Select(field string, fields ...string) *ServiceCategoryUpdateOne { + _u.fields = append([]string{field}, fields...) + return _u +} + +// Save executes the query and returns the updated ServiceCategory entity. +func (_u *ServiceCategoryUpdateOne) Save(ctx context.Context) (*ServiceCategory, error) { + _u.defaults() + return withHooks(ctx, _u.sqlSave, _u.mutation, _u.hooks) +} + +// SaveX is like Save, but panics if an error occurs. +func (_u *ServiceCategoryUpdateOne) SaveX(ctx context.Context) *ServiceCategory { + node, err := _u.Save(ctx) + if err != nil { + panic(err) + } + return node +} + +// Exec executes the query on the entity. +func (_u *ServiceCategoryUpdateOne) Exec(ctx context.Context) error { + _, err := _u.Save(ctx) + return err +} + +// ExecX is like Exec, but panics if an error occurs. +func (_u *ServiceCategoryUpdateOne) ExecX(ctx context.Context) { + if err := _u.Exec(ctx); err != nil { + panic(err) + } +} + +// defaults sets the default values of the builder before save. +func (_u *ServiceCategoryUpdateOne) defaults() { + if _, ok := _u.mutation.UpdatedAt(); !ok { + v := servicecategory.UpdateDefaultUpdatedAt() + _u.mutation.SetUpdatedAt(v) + } +} + +// check runs all checks and user-defined validators on the builder. +func (_u *ServiceCategoryUpdateOne) check() error { + if v, ok := _u.mutation.SortOrder(); ok { + if err := servicecategory.SortOrderValidator(v); err != nil { + return &ValidationError{Name: "sort_order", err: fmt.Errorf(`ent: validator failed for field "ServiceCategory.sort_order": %w`, err)} + } + } + return nil +} + +func (_u *ServiceCategoryUpdateOne) sqlSave(ctx context.Context) (_node *ServiceCategory, err error) { + if err := _u.check(); err != nil { + return _node, err + } + _spec := sqlgraph.NewUpdateSpec(servicecategory.Table, servicecategory.Columns, sqlgraph.NewFieldSpec(servicecategory.FieldID, field.TypeUUID)) + id, ok := _u.mutation.ID() + if !ok { + return nil, &ValidationError{Name: "id", err: errors.New(`ent: missing "ServiceCategory.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, servicecategory.FieldID) + for _, f := range fields { + if !servicecategory.ValidColumn(f) { + return nil, &ValidationError{Name: f, err: fmt.Errorf("ent: invalid field %q for query", f)} + } + if f != servicecategory.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.Active(); ok { + _spec.SetField(servicecategory.FieldActive, field.TypeBool, value) + } + if value, ok := _u.mutation.SortOrder(); ok { + _spec.SetField(servicecategory.FieldSortOrder, field.TypeInt, value) + } + if value, ok := _u.mutation.AddedSortOrder(); ok { + _spec.AddField(servicecategory.FieldSortOrder, field.TypeInt, value) + } + if value, ok := _u.mutation.UpdatedAt(); ok { + _spec.SetField(servicecategory.FieldUpdatedAt, field.TypeTime, value) + } + if _u.mutation.ChildrenCleared() { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.O2M, + Inverse: false, + Table: servicecategory.ChildrenTable, + Columns: []string{servicecategory.ChildrenColumn}, + Bidi: true, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(servicecategory.FieldID, field.TypeUUID), + }, + } + _spec.Edges.Clear = append(_spec.Edges.Clear, edge) + } + if nodes := _u.mutation.RemovedChildrenIDs(); len(nodes) > 0 && !_u.mutation.ChildrenCleared() { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.O2M, + Inverse: false, + Table: servicecategory.ChildrenTable, + Columns: []string{servicecategory.ChildrenColumn}, + Bidi: true, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(servicecategory.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.ChildrenIDs(); len(nodes) > 0 { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.O2M, + Inverse: false, + Table: servicecategory.ChildrenTable, + Columns: []string{servicecategory.ChildrenColumn}, + Bidi: true, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(servicecategory.FieldID, field.TypeUUID), + }, + } + for _, k := range nodes { + edge.Target.Nodes = append(edge.Target.Nodes, k) + } + _spec.Edges.Add = append(_spec.Edges.Add, edge) + } + if _u.mutation.ParentCleared() { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.M2O, + Inverse: true, + Table: servicecategory.ParentTable, + Columns: []string{servicecategory.ParentColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(servicecategory.FieldID, field.TypeUUID), + }, + } + _spec.Edges.Clear = append(_spec.Edges.Clear, edge) + } + if nodes := _u.mutation.ParentIDs(); len(nodes) > 0 { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.M2O, + Inverse: true, + Table: servicecategory.ParentTable, + Columns: []string{servicecategory.ParentColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(servicecategory.FieldID, field.TypeUUID), + }, + } + for _, k := range nodes { + edge.Target.Nodes = append(edge.Target.Nodes, k) + } + _spec.Edges.Add = append(_spec.Edges.Add, edge) + } + if _u.mutation.LocalizedInCleared() { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.M2M, + Inverse: false, + Table: servicecategory.LocalizedInTable, + Columns: servicecategory.LocalizedInPrimaryKey, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(supportedlocale.FieldID, field.TypeString), + }, + } + _spec.Edges.Clear = append(_spec.Edges.Clear, edge) + } + if nodes := _u.mutation.RemovedLocalizedInIDs(); len(nodes) > 0 && !_u.mutation.LocalizedInCleared() { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.M2M, + Inverse: false, + Table: servicecategory.LocalizedInTable, + Columns: servicecategory.LocalizedInPrimaryKey, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(supportedlocale.FieldID, field.TypeString), + }, + } + for _, k := range nodes { + edge.Target.Nodes = append(edge.Target.Nodes, k) + } + _spec.Edges.Clear = append(_spec.Edges.Clear, edge) + } + if nodes := _u.mutation.LocalizedInIDs(); len(nodes) > 0 { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.M2M, + Inverse: false, + Table: servicecategory.LocalizedInTable, + Columns: servicecategory.LocalizedInPrimaryKey, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(supportedlocale.FieldID, field.TypeString), + }, + } + for _, k := range nodes { + edge.Target.Nodes = append(edge.Target.Nodes, k) + } + _spec.Edges.Add = append(_spec.Edges.Add, edge) + } + _node = &ServiceCategory{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{servicecategory.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/backend/ent/servicecategorytranslation.go b/backend/ent/servicecategorytranslation.go new file mode 100644 index 0000000..ed01a84 --- /dev/null +++ b/backend/ent/servicecategorytranslation.go @@ -0,0 +1,179 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "fmt" + "strings" + + "entgo.io/ent" + "entgo.io/ent/dialect/sql" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/servicecategory" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/servicecategorytranslation" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/supportedlocale" + "github.com/google/uuid" +) + +// ServiceCategoryTranslation is the model entity for the ServiceCategoryTranslation schema. +type ServiceCategoryTranslation struct { + config `json:"-"` + // CategoryID holds the value of the "category_id" field. + CategoryID uuid.UUID `json:"category_id,omitempty"` + // Locale holds the value of the "locale" field. + Locale string `json:"locale,omitempty"` + // Name holds the value of the "name" field. + Name string `json:"name,omitempty"` + // Description holds the value of the "description" field. + Description *string `json:"description,omitempty"` + // Edges holds the relations/edges for other nodes in the graph. + // The values are being populated by the ServiceCategoryTranslationQuery when eager-loading is set. + Edges ServiceCategoryTranslationEdges `json:"edges"` + selectValues sql.SelectValues +} + +// ServiceCategoryTranslationEdges holds the relations/edges for other nodes in the graph. +type ServiceCategoryTranslationEdges struct { + // Category holds the value of the category edge. + Category *ServiceCategory `json:"category,omitempty"` + // LocaleRecord holds the value of the locale_record edge. + LocaleRecord *SupportedLocale `json:"locale_record,omitempty"` + // loadedTypes holds the information for reporting if a + // type was loaded (or requested) in eager-loading or not. + loadedTypes [2]bool +} + +// CategoryOrErr returns the Category value or an error if the edge +// was not loaded in eager-loading, or loaded but was not found. +func (e ServiceCategoryTranslationEdges) CategoryOrErr() (*ServiceCategory, error) { + if e.Category != nil { + return e.Category, nil + } else if e.loadedTypes[0] { + return nil, &NotFoundError{label: servicecategory.Label} + } + return nil, &NotLoadedError{edge: "category"} +} + +// LocaleRecordOrErr returns the LocaleRecord value or an error if the edge +// was not loaded in eager-loading, or loaded but was not found. +func (e ServiceCategoryTranslationEdges) LocaleRecordOrErr() (*SupportedLocale, error) { + if e.LocaleRecord != nil { + return e.LocaleRecord, nil + } else if e.loadedTypes[1] { + return nil, &NotFoundError{label: supportedlocale.Label} + } + return nil, &NotLoadedError{edge: "locale_record"} +} + +// scanValues returns the types for scanning values from sql.Rows. +func (*ServiceCategoryTranslation) scanValues(columns []string) ([]any, error) { + values := make([]any, len(columns)) + for i := range columns { + switch columns[i] { + case servicecategorytranslation.FieldLocale, servicecategorytranslation.FieldName, servicecategorytranslation.FieldDescription: + values[i] = new(sql.NullString) + case servicecategorytranslation.FieldCategoryID: + 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 ServiceCategoryTranslation fields. +func (_m *ServiceCategoryTranslation) 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 servicecategorytranslation.FieldCategoryID: + if value, ok := values[i].(*uuid.UUID); !ok { + return fmt.Errorf("unexpected type %T for field category_id", values[i]) + } else if value != nil { + _m.CategoryID = *value + } + case servicecategorytranslation.FieldLocale: + if value, ok := values[i].(*sql.NullString); !ok { + return fmt.Errorf("unexpected type %T for field locale", values[i]) + } else if value.Valid { + _m.Locale = value.String + } + case servicecategorytranslation.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 servicecategorytranslation.FieldDescription: + if value, ok := values[i].(*sql.NullString); !ok { + return fmt.Errorf("unexpected type %T for field description", values[i]) + } else if value.Valid { + _m.Description = new(string) + *_m.Description = value.String + } + default: + _m.selectValues.Set(columns[i], values[i]) + } + } + return nil +} + +// Value returns the ent.Value that was dynamically selected and assigned to the ServiceCategoryTranslation. +// This includes values selected through modifiers, order, etc. +func (_m *ServiceCategoryTranslation) Value(name string) (ent.Value, error) { + return _m.selectValues.Get(name) +} + +// QueryCategory queries the "category" edge of the ServiceCategoryTranslation entity. +func (_m *ServiceCategoryTranslation) QueryCategory() *ServiceCategoryQuery { + return NewServiceCategoryTranslationClient(_m.config).QueryCategory(_m) +} + +// QueryLocaleRecord queries the "locale_record" edge of the ServiceCategoryTranslation entity. +func (_m *ServiceCategoryTranslation) QueryLocaleRecord() *SupportedLocaleQuery { + return NewServiceCategoryTranslationClient(_m.config).QueryLocaleRecord(_m) +} + +// Update returns a builder for updating this ServiceCategoryTranslation. +// Note that you need to call ServiceCategoryTranslation.Unwrap() before calling this method if this ServiceCategoryTranslation +// was returned from a transaction, and the transaction was committed or rolled back. +func (_m *ServiceCategoryTranslation) Update() *ServiceCategoryTranslationUpdateOne { + return NewServiceCategoryTranslationClient(_m.config).UpdateOne(_m) +} + +// Unwrap unwraps the ServiceCategoryTranslation 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 *ServiceCategoryTranslation) Unwrap() *ServiceCategoryTranslation { + _tx, ok := _m.config.driver.(*txDriver) + if !ok { + panic("ent: ServiceCategoryTranslation is not a transactional entity") + } + _m.config.driver = _tx.drv + return _m +} + +// String implements the fmt.Stringer. +func (_m *ServiceCategoryTranslation) String() string { + var builder strings.Builder + builder.WriteString("ServiceCategoryTranslation(") + builder.WriteString("category_id=") + builder.WriteString(fmt.Sprintf("%v", _m.CategoryID)) + builder.WriteString(", ") + builder.WriteString("locale=") + builder.WriteString(_m.Locale) + builder.WriteString(", ") + builder.WriteString("name=") + builder.WriteString(_m.Name) + builder.WriteString(", ") + if v := _m.Description; v != nil { + builder.WriteString("description=") + builder.WriteString(*v) + } + builder.WriteByte(')') + return builder.String() +} + +// ServiceCategoryTranslations is a parsable slice of ServiceCategoryTranslation. +type ServiceCategoryTranslations []*ServiceCategoryTranslation diff --git a/backend/ent/servicecategorytranslation/servicecategorytranslation.go b/backend/ent/servicecategorytranslation/servicecategorytranslation.go new file mode 100644 index 0000000..92c33e7 --- /dev/null +++ b/backend/ent/servicecategorytranslation/servicecategorytranslation.go @@ -0,0 +1,123 @@ +// Code generated by ent, DO NOT EDIT. + +package servicecategorytranslation + +import ( + "entgo.io/ent/dialect/sql" + "entgo.io/ent/dialect/sql/sqlgraph" +) + +const ( + // Label holds the string label denoting the servicecategorytranslation type in the database. + Label = "service_category_translation" + // FieldCategoryID holds the string denoting the category_id field in the database. + FieldCategoryID = "category_id" + // FieldLocale holds the string denoting the locale field in the database. + FieldLocale = "locale" + // FieldName holds the string denoting the name field in the database. + FieldName = "name" + // FieldDescription holds the string denoting the description field in the database. + FieldDescription = "description" + // EdgeCategory holds the string denoting the category edge name in mutations. + EdgeCategory = "category" + // EdgeLocaleRecord holds the string denoting the locale_record edge name in mutations. + EdgeLocaleRecord = "locale_record" + // ServiceCategoryFieldID holds the string denoting the ID field of the ServiceCategory. + ServiceCategoryFieldID = "id" + // SupportedLocaleFieldID holds the string denoting the ID field of the SupportedLocale. + SupportedLocaleFieldID = "id" + // Table holds the table name of the servicecategorytranslation in the database. + Table = "service_category_translations" + // CategoryTable is the table that holds the category relation/edge. + CategoryTable = "service_category_translations" + // CategoryInverseTable is the table name for the ServiceCategory entity. + // It exists in this package in order to avoid circular dependency with the "servicecategory" package. + CategoryInverseTable = "service_categories" + // CategoryColumn is the table column denoting the category relation/edge. + CategoryColumn = "category_id" + // LocaleRecordTable is the table that holds the locale_record relation/edge. + LocaleRecordTable = "service_category_translations" + // LocaleRecordInverseTable is the table name for the SupportedLocale entity. + // It exists in this package in order to avoid circular dependency with the "supportedlocale" package. + LocaleRecordInverseTable = "supported_locales" + // LocaleRecordColumn is the table column denoting the locale_record relation/edge. + LocaleRecordColumn = "locale" +) + +// Columns holds all SQL columns for servicecategorytranslation fields. +var Columns = []string{ + FieldCategoryID, + FieldLocale, + FieldName, + FieldDescription, +} + +// 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 ( + // LocaleValidator is a validator for the "locale" field. It is called by the builders before save. + LocaleValidator func(string) error + // NameValidator is a validator for the "name" field. It is called by the builders before save. + NameValidator func(string) error + // DescriptionValidator is a validator for the "description" field. It is called by the builders before save. + DescriptionValidator func(string) error +) + +// OrderOption defines the ordering options for the ServiceCategoryTranslation queries. +type OrderOption func(*sql.Selector) + +// ByCategoryID orders the results by the category_id field. +func ByCategoryID(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldCategoryID, opts...).ToFunc() +} + +// ByLocale orders the results by the locale field. +func ByLocale(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldLocale, opts...).ToFunc() +} + +// ByName orders the results by the name field. +func ByName(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldName, opts...).ToFunc() +} + +// ByDescription orders the results by the description field. +func ByDescription(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldDescription, opts...).ToFunc() +} + +// ByCategoryField orders the results by category field. +func ByCategoryField(field string, opts ...sql.OrderTermOption) OrderOption { + return func(s *sql.Selector) { + sqlgraph.OrderByNeighborTerms(s, newCategoryStep(), sql.OrderByField(field, opts...)) + } +} + +// ByLocaleRecordField orders the results by locale_record field. +func ByLocaleRecordField(field string, opts ...sql.OrderTermOption) OrderOption { + return func(s *sql.Selector) { + sqlgraph.OrderByNeighborTerms(s, newLocaleRecordStep(), sql.OrderByField(field, opts...)) + } +} +func newCategoryStep() *sqlgraph.Step { + return sqlgraph.NewStep( + sqlgraph.From(Table, CategoryColumn), + sqlgraph.To(CategoryInverseTable, ServiceCategoryFieldID), + sqlgraph.Edge(sqlgraph.M2O, false, CategoryTable, CategoryColumn), + ) +} +func newLocaleRecordStep() *sqlgraph.Step { + return sqlgraph.NewStep( + sqlgraph.From(Table, LocaleRecordColumn), + sqlgraph.To(LocaleRecordInverseTable, SupportedLocaleFieldID), + sqlgraph.Edge(sqlgraph.M2O, false, LocaleRecordTable, LocaleRecordColumn), + ) +} diff --git a/backend/ent/servicecategorytranslation/where.go b/backend/ent/servicecategorytranslation/where.go new file mode 100644 index 0000000..4a2a711 --- /dev/null +++ b/backend/ent/servicecategorytranslation/where.go @@ -0,0 +1,316 @@ +// Code generated by ent, DO NOT EDIT. + +package servicecategorytranslation + +import ( + "entgo.io/ent/dialect/sql" + "entgo.io/ent/dialect/sql/sqlgraph" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/predicate" + "github.com/google/uuid" +) + +// CategoryID applies equality check predicate on the "category_id" field. It's identical to CategoryIDEQ. +func CategoryID(v uuid.UUID) predicate.ServiceCategoryTranslation { + return predicate.ServiceCategoryTranslation(sql.FieldEQ(FieldCategoryID, v)) +} + +// Locale applies equality check predicate on the "locale" field. It's identical to LocaleEQ. +func Locale(v string) predicate.ServiceCategoryTranslation { + return predicate.ServiceCategoryTranslation(sql.FieldEQ(FieldLocale, v)) +} + +// Name applies equality check predicate on the "name" field. It's identical to NameEQ. +func Name(v string) predicate.ServiceCategoryTranslation { + return predicate.ServiceCategoryTranslation(sql.FieldEQ(FieldName, v)) +} + +// Description applies equality check predicate on the "description" field. It's identical to DescriptionEQ. +func Description(v string) predicate.ServiceCategoryTranslation { + return predicate.ServiceCategoryTranslation(sql.FieldEQ(FieldDescription, v)) +} + +// CategoryIDEQ applies the EQ predicate on the "category_id" field. +func CategoryIDEQ(v uuid.UUID) predicate.ServiceCategoryTranslation { + return predicate.ServiceCategoryTranslation(sql.FieldEQ(FieldCategoryID, v)) +} + +// CategoryIDNEQ applies the NEQ predicate on the "category_id" field. +func CategoryIDNEQ(v uuid.UUID) predicate.ServiceCategoryTranslation { + return predicate.ServiceCategoryTranslation(sql.FieldNEQ(FieldCategoryID, v)) +} + +// CategoryIDIn applies the In predicate on the "category_id" field. +func CategoryIDIn(vs ...uuid.UUID) predicate.ServiceCategoryTranslation { + return predicate.ServiceCategoryTranslation(sql.FieldIn(FieldCategoryID, vs...)) +} + +// CategoryIDNotIn applies the NotIn predicate on the "category_id" field. +func CategoryIDNotIn(vs ...uuid.UUID) predicate.ServiceCategoryTranslation { + return predicate.ServiceCategoryTranslation(sql.FieldNotIn(FieldCategoryID, vs...)) +} + +// LocaleEQ applies the EQ predicate on the "locale" field. +func LocaleEQ(v string) predicate.ServiceCategoryTranslation { + return predicate.ServiceCategoryTranslation(sql.FieldEQ(FieldLocale, v)) +} + +// LocaleNEQ applies the NEQ predicate on the "locale" field. +func LocaleNEQ(v string) predicate.ServiceCategoryTranslation { + return predicate.ServiceCategoryTranslation(sql.FieldNEQ(FieldLocale, v)) +} + +// LocaleIn applies the In predicate on the "locale" field. +func LocaleIn(vs ...string) predicate.ServiceCategoryTranslation { + return predicate.ServiceCategoryTranslation(sql.FieldIn(FieldLocale, vs...)) +} + +// LocaleNotIn applies the NotIn predicate on the "locale" field. +func LocaleNotIn(vs ...string) predicate.ServiceCategoryTranslation { + return predicate.ServiceCategoryTranslation(sql.FieldNotIn(FieldLocale, vs...)) +} + +// LocaleGT applies the GT predicate on the "locale" field. +func LocaleGT(v string) predicate.ServiceCategoryTranslation { + return predicate.ServiceCategoryTranslation(sql.FieldGT(FieldLocale, v)) +} + +// LocaleGTE applies the GTE predicate on the "locale" field. +func LocaleGTE(v string) predicate.ServiceCategoryTranslation { + return predicate.ServiceCategoryTranslation(sql.FieldGTE(FieldLocale, v)) +} + +// LocaleLT applies the LT predicate on the "locale" field. +func LocaleLT(v string) predicate.ServiceCategoryTranslation { + return predicate.ServiceCategoryTranslation(sql.FieldLT(FieldLocale, v)) +} + +// LocaleLTE applies the LTE predicate on the "locale" field. +func LocaleLTE(v string) predicate.ServiceCategoryTranslation { + return predicate.ServiceCategoryTranslation(sql.FieldLTE(FieldLocale, v)) +} + +// LocaleContains applies the Contains predicate on the "locale" field. +func LocaleContains(v string) predicate.ServiceCategoryTranslation { + return predicate.ServiceCategoryTranslation(sql.FieldContains(FieldLocale, v)) +} + +// LocaleHasPrefix applies the HasPrefix predicate on the "locale" field. +func LocaleHasPrefix(v string) predicate.ServiceCategoryTranslation { + return predicate.ServiceCategoryTranslation(sql.FieldHasPrefix(FieldLocale, v)) +} + +// LocaleHasSuffix applies the HasSuffix predicate on the "locale" field. +func LocaleHasSuffix(v string) predicate.ServiceCategoryTranslation { + return predicate.ServiceCategoryTranslation(sql.FieldHasSuffix(FieldLocale, v)) +} + +// LocaleEqualFold applies the EqualFold predicate on the "locale" field. +func LocaleEqualFold(v string) predicate.ServiceCategoryTranslation { + return predicate.ServiceCategoryTranslation(sql.FieldEqualFold(FieldLocale, v)) +} + +// LocaleContainsFold applies the ContainsFold predicate on the "locale" field. +func LocaleContainsFold(v string) predicate.ServiceCategoryTranslation { + return predicate.ServiceCategoryTranslation(sql.FieldContainsFold(FieldLocale, v)) +} + +// NameEQ applies the EQ predicate on the "name" field. +func NameEQ(v string) predicate.ServiceCategoryTranslation { + return predicate.ServiceCategoryTranslation(sql.FieldEQ(FieldName, v)) +} + +// NameNEQ applies the NEQ predicate on the "name" field. +func NameNEQ(v string) predicate.ServiceCategoryTranslation { + return predicate.ServiceCategoryTranslation(sql.FieldNEQ(FieldName, v)) +} + +// NameIn applies the In predicate on the "name" field. +func NameIn(vs ...string) predicate.ServiceCategoryTranslation { + return predicate.ServiceCategoryTranslation(sql.FieldIn(FieldName, vs...)) +} + +// NameNotIn applies the NotIn predicate on the "name" field. +func NameNotIn(vs ...string) predicate.ServiceCategoryTranslation { + return predicate.ServiceCategoryTranslation(sql.FieldNotIn(FieldName, vs...)) +} + +// NameGT applies the GT predicate on the "name" field. +func NameGT(v string) predicate.ServiceCategoryTranslation { + return predicate.ServiceCategoryTranslation(sql.FieldGT(FieldName, v)) +} + +// NameGTE applies the GTE predicate on the "name" field. +func NameGTE(v string) predicate.ServiceCategoryTranslation { + return predicate.ServiceCategoryTranslation(sql.FieldGTE(FieldName, v)) +} + +// NameLT applies the LT predicate on the "name" field. +func NameLT(v string) predicate.ServiceCategoryTranslation { + return predicate.ServiceCategoryTranslation(sql.FieldLT(FieldName, v)) +} + +// NameLTE applies the LTE predicate on the "name" field. +func NameLTE(v string) predicate.ServiceCategoryTranslation { + return predicate.ServiceCategoryTranslation(sql.FieldLTE(FieldName, v)) +} + +// NameContains applies the Contains predicate on the "name" field. +func NameContains(v string) predicate.ServiceCategoryTranslation { + return predicate.ServiceCategoryTranslation(sql.FieldContains(FieldName, v)) +} + +// NameHasPrefix applies the HasPrefix predicate on the "name" field. +func NameHasPrefix(v string) predicate.ServiceCategoryTranslation { + return predicate.ServiceCategoryTranslation(sql.FieldHasPrefix(FieldName, v)) +} + +// NameHasSuffix applies the HasSuffix predicate on the "name" field. +func NameHasSuffix(v string) predicate.ServiceCategoryTranslation { + return predicate.ServiceCategoryTranslation(sql.FieldHasSuffix(FieldName, v)) +} + +// NameEqualFold applies the EqualFold predicate on the "name" field. +func NameEqualFold(v string) predicate.ServiceCategoryTranslation { + return predicate.ServiceCategoryTranslation(sql.FieldEqualFold(FieldName, v)) +} + +// NameContainsFold applies the ContainsFold predicate on the "name" field. +func NameContainsFold(v string) predicate.ServiceCategoryTranslation { + return predicate.ServiceCategoryTranslation(sql.FieldContainsFold(FieldName, v)) +} + +// DescriptionEQ applies the EQ predicate on the "description" field. +func DescriptionEQ(v string) predicate.ServiceCategoryTranslation { + return predicate.ServiceCategoryTranslation(sql.FieldEQ(FieldDescription, v)) +} + +// DescriptionNEQ applies the NEQ predicate on the "description" field. +func DescriptionNEQ(v string) predicate.ServiceCategoryTranslation { + return predicate.ServiceCategoryTranslation(sql.FieldNEQ(FieldDescription, v)) +} + +// DescriptionIn applies the In predicate on the "description" field. +func DescriptionIn(vs ...string) predicate.ServiceCategoryTranslation { + return predicate.ServiceCategoryTranslation(sql.FieldIn(FieldDescription, vs...)) +} + +// DescriptionNotIn applies the NotIn predicate on the "description" field. +func DescriptionNotIn(vs ...string) predicate.ServiceCategoryTranslation { + return predicate.ServiceCategoryTranslation(sql.FieldNotIn(FieldDescription, vs...)) +} + +// DescriptionGT applies the GT predicate on the "description" field. +func DescriptionGT(v string) predicate.ServiceCategoryTranslation { + return predicate.ServiceCategoryTranslation(sql.FieldGT(FieldDescription, v)) +} + +// DescriptionGTE applies the GTE predicate on the "description" field. +func DescriptionGTE(v string) predicate.ServiceCategoryTranslation { + return predicate.ServiceCategoryTranslation(sql.FieldGTE(FieldDescription, v)) +} + +// DescriptionLT applies the LT predicate on the "description" field. +func DescriptionLT(v string) predicate.ServiceCategoryTranslation { + return predicate.ServiceCategoryTranslation(sql.FieldLT(FieldDescription, v)) +} + +// DescriptionLTE applies the LTE predicate on the "description" field. +func DescriptionLTE(v string) predicate.ServiceCategoryTranslation { + return predicate.ServiceCategoryTranslation(sql.FieldLTE(FieldDescription, v)) +} + +// DescriptionContains applies the Contains predicate on the "description" field. +func DescriptionContains(v string) predicate.ServiceCategoryTranslation { + return predicate.ServiceCategoryTranslation(sql.FieldContains(FieldDescription, v)) +} + +// DescriptionHasPrefix applies the HasPrefix predicate on the "description" field. +func DescriptionHasPrefix(v string) predicate.ServiceCategoryTranslation { + return predicate.ServiceCategoryTranslation(sql.FieldHasPrefix(FieldDescription, v)) +} + +// DescriptionHasSuffix applies the HasSuffix predicate on the "description" field. +func DescriptionHasSuffix(v string) predicate.ServiceCategoryTranslation { + return predicate.ServiceCategoryTranslation(sql.FieldHasSuffix(FieldDescription, v)) +} + +// DescriptionIsNil applies the IsNil predicate on the "description" field. +func DescriptionIsNil() predicate.ServiceCategoryTranslation { + return predicate.ServiceCategoryTranslation(sql.FieldIsNull(FieldDescription)) +} + +// DescriptionNotNil applies the NotNil predicate on the "description" field. +func DescriptionNotNil() predicate.ServiceCategoryTranslation { + return predicate.ServiceCategoryTranslation(sql.FieldNotNull(FieldDescription)) +} + +// DescriptionEqualFold applies the EqualFold predicate on the "description" field. +func DescriptionEqualFold(v string) predicate.ServiceCategoryTranslation { + return predicate.ServiceCategoryTranslation(sql.FieldEqualFold(FieldDescription, v)) +} + +// DescriptionContainsFold applies the ContainsFold predicate on the "description" field. +func DescriptionContainsFold(v string) predicate.ServiceCategoryTranslation { + return predicate.ServiceCategoryTranslation(sql.FieldContainsFold(FieldDescription, v)) +} + +// HasCategory applies the HasEdge predicate on the "category" edge. +func HasCategory() predicate.ServiceCategoryTranslation { + return predicate.ServiceCategoryTranslation(func(s *sql.Selector) { + step := sqlgraph.NewStep( + sqlgraph.From(Table, CategoryColumn), + sqlgraph.Edge(sqlgraph.M2O, false, CategoryTable, CategoryColumn), + ) + sqlgraph.HasNeighbors(s, step) + }) +} + +// HasCategoryWith applies the HasEdge predicate on the "category" edge with a given conditions (other predicates). +func HasCategoryWith(preds ...predicate.ServiceCategory) predicate.ServiceCategoryTranslation { + return predicate.ServiceCategoryTranslation(func(s *sql.Selector) { + step := newCategoryStep() + sqlgraph.HasNeighborsWith(s, step, func(s *sql.Selector) { + for _, p := range preds { + p(s) + } + }) + }) +} + +// HasLocaleRecord applies the HasEdge predicate on the "locale_record" edge. +func HasLocaleRecord() predicate.ServiceCategoryTranslation { + return predicate.ServiceCategoryTranslation(func(s *sql.Selector) { + step := sqlgraph.NewStep( + sqlgraph.From(Table, LocaleRecordColumn), + sqlgraph.Edge(sqlgraph.M2O, false, LocaleRecordTable, LocaleRecordColumn), + ) + sqlgraph.HasNeighbors(s, step) + }) +} + +// HasLocaleRecordWith applies the HasEdge predicate on the "locale_record" edge with a given conditions (other predicates). +func HasLocaleRecordWith(preds ...predicate.SupportedLocale) predicate.ServiceCategoryTranslation { + return predicate.ServiceCategoryTranslation(func(s *sql.Selector) { + step := newLocaleRecordStep() + 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.ServiceCategoryTranslation) predicate.ServiceCategoryTranslation { + return predicate.ServiceCategoryTranslation(sql.AndPredicates(predicates...)) +} + +// Or groups predicates with the OR operator between them. +func Or(predicates ...predicate.ServiceCategoryTranslation) predicate.ServiceCategoryTranslation { + return predicate.ServiceCategoryTranslation(sql.OrPredicates(predicates...)) +} + +// Not applies the not operator on the given predicate. +func Not(p predicate.ServiceCategoryTranslation) predicate.ServiceCategoryTranslation { + return predicate.ServiceCategoryTranslation(sql.NotPredicates(p)) +} diff --git a/backend/ent/servicecategorytranslation_create.go b/backend/ent/servicecategorytranslation_create.go new file mode 100644 index 0000000..010cac4 --- /dev/null +++ b/backend/ent/servicecategorytranslation_create.go @@ -0,0 +1,284 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "context" + "errors" + "fmt" + + "entgo.io/ent/dialect/sql/sqlgraph" + "entgo.io/ent/schema/field" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/servicecategory" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/servicecategorytranslation" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/supportedlocale" + "github.com/google/uuid" +) + +// ServiceCategoryTranslationCreate is the builder for creating a ServiceCategoryTranslation entity. +type ServiceCategoryTranslationCreate struct { + config + mutation *ServiceCategoryTranslationMutation + hooks []Hook +} + +// SetCategoryID sets the "category_id" field. +func (_c *ServiceCategoryTranslationCreate) SetCategoryID(v uuid.UUID) *ServiceCategoryTranslationCreate { + _c.mutation.SetCategoryID(v) + return _c +} + +// SetLocale sets the "locale" field. +func (_c *ServiceCategoryTranslationCreate) SetLocale(v string) *ServiceCategoryTranslationCreate { + _c.mutation.SetLocale(v) + return _c +} + +// SetName sets the "name" field. +func (_c *ServiceCategoryTranslationCreate) SetName(v string) *ServiceCategoryTranslationCreate { + _c.mutation.SetName(v) + return _c +} + +// SetDescription sets the "description" field. +func (_c *ServiceCategoryTranslationCreate) SetDescription(v string) *ServiceCategoryTranslationCreate { + _c.mutation.SetDescription(v) + return _c +} + +// SetNillableDescription sets the "description" field if the given value is not nil. +func (_c *ServiceCategoryTranslationCreate) SetNillableDescription(v *string) *ServiceCategoryTranslationCreate { + if v != nil { + _c.SetDescription(*v) + } + return _c +} + +// SetCategory sets the "category" edge to the ServiceCategory entity. +func (_c *ServiceCategoryTranslationCreate) SetCategory(v *ServiceCategory) *ServiceCategoryTranslationCreate { + return _c.SetCategoryID(v.ID) +} + +// SetLocaleRecordID sets the "locale_record" edge to the SupportedLocale entity by ID. +func (_c *ServiceCategoryTranslationCreate) SetLocaleRecordID(id string) *ServiceCategoryTranslationCreate { + _c.mutation.SetLocaleRecordID(id) + return _c +} + +// SetLocaleRecord sets the "locale_record" edge to the SupportedLocale entity. +func (_c *ServiceCategoryTranslationCreate) SetLocaleRecord(v *SupportedLocale) *ServiceCategoryTranslationCreate { + return _c.SetLocaleRecordID(v.ID) +} + +// Mutation returns the ServiceCategoryTranslationMutation object of the builder. +func (_c *ServiceCategoryTranslationCreate) Mutation() *ServiceCategoryTranslationMutation { + return _c.mutation +} + +// Save creates the ServiceCategoryTranslation in the database. +func (_c *ServiceCategoryTranslationCreate) Save(ctx context.Context) (*ServiceCategoryTranslation, error) { + return withHooks(ctx, _c.sqlSave, _c.mutation, _c.hooks) +} + +// SaveX calls Save and panics if Save returns an error. +func (_c *ServiceCategoryTranslationCreate) SaveX(ctx context.Context) *ServiceCategoryTranslation { + v, err := _c.Save(ctx) + if err != nil { + panic(err) + } + return v +} + +// Exec executes the query. +func (_c *ServiceCategoryTranslationCreate) Exec(ctx context.Context) error { + _, err := _c.Save(ctx) + return err +} + +// ExecX is like Exec, but panics if an error occurs. +func (_c *ServiceCategoryTranslationCreate) 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 *ServiceCategoryTranslationCreate) check() error { + if _, ok := _c.mutation.CategoryID(); !ok { + return &ValidationError{Name: "category_id", err: errors.New(`ent: missing required field "ServiceCategoryTranslation.category_id"`)} + } + if _, ok := _c.mutation.Locale(); !ok { + return &ValidationError{Name: "locale", err: errors.New(`ent: missing required field "ServiceCategoryTranslation.locale"`)} + } + if v, ok := _c.mutation.Locale(); ok { + if err := servicecategorytranslation.LocaleValidator(v); err != nil { + return &ValidationError{Name: "locale", err: fmt.Errorf(`ent: validator failed for field "ServiceCategoryTranslation.locale": %w`, err)} + } + } + if _, ok := _c.mutation.Name(); !ok { + return &ValidationError{Name: "name", err: errors.New(`ent: missing required field "ServiceCategoryTranslation.name"`)} + } + if v, ok := _c.mutation.Name(); ok { + if err := servicecategorytranslation.NameValidator(v); err != nil { + return &ValidationError{Name: "name", err: fmt.Errorf(`ent: validator failed for field "ServiceCategoryTranslation.name": %w`, err)} + } + } + if v, ok := _c.mutation.Description(); ok { + if err := servicecategorytranslation.DescriptionValidator(v); err != nil { + return &ValidationError{Name: "description", err: fmt.Errorf(`ent: validator failed for field "ServiceCategoryTranslation.description": %w`, err)} + } + } + if len(_c.mutation.CategoryIDs()) == 0 { + return &ValidationError{Name: "category", err: errors.New(`ent: missing required edge "ServiceCategoryTranslation.category"`)} + } + if len(_c.mutation.LocaleRecordIDs()) == 0 { + return &ValidationError{Name: "locale_record", err: errors.New(`ent: missing required edge "ServiceCategoryTranslation.locale_record"`)} + } + return nil +} + +func (_c *ServiceCategoryTranslationCreate) sqlSave(ctx context.Context) (*ServiceCategoryTranslation, 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 + } + return _node, nil +} + +func (_c *ServiceCategoryTranslationCreate) createSpec() (*ServiceCategoryTranslation, *sqlgraph.CreateSpec) { + var ( + _node = &ServiceCategoryTranslation{config: _c.config} + _spec = sqlgraph.NewCreateSpec(servicecategorytranslation.Table, nil) + ) + if value, ok := _c.mutation.Name(); ok { + _spec.SetField(servicecategorytranslation.FieldName, field.TypeString, value) + _node.Name = value + } + if value, ok := _c.mutation.Description(); ok { + _spec.SetField(servicecategorytranslation.FieldDescription, field.TypeString, value) + _node.Description = &value + } + if nodes := _c.mutation.CategoryIDs(); len(nodes) > 0 { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.M2O, + Inverse: false, + Table: servicecategorytranslation.CategoryTable, + Columns: []string{servicecategorytranslation.CategoryColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(servicecategory.FieldID, field.TypeUUID), + }, + } + for _, k := range nodes { + edge.Target.Nodes = append(edge.Target.Nodes, k) + } + _node.CategoryID = nodes[0] + _spec.Edges = append(_spec.Edges, edge) + } + if nodes := _c.mutation.LocaleRecordIDs(); len(nodes) > 0 { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.M2O, + Inverse: false, + Table: servicecategorytranslation.LocaleRecordTable, + Columns: []string{servicecategorytranslation.LocaleRecordColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(supportedlocale.FieldID, field.TypeString), + }, + } + for _, k := range nodes { + edge.Target.Nodes = append(edge.Target.Nodes, k) + } + _node.Locale = nodes[0] + _spec.Edges = append(_spec.Edges, edge) + } + return _node, _spec +} + +// ServiceCategoryTranslationCreateBulk is the builder for creating many ServiceCategoryTranslation entities in bulk. +type ServiceCategoryTranslationCreateBulk struct { + config + err error + builders []*ServiceCategoryTranslationCreate +} + +// Save creates the ServiceCategoryTranslation entities in the database. +func (_c *ServiceCategoryTranslationCreateBulk) Save(ctx context.Context) ([]*ServiceCategoryTranslation, error) { + if _c.err != nil { + return nil, _c.err + } + specs := make([]*sqlgraph.CreateSpec, len(_c.builders)) + nodes := make([]*ServiceCategoryTranslation, 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.(*ServiceCategoryTranslationMutation) + 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.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 *ServiceCategoryTranslationCreateBulk) SaveX(ctx context.Context) []*ServiceCategoryTranslation { + v, err := _c.Save(ctx) + if err != nil { + panic(err) + } + return v +} + +// Exec executes the query. +func (_c *ServiceCategoryTranslationCreateBulk) Exec(ctx context.Context) error { + _, err := _c.Save(ctx) + return err +} + +// ExecX is like Exec, but panics if an error occurs. +func (_c *ServiceCategoryTranslationCreateBulk) ExecX(ctx context.Context) { + if err := _c.Exec(ctx); err != nil { + panic(err) + } +} diff --git a/backend/ent/servicecategorytranslation_delete.go b/backend/ent/servicecategorytranslation_delete.go new file mode 100644 index 0000000..6993d50 --- /dev/null +++ b/backend/ent/servicecategorytranslation_delete.go @@ -0,0 +1,87 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "context" + + "entgo.io/ent/dialect/sql" + "entgo.io/ent/dialect/sql/sqlgraph" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/predicate" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/servicecategorytranslation" +) + +// ServiceCategoryTranslationDelete is the builder for deleting a ServiceCategoryTranslation entity. +type ServiceCategoryTranslationDelete struct { + config + hooks []Hook + mutation *ServiceCategoryTranslationMutation +} + +// Where appends a list predicates to the ServiceCategoryTranslationDelete builder. +func (_d *ServiceCategoryTranslationDelete) Where(ps ...predicate.ServiceCategoryTranslation) *ServiceCategoryTranslationDelete { + _d.mutation.Where(ps...) + return _d +} + +// Exec executes the deletion query and returns how many vertices were deleted. +func (_d *ServiceCategoryTranslationDelete) 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 *ServiceCategoryTranslationDelete) ExecX(ctx context.Context) int { + n, err := _d.Exec(ctx) + if err != nil { + panic(err) + } + return n +} + +func (_d *ServiceCategoryTranslationDelete) sqlExec(ctx context.Context) (int, error) { + _spec := sqlgraph.NewDeleteSpec(servicecategorytranslation.Table, nil) + 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 +} + +// ServiceCategoryTranslationDeleteOne is the builder for deleting a single ServiceCategoryTranslation entity. +type ServiceCategoryTranslationDeleteOne struct { + _d *ServiceCategoryTranslationDelete +} + +// Where appends a list predicates to the ServiceCategoryTranslationDelete builder. +func (_d *ServiceCategoryTranslationDeleteOne) Where(ps ...predicate.ServiceCategoryTranslation) *ServiceCategoryTranslationDeleteOne { + _d._d.mutation.Where(ps...) + return _d +} + +// Exec executes the deletion query. +func (_d *ServiceCategoryTranslationDeleteOne) Exec(ctx context.Context) error { + n, err := _d._d.Exec(ctx) + switch { + case err != nil: + return err + case n == 0: + return &NotFoundError{servicecategorytranslation.Label} + default: + return nil + } +} + +// ExecX is like Exec, but panics if an error occurs. +func (_d *ServiceCategoryTranslationDeleteOne) ExecX(ctx context.Context) { + if err := _d.Exec(ctx); err != nil { + panic(err) + } +} diff --git a/backend/ent/servicecategorytranslation_query.go b/backend/ent/servicecategorytranslation_query.go new file mode 100644 index 0000000..7d9a8fe --- /dev/null +++ b/backend/ent/servicecategorytranslation_query.go @@ -0,0 +1,604 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "context" + "fmt" + "math" + + "entgo.io/ent" + "entgo.io/ent/dialect/sql" + "entgo.io/ent/dialect/sql/sqlgraph" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/predicate" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/servicecategory" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/servicecategorytranslation" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/supportedlocale" + "github.com/google/uuid" +) + +// ServiceCategoryTranslationQuery is the builder for querying ServiceCategoryTranslation entities. +type ServiceCategoryTranslationQuery struct { + config + ctx *QueryContext + order []servicecategorytranslation.OrderOption + inters []Interceptor + predicates []predicate.ServiceCategoryTranslation + withCategory *ServiceCategoryQuery + withLocaleRecord *SupportedLocaleQuery + // intermediate query (i.e. traversal path). + sql *sql.Selector + path func(context.Context) (*sql.Selector, error) +} + +// Where adds a new predicate for the ServiceCategoryTranslationQuery builder. +func (_q *ServiceCategoryTranslationQuery) Where(ps ...predicate.ServiceCategoryTranslation) *ServiceCategoryTranslationQuery { + _q.predicates = append(_q.predicates, ps...) + return _q +} + +// Limit the number of records to be returned by this query. +func (_q *ServiceCategoryTranslationQuery) Limit(limit int) *ServiceCategoryTranslationQuery { + _q.ctx.Limit = &limit + return _q +} + +// Offset to start from. +func (_q *ServiceCategoryTranslationQuery) Offset(offset int) *ServiceCategoryTranslationQuery { + _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 *ServiceCategoryTranslationQuery) Unique(unique bool) *ServiceCategoryTranslationQuery { + _q.ctx.Unique = &unique + return _q +} + +// Order specifies how the records should be ordered. +func (_q *ServiceCategoryTranslationQuery) Order(o ...servicecategorytranslation.OrderOption) *ServiceCategoryTranslationQuery { + _q.order = append(_q.order, o...) + return _q +} + +// QueryCategory chains the current query on the "category" edge. +func (_q *ServiceCategoryTranslationQuery) QueryCategory() *ServiceCategoryQuery { + query := (&ServiceCategoryClient{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(servicecategorytranslation.Table, servicecategorytranslation.CategoryColumn, selector), + sqlgraph.To(servicecategory.Table, servicecategory.FieldID), + sqlgraph.Edge(sqlgraph.M2O, false, servicecategorytranslation.CategoryTable, servicecategorytranslation.CategoryColumn), + ) + fromU = sqlgraph.SetNeighbors(_q.driver.Dialect(), step) + return fromU, nil + } + return query +} + +// QueryLocaleRecord chains the current query on the "locale_record" edge. +func (_q *ServiceCategoryTranslationQuery) QueryLocaleRecord() *SupportedLocaleQuery { + query := (&SupportedLocaleClient{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(servicecategorytranslation.Table, servicecategorytranslation.LocaleRecordColumn, selector), + sqlgraph.To(supportedlocale.Table, supportedlocale.FieldID), + sqlgraph.Edge(sqlgraph.M2O, false, servicecategorytranslation.LocaleRecordTable, servicecategorytranslation.LocaleRecordColumn), + ) + fromU = sqlgraph.SetNeighbors(_q.driver.Dialect(), step) + return fromU, nil + } + return query +} + +// First returns the first ServiceCategoryTranslation entity from the query. +// Returns a *NotFoundError when no ServiceCategoryTranslation was found. +func (_q *ServiceCategoryTranslationQuery) First(ctx context.Context) (*ServiceCategoryTranslation, 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{servicecategorytranslation.Label} + } + return nodes[0], nil +} + +// FirstX is like First, but panics if an error occurs. +func (_q *ServiceCategoryTranslationQuery) FirstX(ctx context.Context) *ServiceCategoryTranslation { + node, err := _q.First(ctx) + if err != nil && !IsNotFound(err) { + panic(err) + } + return node +} + +// Only returns a single ServiceCategoryTranslation entity found by the query, ensuring it only returns one. +// Returns a *NotSingularError when more than one ServiceCategoryTranslation entity is found. +// Returns a *NotFoundError when no ServiceCategoryTranslation entities are found. +func (_q *ServiceCategoryTranslationQuery) Only(ctx context.Context) (*ServiceCategoryTranslation, 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{servicecategorytranslation.Label} + default: + return nil, &NotSingularError{servicecategorytranslation.Label} + } +} + +// OnlyX is like Only, but panics if an error occurs. +func (_q *ServiceCategoryTranslationQuery) OnlyX(ctx context.Context) *ServiceCategoryTranslation { + node, err := _q.Only(ctx) + if err != nil { + panic(err) + } + return node +} + +// All executes the query and returns a list of ServiceCategoryTranslations. +func (_q *ServiceCategoryTranslationQuery) All(ctx context.Context) ([]*ServiceCategoryTranslation, error) { + ctx = setContextOp(ctx, _q.ctx, ent.OpQueryAll) + if err := _q.prepareQuery(ctx); err != nil { + return nil, err + } + qr := querierAll[[]*ServiceCategoryTranslation, *ServiceCategoryTranslationQuery]() + return withInterceptors[[]*ServiceCategoryTranslation](ctx, _q, qr, _q.inters) +} + +// AllX is like All, but panics if an error occurs. +func (_q *ServiceCategoryTranslationQuery) AllX(ctx context.Context) []*ServiceCategoryTranslation { + nodes, err := _q.All(ctx) + if err != nil { + panic(err) + } + return nodes +} + +// Count returns the count of the given query. +func (_q *ServiceCategoryTranslationQuery) 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[*ServiceCategoryTranslationQuery](), _q.inters) +} + +// CountX is like Count, but panics if an error occurs. +func (_q *ServiceCategoryTranslationQuery) 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 *ServiceCategoryTranslationQuery) Exist(ctx context.Context) (bool, error) { + ctx = setContextOp(ctx, _q.ctx, ent.OpQueryExist) + switch _, err := _q.First(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 *ServiceCategoryTranslationQuery) ExistX(ctx context.Context) bool { + exist, err := _q.Exist(ctx) + if err != nil { + panic(err) + } + return exist +} + +// Clone returns a duplicate of the ServiceCategoryTranslationQuery 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 *ServiceCategoryTranslationQuery) Clone() *ServiceCategoryTranslationQuery { + if _q == nil { + return nil + } + return &ServiceCategoryTranslationQuery{ + config: _q.config, + ctx: _q.ctx.Clone(), + order: append([]servicecategorytranslation.OrderOption{}, _q.order...), + inters: append([]Interceptor{}, _q.inters...), + predicates: append([]predicate.ServiceCategoryTranslation{}, _q.predicates...), + withCategory: _q.withCategory.Clone(), + withLocaleRecord: _q.withLocaleRecord.Clone(), + // clone intermediate query. + sql: _q.sql.Clone(), + path: _q.path, + } +} + +// WithCategory tells the query-builder to eager-load the nodes that are connected to +// the "category" edge. The optional arguments are used to configure the query builder of the edge. +func (_q *ServiceCategoryTranslationQuery) WithCategory(opts ...func(*ServiceCategoryQuery)) *ServiceCategoryTranslationQuery { + query := (&ServiceCategoryClient{config: _q.config}).Query() + for _, opt := range opts { + opt(query) + } + _q.withCategory = query + return _q +} + +// WithLocaleRecord tells the query-builder to eager-load the nodes that are connected to +// the "locale_record" edge. The optional arguments are used to configure the query builder of the edge. +func (_q *ServiceCategoryTranslationQuery) WithLocaleRecord(opts ...func(*SupportedLocaleQuery)) *ServiceCategoryTranslationQuery { + query := (&SupportedLocaleClient{config: _q.config}).Query() + for _, opt := range opts { + opt(query) + } + _q.withLocaleRecord = 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 { +// CategoryID uuid.UUID `json:"category_id,omitempty"` +// Count int `json:"count,omitempty"` +// } +// +// client.ServiceCategoryTranslation.Query(). +// GroupBy(servicecategorytranslation.FieldCategoryID). +// Aggregate(ent.Count()). +// Scan(ctx, &v) +func (_q *ServiceCategoryTranslationQuery) GroupBy(field string, fields ...string) *ServiceCategoryTranslationGroupBy { + _q.ctx.Fields = append([]string{field}, fields...) + grbuild := &ServiceCategoryTranslationGroupBy{build: _q} + grbuild.flds = &_q.ctx.Fields + grbuild.label = servicecategorytranslation.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 { +// CategoryID uuid.UUID `json:"category_id,omitempty"` +// } +// +// client.ServiceCategoryTranslation.Query(). +// Select(servicecategorytranslation.FieldCategoryID). +// Scan(ctx, &v) +func (_q *ServiceCategoryTranslationQuery) Select(fields ...string) *ServiceCategoryTranslationSelect { + _q.ctx.Fields = append(_q.ctx.Fields, fields...) + sbuild := &ServiceCategoryTranslationSelect{ServiceCategoryTranslationQuery: _q} + sbuild.label = servicecategorytranslation.Label + sbuild.flds, sbuild.scan = &_q.ctx.Fields, sbuild.Scan + return sbuild +} + +// Aggregate returns a ServiceCategoryTranslationSelect configured with the given aggregations. +func (_q *ServiceCategoryTranslationQuery) Aggregate(fns ...AggregateFunc) *ServiceCategoryTranslationSelect { + return _q.Select().Aggregate(fns...) +} + +func (_q *ServiceCategoryTranslationQuery) 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 !servicecategorytranslation.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 *ServiceCategoryTranslationQuery) sqlAll(ctx context.Context, hooks ...queryHook) ([]*ServiceCategoryTranslation, error) { + var ( + nodes = []*ServiceCategoryTranslation{} + _spec = _q.querySpec() + loadedTypes = [2]bool{ + _q.withCategory != nil, + _q.withLocaleRecord != nil, + } + ) + _spec.ScanValues = func(columns []string) ([]any, error) { + return (*ServiceCategoryTranslation).scanValues(nil, columns) + } + _spec.Assign = func(columns []string, values []any) error { + node := &ServiceCategoryTranslation{config: _q.config} + nodes = append(nodes, node) + node.Edges.loadedTypes = loadedTypes + return node.assignValues(columns, values) + } + 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.withCategory; query != nil { + if err := _q.loadCategory(ctx, query, nodes, nil, + func(n *ServiceCategoryTranslation, e *ServiceCategory) { n.Edges.Category = e }); err != nil { + return nil, err + } + } + if query := _q.withLocaleRecord; query != nil { + if err := _q.loadLocaleRecord(ctx, query, nodes, nil, + func(n *ServiceCategoryTranslation, e *SupportedLocale) { n.Edges.LocaleRecord = e }); err != nil { + return nil, err + } + } + return nodes, nil +} + +func (_q *ServiceCategoryTranslationQuery) loadCategory(ctx context.Context, query *ServiceCategoryQuery, nodes []*ServiceCategoryTranslation, init func(*ServiceCategoryTranslation), assign func(*ServiceCategoryTranslation, *ServiceCategory)) error { + ids := make([]uuid.UUID, 0, len(nodes)) + nodeids := make(map[uuid.UUID][]*ServiceCategoryTranslation) + for i := range nodes { + fk := nodes[i].CategoryID + if _, ok := nodeids[fk]; !ok { + ids = append(ids, fk) + } + nodeids[fk] = append(nodeids[fk], nodes[i]) + } + if len(ids) == 0 { + return nil + } + query.Where(servicecategory.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 "category_id" returned %v`, n.ID) + } + for i := range nodes { + assign(nodes[i], n) + } + } + return nil +} +func (_q *ServiceCategoryTranslationQuery) loadLocaleRecord(ctx context.Context, query *SupportedLocaleQuery, nodes []*ServiceCategoryTranslation, init func(*ServiceCategoryTranslation), assign func(*ServiceCategoryTranslation, *SupportedLocale)) error { + ids := make([]string, 0, len(nodes)) + nodeids := make(map[string][]*ServiceCategoryTranslation) + for i := range nodes { + fk := nodes[i].Locale + if _, ok := nodeids[fk]; !ok { + ids = append(ids, fk) + } + nodeids[fk] = append(nodeids[fk], nodes[i]) + } + if len(ids) == 0 { + return nil + } + query.Where(supportedlocale.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 "locale" returned %v`, n.ID) + } + for i := range nodes { + assign(nodes[i], n) + } + } + return nil +} + +func (_q *ServiceCategoryTranslationQuery) sqlCount(ctx context.Context) (int, error) { + _spec := _q.querySpec() + _spec.Unique = false + _spec.Node.Columns = nil + return sqlgraph.CountNodes(ctx, _q.driver, _spec) +} + +func (_q *ServiceCategoryTranslationQuery) querySpec() *sqlgraph.QuerySpec { + _spec := sqlgraph.NewQuerySpec(servicecategorytranslation.Table, servicecategorytranslation.Columns, nil) + _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)) + for i := range fields { + _spec.Node.Columns = append(_spec.Node.Columns, fields[i]) + } + if _q.withCategory != nil { + _spec.Node.AddColumnOnce(servicecategorytranslation.FieldCategoryID) + } + if _q.withLocaleRecord != nil { + _spec.Node.AddColumnOnce(servicecategorytranslation.FieldLocale) + } + } + 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 *ServiceCategoryTranslationQuery) sqlQuery(ctx context.Context) *sql.Selector { + builder := sql.Dialect(_q.driver.Dialect()) + t1 := builder.Table(servicecategorytranslation.Table) + columns := _q.ctx.Fields + if len(columns) == 0 { + columns = servicecategorytranslation.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 _, 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 +} + +// ServiceCategoryTranslationGroupBy is the group-by builder for ServiceCategoryTranslation entities. +type ServiceCategoryTranslationGroupBy struct { + selector + build *ServiceCategoryTranslationQuery +} + +// Aggregate adds the given aggregation functions to the group-by query. +func (_g *ServiceCategoryTranslationGroupBy) Aggregate(fns ...AggregateFunc) *ServiceCategoryTranslationGroupBy { + _g.fns = append(_g.fns, fns...) + return _g +} + +// Scan applies the selector query and scans the result into the given value. +func (_g *ServiceCategoryTranslationGroupBy) 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[*ServiceCategoryTranslationQuery, *ServiceCategoryTranslationGroupBy](ctx, _g.build, _g, _g.build.inters, v) +} + +func (_g *ServiceCategoryTranslationGroupBy) sqlScan(ctx context.Context, root *ServiceCategoryTranslationQuery, 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) +} + +// ServiceCategoryTranslationSelect is the builder for selecting fields of ServiceCategoryTranslation entities. +type ServiceCategoryTranslationSelect struct { + *ServiceCategoryTranslationQuery + selector +} + +// Aggregate adds the given aggregation functions to the selector query. +func (_s *ServiceCategoryTranslationSelect) Aggregate(fns ...AggregateFunc) *ServiceCategoryTranslationSelect { + _s.fns = append(_s.fns, fns...) + return _s +} + +// Scan applies the selector query and scans the result into the given value. +func (_s *ServiceCategoryTranslationSelect) 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[*ServiceCategoryTranslationQuery, *ServiceCategoryTranslationSelect](ctx, _s.ServiceCategoryTranslationQuery, _s, _s.inters, v) +} + +func (_s *ServiceCategoryTranslationSelect) sqlScan(ctx context.Context, root *ServiceCategoryTranslationQuery, 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/backend/ent/servicecategorytranslation_update.go b/backend/ent/servicecategorytranslation_update.go new file mode 100644 index 0000000..38e6974 --- /dev/null +++ b/backend/ent/servicecategorytranslation_update.go @@ -0,0 +1,311 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "context" + "errors" + "fmt" + + "entgo.io/ent/dialect/sql" + "entgo.io/ent/dialect/sql/sqlgraph" + "entgo.io/ent/schema/field" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/predicate" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/servicecategorytranslation" +) + +// ServiceCategoryTranslationUpdate is the builder for updating ServiceCategoryTranslation entities. +type ServiceCategoryTranslationUpdate struct { + config + hooks []Hook + mutation *ServiceCategoryTranslationMutation +} + +// Where appends a list predicates to the ServiceCategoryTranslationUpdate builder. +func (_u *ServiceCategoryTranslationUpdate) Where(ps ...predicate.ServiceCategoryTranslation) *ServiceCategoryTranslationUpdate { + _u.mutation.Where(ps...) + return _u +} + +// SetName sets the "name" field. +func (_u *ServiceCategoryTranslationUpdate) SetName(v string) *ServiceCategoryTranslationUpdate { + _u.mutation.SetName(v) + return _u +} + +// SetNillableName sets the "name" field if the given value is not nil. +func (_u *ServiceCategoryTranslationUpdate) SetNillableName(v *string) *ServiceCategoryTranslationUpdate { + if v != nil { + _u.SetName(*v) + } + return _u +} + +// SetDescription sets the "description" field. +func (_u *ServiceCategoryTranslationUpdate) SetDescription(v string) *ServiceCategoryTranslationUpdate { + _u.mutation.SetDescription(v) + return _u +} + +// SetNillableDescription sets the "description" field if the given value is not nil. +func (_u *ServiceCategoryTranslationUpdate) SetNillableDescription(v *string) *ServiceCategoryTranslationUpdate { + if v != nil { + _u.SetDescription(*v) + } + return _u +} + +// ClearDescription clears the value of the "description" field. +func (_u *ServiceCategoryTranslationUpdate) ClearDescription() *ServiceCategoryTranslationUpdate { + _u.mutation.ClearDescription() + return _u +} + +// Mutation returns the ServiceCategoryTranslationMutation object of the builder. +func (_u *ServiceCategoryTranslationUpdate) Mutation() *ServiceCategoryTranslationMutation { + return _u.mutation +} + +// Save executes the query and returns the number of nodes affected by the update operation. +func (_u *ServiceCategoryTranslationUpdate) 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 *ServiceCategoryTranslationUpdate) SaveX(ctx context.Context) int { + affected, err := _u.Save(ctx) + if err != nil { + panic(err) + } + return affected +} + +// Exec executes the query. +func (_u *ServiceCategoryTranslationUpdate) Exec(ctx context.Context) error { + _, err := _u.Save(ctx) + return err +} + +// ExecX is like Exec, but panics if an error occurs. +func (_u *ServiceCategoryTranslationUpdate) 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 *ServiceCategoryTranslationUpdate) check() error { + if v, ok := _u.mutation.Name(); ok { + if err := servicecategorytranslation.NameValidator(v); err != nil { + return &ValidationError{Name: "name", err: fmt.Errorf(`ent: validator failed for field "ServiceCategoryTranslation.name": %w`, err)} + } + } + if v, ok := _u.mutation.Description(); ok { + if err := servicecategorytranslation.DescriptionValidator(v); err != nil { + return &ValidationError{Name: "description", err: fmt.Errorf(`ent: validator failed for field "ServiceCategoryTranslation.description": %w`, err)} + } + } + if _u.mutation.CategoryCleared() && len(_u.mutation.CategoryIDs()) > 0 { + return errors.New(`ent: clearing a required unique edge "ServiceCategoryTranslation.category"`) + } + if _u.mutation.LocaleRecordCleared() && len(_u.mutation.LocaleRecordIDs()) > 0 { + return errors.New(`ent: clearing a required unique edge "ServiceCategoryTranslation.locale_record"`) + } + return nil +} + +func (_u *ServiceCategoryTranslationUpdate) sqlSave(ctx context.Context) (_node int, err error) { + if err := _u.check(); err != nil { + return _node, err + } + _spec := sqlgraph.NewUpdateSpec(servicecategorytranslation.Table, servicecategorytranslation.Columns, sqlgraph.NewFieldSpec(servicecategorytranslation.FieldCategoryID, field.TypeUUID), sqlgraph.NewFieldSpec(servicecategorytranslation.FieldLocale, field.TypeString)) + 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(servicecategorytranslation.FieldName, field.TypeString, value) + } + if value, ok := _u.mutation.Description(); ok { + _spec.SetField(servicecategorytranslation.FieldDescription, field.TypeString, value) + } + if _u.mutation.DescriptionCleared() { + _spec.ClearField(servicecategorytranslation.FieldDescription, field.TypeString) + } + if _node, err = sqlgraph.UpdateNodes(ctx, _u.driver, _spec); err != nil { + if _, ok := err.(*sqlgraph.NotFoundError); ok { + err = &NotFoundError{servicecategorytranslation.Label} + } else if sqlgraph.IsConstraintError(err) { + err = &ConstraintError{msg: err.Error(), wrap: err} + } + return 0, err + } + _u.mutation.done = true + return _node, nil +} + +// ServiceCategoryTranslationUpdateOne is the builder for updating a single ServiceCategoryTranslation entity. +type ServiceCategoryTranslationUpdateOne struct { + config + fields []string + hooks []Hook + mutation *ServiceCategoryTranslationMutation +} + +// SetName sets the "name" field. +func (_u *ServiceCategoryTranslationUpdateOne) SetName(v string) *ServiceCategoryTranslationUpdateOne { + _u.mutation.SetName(v) + return _u +} + +// SetNillableName sets the "name" field if the given value is not nil. +func (_u *ServiceCategoryTranslationUpdateOne) SetNillableName(v *string) *ServiceCategoryTranslationUpdateOne { + if v != nil { + _u.SetName(*v) + } + return _u +} + +// SetDescription sets the "description" field. +func (_u *ServiceCategoryTranslationUpdateOne) SetDescription(v string) *ServiceCategoryTranslationUpdateOne { + _u.mutation.SetDescription(v) + return _u +} + +// SetNillableDescription sets the "description" field if the given value is not nil. +func (_u *ServiceCategoryTranslationUpdateOne) SetNillableDescription(v *string) *ServiceCategoryTranslationUpdateOne { + if v != nil { + _u.SetDescription(*v) + } + return _u +} + +// ClearDescription clears the value of the "description" field. +func (_u *ServiceCategoryTranslationUpdateOne) ClearDescription() *ServiceCategoryTranslationUpdateOne { + _u.mutation.ClearDescription() + return _u +} + +// Mutation returns the ServiceCategoryTranslationMutation object of the builder. +func (_u *ServiceCategoryTranslationUpdateOne) Mutation() *ServiceCategoryTranslationMutation { + return _u.mutation +} + +// Where appends a list predicates to the ServiceCategoryTranslationUpdate builder. +func (_u *ServiceCategoryTranslationUpdateOne) Where(ps ...predicate.ServiceCategoryTranslation) *ServiceCategoryTranslationUpdateOne { + _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 *ServiceCategoryTranslationUpdateOne) Select(field string, fields ...string) *ServiceCategoryTranslationUpdateOne { + _u.fields = append([]string{field}, fields...) + return _u +} + +// Save executes the query and returns the updated ServiceCategoryTranslation entity. +func (_u *ServiceCategoryTranslationUpdateOne) Save(ctx context.Context) (*ServiceCategoryTranslation, error) { + return withHooks(ctx, _u.sqlSave, _u.mutation, _u.hooks) +} + +// SaveX is like Save, but panics if an error occurs. +func (_u *ServiceCategoryTranslationUpdateOne) SaveX(ctx context.Context) *ServiceCategoryTranslation { + node, err := _u.Save(ctx) + if err != nil { + panic(err) + } + return node +} + +// Exec executes the query on the entity. +func (_u *ServiceCategoryTranslationUpdateOne) Exec(ctx context.Context) error { + _, err := _u.Save(ctx) + return err +} + +// ExecX is like Exec, but panics if an error occurs. +func (_u *ServiceCategoryTranslationUpdateOne) 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 *ServiceCategoryTranslationUpdateOne) check() error { + if v, ok := _u.mutation.Name(); ok { + if err := servicecategorytranslation.NameValidator(v); err != nil { + return &ValidationError{Name: "name", err: fmt.Errorf(`ent: validator failed for field "ServiceCategoryTranslation.name": %w`, err)} + } + } + if v, ok := _u.mutation.Description(); ok { + if err := servicecategorytranslation.DescriptionValidator(v); err != nil { + return &ValidationError{Name: "description", err: fmt.Errorf(`ent: validator failed for field "ServiceCategoryTranslation.description": %w`, err)} + } + } + if _u.mutation.CategoryCleared() && len(_u.mutation.CategoryIDs()) > 0 { + return errors.New(`ent: clearing a required unique edge "ServiceCategoryTranslation.category"`) + } + if _u.mutation.LocaleRecordCleared() && len(_u.mutation.LocaleRecordIDs()) > 0 { + return errors.New(`ent: clearing a required unique edge "ServiceCategoryTranslation.locale_record"`) + } + return nil +} + +func (_u *ServiceCategoryTranslationUpdateOne) sqlSave(ctx context.Context) (_node *ServiceCategoryTranslation, err error) { + if err := _u.check(); err != nil { + return _node, err + } + _spec := sqlgraph.NewUpdateSpec(servicecategorytranslation.Table, servicecategorytranslation.Columns, sqlgraph.NewFieldSpec(servicecategorytranslation.FieldCategoryID, field.TypeUUID), sqlgraph.NewFieldSpec(servicecategorytranslation.FieldLocale, field.TypeString)) + if id, ok := _u.mutation.CategoryID(); !ok { + return nil, &ValidationError{Name: "category_id", err: errors.New(`ent: missing "ServiceCategoryTranslation.category_id" for update`)} + } else { + _spec.Node.CompositeID[0].Value = id + } + if id, ok := _u.mutation.Locale(); !ok { + return nil, &ValidationError{Name: "locale", err: errors.New(`ent: missing "ServiceCategoryTranslation.locale" for update`)} + } else { + _spec.Node.CompositeID[1].Value = id + } + if fields := _u.fields; len(fields) > 0 { + _spec.Node.Columns = make([]string, len(fields)) + for i, f := range fields { + if !servicecategorytranslation.ValidColumn(f) { + return nil, &ValidationError{Name: f, err: fmt.Errorf("ent: invalid field %q for query", f)} + } + _spec.Node.Columns[i] = 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(servicecategorytranslation.FieldName, field.TypeString, value) + } + if value, ok := _u.mutation.Description(); ok { + _spec.SetField(servicecategorytranslation.FieldDescription, field.TypeString, value) + } + if _u.mutation.DescriptionCleared() { + _spec.ClearField(servicecategorytranslation.FieldDescription, field.TypeString) + } + _node = &ServiceCategoryTranslation{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{servicecategorytranslation.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/backend/ent/spokenlanguage.go b/backend/ent/spokenlanguage.go new file mode 100644 index 0000000..1819b6c --- /dev/null +++ b/backend/ent/spokenlanguage.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" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/spokenlanguage" +) + +// SpokenLanguage is the model entity for the SpokenLanguage schema. +type SpokenLanguage struct { + config `json:"-"` + // ID of the ent. + ID string `json:"id,omitempty"` + // Active holds the value of the "active" field. + Active bool `json:"active,omitempty"` + // SortOrder holds the value of the "sort_order" field. + SortOrder int `json:"sort_order,omitempty"` + // Edges holds the relations/edges for other nodes in the graph. + // The values are being populated by the SpokenLanguageQuery when eager-loading is set. + Edges SpokenLanguageEdges `json:"edges"` + selectValues sql.SelectValues +} + +// SpokenLanguageEdges holds the relations/edges for other nodes in the graph. +type SpokenLanguageEdges struct { + // LocalizedIn holds the value of the localized_in edge. + LocalizedIn []*SupportedLocale `json:"localized_in,omitempty"` + // ProviderProfiles holds the value of the provider_profiles edge. + ProviderProfiles []*ProviderProfile `json:"provider_profiles,omitempty"` + // Translations holds the value of the translations edge. + Translations []*SpokenLanguageTranslation `json:"translations,omitempty"` + // loadedTypes holds the information for reporting if a + // type was loaded (or requested) in eager-loading or not. + loadedTypes [3]bool +} + +// LocalizedInOrErr returns the LocalizedIn value or an error if the edge +// was not loaded in eager-loading. +func (e SpokenLanguageEdges) LocalizedInOrErr() ([]*SupportedLocale, error) { + if e.loadedTypes[0] { + return e.LocalizedIn, nil + } + return nil, &NotLoadedError{edge: "localized_in"} +} + +// ProviderProfilesOrErr returns the ProviderProfiles value or an error if the edge +// was not loaded in eager-loading. +func (e SpokenLanguageEdges) ProviderProfilesOrErr() ([]*ProviderProfile, error) { + if e.loadedTypes[1] { + return e.ProviderProfiles, nil + } + return nil, &NotLoadedError{edge: "provider_profiles"} +} + +// TranslationsOrErr returns the Translations value or an error if the edge +// was not loaded in eager-loading. +func (e SpokenLanguageEdges) TranslationsOrErr() ([]*SpokenLanguageTranslation, error) { + if e.loadedTypes[2] { + return e.Translations, nil + } + return nil, &NotLoadedError{edge: "translations"} +} + +// scanValues returns the types for scanning values from sql.Rows. +func (*SpokenLanguage) scanValues(columns []string) ([]any, error) { + values := make([]any, len(columns)) + for i := range columns { + switch columns[i] { + case spokenlanguage.FieldActive: + values[i] = new(sql.NullBool) + case spokenlanguage.FieldSortOrder: + values[i] = new(sql.NullInt64) + case spokenlanguage.FieldID: + values[i] = new(sql.NullString) + default: + values[i] = new(sql.UnknownType) + } + } + return values, nil +} + +// assignValues assigns the values that were returned from sql.Rows (after scanning) +// to the SpokenLanguage fields. +func (_m *SpokenLanguage) 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 spokenlanguage.FieldID: + if value, ok := values[i].(*sql.NullString); !ok { + return fmt.Errorf("unexpected type %T for field id", values[i]) + } else if value.Valid { + _m.ID = value.String + } + case spokenlanguage.FieldActive: + if value, ok := values[i].(*sql.NullBool); !ok { + return fmt.Errorf("unexpected type %T for field active", values[i]) + } else if value.Valid { + _m.Active = value.Bool + } + case spokenlanguage.FieldSortOrder: + if value, ok := values[i].(*sql.NullInt64); !ok { + return fmt.Errorf("unexpected type %T for field sort_order", values[i]) + } else if value.Valid { + _m.SortOrder = 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 SpokenLanguage. +// This includes values selected through modifiers, order, etc. +func (_m *SpokenLanguage) Value(name string) (ent.Value, error) { + return _m.selectValues.Get(name) +} + +// QueryLocalizedIn queries the "localized_in" edge of the SpokenLanguage entity. +func (_m *SpokenLanguage) QueryLocalizedIn() *SupportedLocaleQuery { + return NewSpokenLanguageClient(_m.config).QueryLocalizedIn(_m) +} + +// QueryProviderProfiles queries the "provider_profiles" edge of the SpokenLanguage entity. +func (_m *SpokenLanguage) QueryProviderProfiles() *ProviderProfileQuery { + return NewSpokenLanguageClient(_m.config).QueryProviderProfiles(_m) +} + +// QueryTranslations queries the "translations" edge of the SpokenLanguage entity. +func (_m *SpokenLanguage) QueryTranslations() *SpokenLanguageTranslationQuery { + return NewSpokenLanguageClient(_m.config).QueryTranslations(_m) +} + +// Update returns a builder for updating this SpokenLanguage. +// Note that you need to call SpokenLanguage.Unwrap() before calling this method if this SpokenLanguage +// was returned from a transaction, and the transaction was committed or rolled back. +func (_m *SpokenLanguage) Update() *SpokenLanguageUpdateOne { + return NewSpokenLanguageClient(_m.config).UpdateOne(_m) +} + +// Unwrap unwraps the SpokenLanguage 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 *SpokenLanguage) Unwrap() *SpokenLanguage { + _tx, ok := _m.config.driver.(*txDriver) + if !ok { + panic("ent: SpokenLanguage is not a transactional entity") + } + _m.config.driver = _tx.drv + return _m +} + +// String implements the fmt.Stringer. +func (_m *SpokenLanguage) String() string { + var builder strings.Builder + builder.WriteString("SpokenLanguage(") + builder.WriteString(fmt.Sprintf("id=%v, ", _m.ID)) + builder.WriteString("active=") + builder.WriteString(fmt.Sprintf("%v", _m.Active)) + builder.WriteString(", ") + builder.WriteString("sort_order=") + builder.WriteString(fmt.Sprintf("%v", _m.SortOrder)) + builder.WriteByte(')') + return builder.String() +} + +// SpokenLanguages is a parsable slice of SpokenLanguage. +type SpokenLanguages []*SpokenLanguage diff --git a/backend/ent/spokenlanguage/spokenlanguage.go b/backend/ent/spokenlanguage/spokenlanguage.go new file mode 100644 index 0000000..e83767d --- /dev/null +++ b/backend/ent/spokenlanguage/spokenlanguage.go @@ -0,0 +1,162 @@ +// Code generated by ent, DO NOT EDIT. + +package spokenlanguage + +import ( + "entgo.io/ent/dialect/sql" + "entgo.io/ent/dialect/sql/sqlgraph" +) + +const ( + // Label holds the string label denoting the spokenlanguage type in the database. + Label = "spoken_language" + // FieldID holds the string denoting the id field in the database. + FieldID = "id" + // FieldActive holds the string denoting the active field in the database. + FieldActive = "active" + // FieldSortOrder holds the string denoting the sort_order field in the database. + FieldSortOrder = "sort_order" + // EdgeLocalizedIn holds the string denoting the localized_in edge name in mutations. + EdgeLocalizedIn = "localized_in" + // EdgeProviderProfiles holds the string denoting the provider_profiles edge name in mutations. + EdgeProviderProfiles = "provider_profiles" + // EdgeTranslations holds the string denoting the translations edge name in mutations. + EdgeTranslations = "translations" + // ProviderProfileFieldID holds the string denoting the ID field of the ProviderProfile. + ProviderProfileFieldID = "internal_user_id" + // Table holds the table name of the spokenlanguage in the database. + Table = "spoken_languages" + // LocalizedInTable is the table that holds the localized_in relation/edge. The primary key declared below. + LocalizedInTable = "spoken_language_translations" + // LocalizedInInverseTable is the table name for the SupportedLocale entity. + // It exists in this package in order to avoid circular dependency with the "supportedlocale" package. + LocalizedInInverseTable = "supported_locales" + // ProviderProfilesTable is the table that holds the provider_profiles relation/edge. The primary key declared below. + ProviderProfilesTable = "provider_spoken_languages" + // ProviderProfilesInverseTable is the table name for the ProviderProfile entity. + // It exists in this package in order to avoid circular dependency with the "providerprofile" package. + ProviderProfilesInverseTable = "provider_profiles" + // TranslationsTable is the table that holds the translations relation/edge. + TranslationsTable = "spoken_language_translations" + // TranslationsInverseTable is the table name for the SpokenLanguageTranslation entity. + // It exists in this package in order to avoid circular dependency with the "spokenlanguagetranslation" package. + TranslationsInverseTable = "spoken_language_translations" + // TranslationsColumn is the table column denoting the translations relation/edge. + TranslationsColumn = "language_code" +) + +// Columns holds all SQL columns for spokenlanguage fields. +var Columns = []string{ + FieldID, + FieldActive, + FieldSortOrder, +} + +var ( + // LocalizedInPrimaryKey and LocalizedInColumn2 are the table columns denoting the + // primary key for the localized_in relation (M2M). + LocalizedInPrimaryKey = []string{"language_code", "locale"} + // ProviderProfilesPrimaryKey and ProviderProfilesColumn2 are the table columns denoting the + // primary key for the provider_profiles relation (M2M). + ProviderProfilesPrimaryKey = []string{"internal_user_id", "language_code"} +) + +// 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 ( + // DefaultActive holds the default value on creation for the "active" field. + DefaultActive bool + // SortOrderValidator is a validator for the "sort_order" field. It is called by the builders before save. + SortOrderValidator func(int) error + // IDValidator is a validator for the "id" field. It is called by the builders before save. + IDValidator func(string) error +) + +// OrderOption defines the ordering options for the SpokenLanguage 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() +} + +// ByActive orders the results by the active field. +func ByActive(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldActive, opts...).ToFunc() +} + +// BySortOrder orders the results by the sort_order field. +func BySortOrder(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldSortOrder, opts...).ToFunc() +} + +// ByLocalizedInCount orders the results by localized_in count. +func ByLocalizedInCount(opts ...sql.OrderTermOption) OrderOption { + return func(s *sql.Selector) { + sqlgraph.OrderByNeighborsCount(s, newLocalizedInStep(), opts...) + } +} + +// ByLocalizedIn orders the results by localized_in terms. +func ByLocalizedIn(term sql.OrderTerm, terms ...sql.OrderTerm) OrderOption { + return func(s *sql.Selector) { + sqlgraph.OrderByNeighborTerms(s, newLocalizedInStep(), append([]sql.OrderTerm{term}, terms...)...) + } +} + +// ByProviderProfilesCount orders the results by provider_profiles count. +func ByProviderProfilesCount(opts ...sql.OrderTermOption) OrderOption { + return func(s *sql.Selector) { + sqlgraph.OrderByNeighborsCount(s, newProviderProfilesStep(), opts...) + } +} + +// ByProviderProfiles orders the results by provider_profiles terms. +func ByProviderProfiles(term sql.OrderTerm, terms ...sql.OrderTerm) OrderOption { + return func(s *sql.Selector) { + sqlgraph.OrderByNeighborTerms(s, newProviderProfilesStep(), append([]sql.OrderTerm{term}, terms...)...) + } +} + +// ByTranslationsCount orders the results by translations count. +func ByTranslationsCount(opts ...sql.OrderTermOption) OrderOption { + return func(s *sql.Selector) { + sqlgraph.OrderByNeighborsCount(s, newTranslationsStep(), opts...) + } +} + +// ByTranslations orders the results by translations terms. +func ByTranslations(term sql.OrderTerm, terms ...sql.OrderTerm) OrderOption { + return func(s *sql.Selector) { + sqlgraph.OrderByNeighborTerms(s, newTranslationsStep(), append([]sql.OrderTerm{term}, terms...)...) + } +} +func newLocalizedInStep() *sqlgraph.Step { + return sqlgraph.NewStep( + sqlgraph.From(Table, FieldID), + sqlgraph.To(LocalizedInInverseTable, FieldID), + sqlgraph.Edge(sqlgraph.M2M, false, LocalizedInTable, LocalizedInPrimaryKey...), + ) +} +func newProviderProfilesStep() *sqlgraph.Step { + return sqlgraph.NewStep( + sqlgraph.From(Table, FieldID), + sqlgraph.To(ProviderProfilesInverseTable, ProviderProfileFieldID), + sqlgraph.Edge(sqlgraph.M2M, true, ProviderProfilesTable, ProviderProfilesPrimaryKey...), + ) +} +func newTranslationsStep() *sqlgraph.Step { + return sqlgraph.NewStep( + sqlgraph.From(Table, FieldID), + sqlgraph.To(TranslationsInverseTable, TranslationsColumn), + sqlgraph.Edge(sqlgraph.O2M, true, TranslationsTable, TranslationsColumn), + ) +} diff --git a/backend/ent/spokenlanguage/where.go b/backend/ent/spokenlanguage/where.go new file mode 100644 index 0000000..5d092bd --- /dev/null +++ b/backend/ent/spokenlanguage/where.go @@ -0,0 +1,208 @@ +// Code generated by ent, DO NOT EDIT. + +package spokenlanguage + +import ( + "entgo.io/ent/dialect/sql" + "entgo.io/ent/dialect/sql/sqlgraph" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/predicate" +) + +// ID filters vertices based on their ID field. +func ID(id string) predicate.SpokenLanguage { + return predicate.SpokenLanguage(sql.FieldEQ(FieldID, id)) +} + +// IDEQ applies the EQ predicate on the ID field. +func IDEQ(id string) predicate.SpokenLanguage { + return predicate.SpokenLanguage(sql.FieldEQ(FieldID, id)) +} + +// IDNEQ applies the NEQ predicate on the ID field. +func IDNEQ(id string) predicate.SpokenLanguage { + return predicate.SpokenLanguage(sql.FieldNEQ(FieldID, id)) +} + +// IDIn applies the In predicate on the ID field. +func IDIn(ids ...string) predicate.SpokenLanguage { + return predicate.SpokenLanguage(sql.FieldIn(FieldID, ids...)) +} + +// IDNotIn applies the NotIn predicate on the ID field. +func IDNotIn(ids ...string) predicate.SpokenLanguage { + return predicate.SpokenLanguage(sql.FieldNotIn(FieldID, ids...)) +} + +// IDGT applies the GT predicate on the ID field. +func IDGT(id string) predicate.SpokenLanguage { + return predicate.SpokenLanguage(sql.FieldGT(FieldID, id)) +} + +// IDGTE applies the GTE predicate on the ID field. +func IDGTE(id string) predicate.SpokenLanguage { + return predicate.SpokenLanguage(sql.FieldGTE(FieldID, id)) +} + +// IDLT applies the LT predicate on the ID field. +func IDLT(id string) predicate.SpokenLanguage { + return predicate.SpokenLanguage(sql.FieldLT(FieldID, id)) +} + +// IDLTE applies the LTE predicate on the ID field. +func IDLTE(id string) predicate.SpokenLanguage { + return predicate.SpokenLanguage(sql.FieldLTE(FieldID, id)) +} + +// IDEqualFold applies the EqualFold predicate on the ID field. +func IDEqualFold(id string) predicate.SpokenLanguage { + return predicate.SpokenLanguage(sql.FieldEqualFold(FieldID, id)) +} + +// IDContainsFold applies the ContainsFold predicate on the ID field. +func IDContainsFold(id string) predicate.SpokenLanguage { + return predicate.SpokenLanguage(sql.FieldContainsFold(FieldID, id)) +} + +// Active applies equality check predicate on the "active" field. It's identical to ActiveEQ. +func Active(v bool) predicate.SpokenLanguage { + return predicate.SpokenLanguage(sql.FieldEQ(FieldActive, v)) +} + +// SortOrder applies equality check predicate on the "sort_order" field. It's identical to SortOrderEQ. +func SortOrder(v int) predicate.SpokenLanguage { + return predicate.SpokenLanguage(sql.FieldEQ(FieldSortOrder, v)) +} + +// ActiveEQ applies the EQ predicate on the "active" field. +func ActiveEQ(v bool) predicate.SpokenLanguage { + return predicate.SpokenLanguage(sql.FieldEQ(FieldActive, v)) +} + +// ActiveNEQ applies the NEQ predicate on the "active" field. +func ActiveNEQ(v bool) predicate.SpokenLanguage { + return predicate.SpokenLanguage(sql.FieldNEQ(FieldActive, v)) +} + +// SortOrderEQ applies the EQ predicate on the "sort_order" field. +func SortOrderEQ(v int) predicate.SpokenLanguage { + return predicate.SpokenLanguage(sql.FieldEQ(FieldSortOrder, v)) +} + +// SortOrderNEQ applies the NEQ predicate on the "sort_order" field. +func SortOrderNEQ(v int) predicate.SpokenLanguage { + return predicate.SpokenLanguage(sql.FieldNEQ(FieldSortOrder, v)) +} + +// SortOrderIn applies the In predicate on the "sort_order" field. +func SortOrderIn(vs ...int) predicate.SpokenLanguage { + return predicate.SpokenLanguage(sql.FieldIn(FieldSortOrder, vs...)) +} + +// SortOrderNotIn applies the NotIn predicate on the "sort_order" field. +func SortOrderNotIn(vs ...int) predicate.SpokenLanguage { + return predicate.SpokenLanguage(sql.FieldNotIn(FieldSortOrder, vs...)) +} + +// SortOrderGT applies the GT predicate on the "sort_order" field. +func SortOrderGT(v int) predicate.SpokenLanguage { + return predicate.SpokenLanguage(sql.FieldGT(FieldSortOrder, v)) +} + +// SortOrderGTE applies the GTE predicate on the "sort_order" field. +func SortOrderGTE(v int) predicate.SpokenLanguage { + return predicate.SpokenLanguage(sql.FieldGTE(FieldSortOrder, v)) +} + +// SortOrderLT applies the LT predicate on the "sort_order" field. +func SortOrderLT(v int) predicate.SpokenLanguage { + return predicate.SpokenLanguage(sql.FieldLT(FieldSortOrder, v)) +} + +// SortOrderLTE applies the LTE predicate on the "sort_order" field. +func SortOrderLTE(v int) predicate.SpokenLanguage { + return predicate.SpokenLanguage(sql.FieldLTE(FieldSortOrder, v)) +} + +// HasLocalizedIn applies the HasEdge predicate on the "localized_in" edge. +func HasLocalizedIn() predicate.SpokenLanguage { + return predicate.SpokenLanguage(func(s *sql.Selector) { + step := sqlgraph.NewStep( + sqlgraph.From(Table, FieldID), + sqlgraph.Edge(sqlgraph.M2M, false, LocalizedInTable, LocalizedInPrimaryKey...), + ) + sqlgraph.HasNeighbors(s, step) + }) +} + +// HasLocalizedInWith applies the HasEdge predicate on the "localized_in" edge with a given conditions (other predicates). +func HasLocalizedInWith(preds ...predicate.SupportedLocale) predicate.SpokenLanguage { + return predicate.SpokenLanguage(func(s *sql.Selector) { + step := newLocalizedInStep() + sqlgraph.HasNeighborsWith(s, step, func(s *sql.Selector) { + for _, p := range preds { + p(s) + } + }) + }) +} + +// HasProviderProfiles applies the HasEdge predicate on the "provider_profiles" edge. +func HasProviderProfiles() predicate.SpokenLanguage { + return predicate.SpokenLanguage(func(s *sql.Selector) { + step := sqlgraph.NewStep( + sqlgraph.From(Table, FieldID), + sqlgraph.Edge(sqlgraph.M2M, true, ProviderProfilesTable, ProviderProfilesPrimaryKey...), + ) + sqlgraph.HasNeighbors(s, step) + }) +} + +// HasProviderProfilesWith applies the HasEdge predicate on the "provider_profiles" edge with a given conditions (other predicates). +func HasProviderProfilesWith(preds ...predicate.ProviderProfile) predicate.SpokenLanguage { + return predicate.SpokenLanguage(func(s *sql.Selector) { + step := newProviderProfilesStep() + sqlgraph.HasNeighborsWith(s, step, func(s *sql.Selector) { + for _, p := range preds { + p(s) + } + }) + }) +} + +// HasTranslations applies the HasEdge predicate on the "translations" edge. +func HasTranslations() predicate.SpokenLanguage { + return predicate.SpokenLanguage(func(s *sql.Selector) { + step := sqlgraph.NewStep( + sqlgraph.From(Table, FieldID), + sqlgraph.Edge(sqlgraph.O2M, true, TranslationsTable, TranslationsColumn), + ) + sqlgraph.HasNeighbors(s, step) + }) +} + +// HasTranslationsWith applies the HasEdge predicate on the "translations" edge with a given conditions (other predicates). +func HasTranslationsWith(preds ...predicate.SpokenLanguageTranslation) predicate.SpokenLanguage { + return predicate.SpokenLanguage(func(s *sql.Selector) { + step := newTranslationsStep() + 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.SpokenLanguage) predicate.SpokenLanguage { + return predicate.SpokenLanguage(sql.AndPredicates(predicates...)) +} + +// Or groups predicates with the OR operator between them. +func Or(predicates ...predicate.SpokenLanguage) predicate.SpokenLanguage { + return predicate.SpokenLanguage(sql.OrPredicates(predicates...)) +} + +// Not applies the not operator on the given predicate. +func Not(p predicate.SpokenLanguage) predicate.SpokenLanguage { + return predicate.SpokenLanguage(sql.NotPredicates(p)) +} diff --git a/backend/ent/spokenlanguage_create.go b/backend/ent/spokenlanguage_create.go new file mode 100644 index 0000000..4e27c19 --- /dev/null +++ b/backend/ent/spokenlanguage_create.go @@ -0,0 +1,300 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "context" + "errors" + "fmt" + + "entgo.io/ent/dialect/sql/sqlgraph" + "entgo.io/ent/schema/field" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/providerprofile" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/spokenlanguage" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/supportedlocale" + "github.com/google/uuid" +) + +// SpokenLanguageCreate is the builder for creating a SpokenLanguage entity. +type SpokenLanguageCreate struct { + config + mutation *SpokenLanguageMutation + hooks []Hook +} + +// SetActive sets the "active" field. +func (_c *SpokenLanguageCreate) SetActive(v bool) *SpokenLanguageCreate { + _c.mutation.SetActive(v) + return _c +} + +// SetNillableActive sets the "active" field if the given value is not nil. +func (_c *SpokenLanguageCreate) SetNillableActive(v *bool) *SpokenLanguageCreate { + if v != nil { + _c.SetActive(*v) + } + return _c +} + +// SetSortOrder sets the "sort_order" field. +func (_c *SpokenLanguageCreate) SetSortOrder(v int) *SpokenLanguageCreate { + _c.mutation.SetSortOrder(v) + return _c +} + +// SetID sets the "id" field. +func (_c *SpokenLanguageCreate) SetID(v string) *SpokenLanguageCreate { + _c.mutation.SetID(v) + return _c +} + +// AddLocalizedInIDs adds the "localized_in" edge to the SupportedLocale entity by IDs. +func (_c *SpokenLanguageCreate) AddLocalizedInIDs(ids ...string) *SpokenLanguageCreate { + _c.mutation.AddLocalizedInIDs(ids...) + return _c +} + +// AddLocalizedIn adds the "localized_in" edges to the SupportedLocale entity. +func (_c *SpokenLanguageCreate) AddLocalizedIn(v ...*SupportedLocale) *SpokenLanguageCreate { + ids := make([]string, len(v)) + for i := range v { + ids[i] = v[i].ID + } + return _c.AddLocalizedInIDs(ids...) +} + +// AddProviderProfileIDs adds the "provider_profiles" edge to the ProviderProfile entity by IDs. +func (_c *SpokenLanguageCreate) AddProviderProfileIDs(ids ...uuid.UUID) *SpokenLanguageCreate { + _c.mutation.AddProviderProfileIDs(ids...) + return _c +} + +// AddProviderProfiles adds the "provider_profiles" edges to the ProviderProfile entity. +func (_c *SpokenLanguageCreate) AddProviderProfiles(v ...*ProviderProfile) *SpokenLanguageCreate { + ids := make([]uuid.UUID, len(v)) + for i := range v { + ids[i] = v[i].ID + } + return _c.AddProviderProfileIDs(ids...) +} + +// Mutation returns the SpokenLanguageMutation object of the builder. +func (_c *SpokenLanguageCreate) Mutation() *SpokenLanguageMutation { + return _c.mutation +} + +// Save creates the SpokenLanguage in the database. +func (_c *SpokenLanguageCreate) Save(ctx context.Context) (*SpokenLanguage, error) { + _c.defaults() + return withHooks(ctx, _c.sqlSave, _c.mutation, _c.hooks) +} + +// SaveX calls Save and panics if Save returns an error. +func (_c *SpokenLanguageCreate) SaveX(ctx context.Context) *SpokenLanguage { + v, err := _c.Save(ctx) + if err != nil { + panic(err) + } + return v +} + +// Exec executes the query. +func (_c *SpokenLanguageCreate) Exec(ctx context.Context) error { + _, err := _c.Save(ctx) + return err +} + +// ExecX is like Exec, but panics if an error occurs. +func (_c *SpokenLanguageCreate) 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 *SpokenLanguageCreate) defaults() { + if _, ok := _c.mutation.Active(); !ok { + v := spokenlanguage.DefaultActive + _c.mutation.SetActive(v) + } +} + +// check runs all checks and user-defined validators on the builder. +func (_c *SpokenLanguageCreate) check() error { + if _, ok := _c.mutation.Active(); !ok { + return &ValidationError{Name: "active", err: errors.New(`ent: missing required field "SpokenLanguage.active"`)} + } + if _, ok := _c.mutation.SortOrder(); !ok { + return &ValidationError{Name: "sort_order", err: errors.New(`ent: missing required field "SpokenLanguage.sort_order"`)} + } + if v, ok := _c.mutation.SortOrder(); ok { + if err := spokenlanguage.SortOrderValidator(v); err != nil { + return &ValidationError{Name: "sort_order", err: fmt.Errorf(`ent: validator failed for field "SpokenLanguage.sort_order": %w`, err)} + } + } + if v, ok := _c.mutation.ID(); ok { + if err := spokenlanguage.IDValidator(v); err != nil { + return &ValidationError{Name: "id", err: fmt.Errorf(`ent: validator failed for field "SpokenLanguage.id": %w`, err)} + } + } + return nil +} + +func (_c *SpokenLanguageCreate) sqlSave(ctx context.Context) (*SpokenLanguage, 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.(string); ok { + _node.ID = id + } else { + return nil, fmt.Errorf("unexpected SpokenLanguage.ID type: %T", _spec.ID.Value) + } + } + _c.mutation.id = &_node.ID + _c.mutation.done = true + return _node, nil +} + +func (_c *SpokenLanguageCreate) createSpec() (*SpokenLanguage, *sqlgraph.CreateSpec) { + var ( + _node = &SpokenLanguage{config: _c.config} + _spec = sqlgraph.NewCreateSpec(spokenlanguage.Table, sqlgraph.NewFieldSpec(spokenlanguage.FieldID, field.TypeString)) + ) + if id, ok := _c.mutation.ID(); ok { + _node.ID = id + _spec.ID.Value = id + } + if value, ok := _c.mutation.Active(); ok { + _spec.SetField(spokenlanguage.FieldActive, field.TypeBool, value) + _node.Active = value + } + if value, ok := _c.mutation.SortOrder(); ok { + _spec.SetField(spokenlanguage.FieldSortOrder, field.TypeInt, value) + _node.SortOrder = value + } + if nodes := _c.mutation.LocalizedInIDs(); len(nodes) > 0 { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.M2M, + Inverse: false, + Table: spokenlanguage.LocalizedInTable, + Columns: spokenlanguage.LocalizedInPrimaryKey, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(supportedlocale.FieldID, field.TypeString), + }, + } + for _, k := range nodes { + edge.Target.Nodes = append(edge.Target.Nodes, k) + } + _spec.Edges = append(_spec.Edges, edge) + } + if nodes := _c.mutation.ProviderProfilesIDs(); len(nodes) > 0 { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.M2M, + Inverse: true, + Table: spokenlanguage.ProviderProfilesTable, + Columns: spokenlanguage.ProviderProfilesPrimaryKey, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(providerprofile.FieldID, field.TypeUUID), + }, + } + for _, k := range nodes { + edge.Target.Nodes = append(edge.Target.Nodes, k) + } + _spec.Edges = append(_spec.Edges, edge) + } + return _node, _spec +} + +// SpokenLanguageCreateBulk is the builder for creating many SpokenLanguage entities in bulk. +type SpokenLanguageCreateBulk struct { + config + err error + builders []*SpokenLanguageCreate +} + +// Save creates the SpokenLanguage entities in the database. +func (_c *SpokenLanguageCreateBulk) Save(ctx context.Context) ([]*SpokenLanguage, error) { + if _c.err != nil { + return nil, _c.err + } + specs := make([]*sqlgraph.CreateSpec, len(_c.builders)) + nodes := make([]*SpokenLanguage, 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.(*SpokenLanguageMutation) + 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 *SpokenLanguageCreateBulk) SaveX(ctx context.Context) []*SpokenLanguage { + v, err := _c.Save(ctx) + if err != nil { + panic(err) + } + return v +} + +// Exec executes the query. +func (_c *SpokenLanguageCreateBulk) Exec(ctx context.Context) error { + _, err := _c.Save(ctx) + return err +} + +// ExecX is like Exec, but panics if an error occurs. +func (_c *SpokenLanguageCreateBulk) ExecX(ctx context.Context) { + if err := _c.Exec(ctx); err != nil { + panic(err) + } +} diff --git a/backend/ent/spokenlanguage_delete.go b/backend/ent/spokenlanguage_delete.go new file mode 100644 index 0000000..f5e9e75 --- /dev/null +++ b/backend/ent/spokenlanguage_delete.go @@ -0,0 +1,88 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "context" + + "entgo.io/ent/dialect/sql" + "entgo.io/ent/dialect/sql/sqlgraph" + "entgo.io/ent/schema/field" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/predicate" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/spokenlanguage" +) + +// SpokenLanguageDelete is the builder for deleting a SpokenLanguage entity. +type SpokenLanguageDelete struct { + config + hooks []Hook + mutation *SpokenLanguageMutation +} + +// Where appends a list predicates to the SpokenLanguageDelete builder. +func (_d *SpokenLanguageDelete) Where(ps ...predicate.SpokenLanguage) *SpokenLanguageDelete { + _d.mutation.Where(ps...) + return _d +} + +// Exec executes the deletion query and returns how many vertices were deleted. +func (_d *SpokenLanguageDelete) 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 *SpokenLanguageDelete) ExecX(ctx context.Context) int { + n, err := _d.Exec(ctx) + if err != nil { + panic(err) + } + return n +} + +func (_d *SpokenLanguageDelete) sqlExec(ctx context.Context) (int, error) { + _spec := sqlgraph.NewDeleteSpec(spokenlanguage.Table, sqlgraph.NewFieldSpec(spokenlanguage.FieldID, field.TypeString)) + 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 +} + +// SpokenLanguageDeleteOne is the builder for deleting a single SpokenLanguage entity. +type SpokenLanguageDeleteOne struct { + _d *SpokenLanguageDelete +} + +// Where appends a list predicates to the SpokenLanguageDelete builder. +func (_d *SpokenLanguageDeleteOne) Where(ps ...predicate.SpokenLanguage) *SpokenLanguageDeleteOne { + _d._d.mutation.Where(ps...) + return _d +} + +// Exec executes the deletion query. +func (_d *SpokenLanguageDeleteOne) Exec(ctx context.Context) error { + n, err := _d._d.Exec(ctx) + switch { + case err != nil: + return err + case n == 0: + return &NotFoundError{spokenlanguage.Label} + default: + return nil + } +} + +// ExecX is like Exec, but panics if an error occurs. +func (_d *SpokenLanguageDeleteOne) ExecX(ctx context.Context) { + if err := _d.Exec(ctx); err != nil { + panic(err) + } +} diff --git a/backend/ent/spokenlanguage_query.go b/backend/ent/spokenlanguage_query.go new file mode 100644 index 0000000..4a4a6e3 --- /dev/null +++ b/backend/ent/spokenlanguage_query.go @@ -0,0 +1,821 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "context" + "database/sql/driver" + "fmt" + "math" + + "entgo.io/ent" + "entgo.io/ent/dialect/sql" + "entgo.io/ent/dialect/sql/sqlgraph" + "entgo.io/ent/schema/field" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/predicate" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/providerprofile" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/spokenlanguage" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/spokenlanguagetranslation" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/supportedlocale" + "github.com/google/uuid" +) + +// SpokenLanguageQuery is the builder for querying SpokenLanguage entities. +type SpokenLanguageQuery struct { + config + ctx *QueryContext + order []spokenlanguage.OrderOption + inters []Interceptor + predicates []predicate.SpokenLanguage + withLocalizedIn *SupportedLocaleQuery + withProviderProfiles *ProviderProfileQuery + withTranslations *SpokenLanguageTranslationQuery + // intermediate query (i.e. traversal path). + sql *sql.Selector + path func(context.Context) (*sql.Selector, error) +} + +// Where adds a new predicate for the SpokenLanguageQuery builder. +func (_q *SpokenLanguageQuery) Where(ps ...predicate.SpokenLanguage) *SpokenLanguageQuery { + _q.predicates = append(_q.predicates, ps...) + return _q +} + +// Limit the number of records to be returned by this query. +func (_q *SpokenLanguageQuery) Limit(limit int) *SpokenLanguageQuery { + _q.ctx.Limit = &limit + return _q +} + +// Offset to start from. +func (_q *SpokenLanguageQuery) Offset(offset int) *SpokenLanguageQuery { + _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 *SpokenLanguageQuery) Unique(unique bool) *SpokenLanguageQuery { + _q.ctx.Unique = &unique + return _q +} + +// Order specifies how the records should be ordered. +func (_q *SpokenLanguageQuery) Order(o ...spokenlanguage.OrderOption) *SpokenLanguageQuery { + _q.order = append(_q.order, o...) + return _q +} + +// QueryLocalizedIn chains the current query on the "localized_in" edge. +func (_q *SpokenLanguageQuery) QueryLocalizedIn() *SupportedLocaleQuery { + query := (&SupportedLocaleClient{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(spokenlanguage.Table, spokenlanguage.FieldID, selector), + sqlgraph.To(supportedlocale.Table, supportedlocale.FieldID), + sqlgraph.Edge(sqlgraph.M2M, false, spokenlanguage.LocalizedInTable, spokenlanguage.LocalizedInPrimaryKey...), + ) + fromU = sqlgraph.SetNeighbors(_q.driver.Dialect(), step) + return fromU, nil + } + return query +} + +// QueryProviderProfiles chains the current query on the "provider_profiles" edge. +func (_q *SpokenLanguageQuery) QueryProviderProfiles() *ProviderProfileQuery { + query := (&ProviderProfileClient{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(spokenlanguage.Table, spokenlanguage.FieldID, selector), + sqlgraph.To(providerprofile.Table, providerprofile.FieldID), + sqlgraph.Edge(sqlgraph.M2M, true, spokenlanguage.ProviderProfilesTable, spokenlanguage.ProviderProfilesPrimaryKey...), + ) + fromU = sqlgraph.SetNeighbors(_q.driver.Dialect(), step) + return fromU, nil + } + return query +} + +// QueryTranslations chains the current query on the "translations" edge. +func (_q *SpokenLanguageQuery) QueryTranslations() *SpokenLanguageTranslationQuery { + query := (&SpokenLanguageTranslationClient{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(spokenlanguage.Table, spokenlanguage.FieldID, selector), + sqlgraph.To(spokenlanguagetranslation.Table, spokenlanguagetranslation.LanguageColumn), + sqlgraph.Edge(sqlgraph.O2M, true, spokenlanguage.TranslationsTable, spokenlanguage.TranslationsColumn), + ) + fromU = sqlgraph.SetNeighbors(_q.driver.Dialect(), step) + return fromU, nil + } + return query +} + +// First returns the first SpokenLanguage entity from the query. +// Returns a *NotFoundError when no SpokenLanguage was found. +func (_q *SpokenLanguageQuery) First(ctx context.Context) (*SpokenLanguage, 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{spokenlanguage.Label} + } + return nodes[0], nil +} + +// FirstX is like First, but panics if an error occurs. +func (_q *SpokenLanguageQuery) FirstX(ctx context.Context) *SpokenLanguage { + node, err := _q.First(ctx) + if err != nil && !IsNotFound(err) { + panic(err) + } + return node +} + +// FirstID returns the first SpokenLanguage ID from the query. +// Returns a *NotFoundError when no SpokenLanguage ID was found. +func (_q *SpokenLanguageQuery) FirstID(ctx context.Context) (id string, err error) { + var ids []string + if ids, err = _q.Limit(1).IDs(setContextOp(ctx, _q.ctx, ent.OpQueryFirstID)); err != nil { + return + } + if len(ids) == 0 { + err = &NotFoundError{spokenlanguage.Label} + return + } + return ids[0], nil +} + +// FirstIDX is like FirstID, but panics if an error occurs. +func (_q *SpokenLanguageQuery) FirstIDX(ctx context.Context) string { + id, err := _q.FirstID(ctx) + if err != nil && !IsNotFound(err) { + panic(err) + } + return id +} + +// Only returns a single SpokenLanguage entity found by the query, ensuring it only returns one. +// Returns a *NotSingularError when more than one SpokenLanguage entity is found. +// Returns a *NotFoundError when no SpokenLanguage entities are found. +func (_q *SpokenLanguageQuery) Only(ctx context.Context) (*SpokenLanguage, 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{spokenlanguage.Label} + default: + return nil, &NotSingularError{spokenlanguage.Label} + } +} + +// OnlyX is like Only, but panics if an error occurs. +func (_q *SpokenLanguageQuery) OnlyX(ctx context.Context) *SpokenLanguage { + node, err := _q.Only(ctx) + if err != nil { + panic(err) + } + return node +} + +// OnlyID is like Only, but returns the only SpokenLanguage ID in the query. +// Returns a *NotSingularError when more than one SpokenLanguage ID is found. +// Returns a *NotFoundError when no entities are found. +func (_q *SpokenLanguageQuery) OnlyID(ctx context.Context) (id string, err error) { + var ids []string + 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{spokenlanguage.Label} + default: + err = &NotSingularError{spokenlanguage.Label} + } + return +} + +// OnlyIDX is like OnlyID, but panics if an error occurs. +func (_q *SpokenLanguageQuery) OnlyIDX(ctx context.Context) string { + id, err := _q.OnlyID(ctx) + if err != nil { + panic(err) + } + return id +} + +// All executes the query and returns a list of SpokenLanguages. +func (_q *SpokenLanguageQuery) All(ctx context.Context) ([]*SpokenLanguage, error) { + ctx = setContextOp(ctx, _q.ctx, ent.OpQueryAll) + if err := _q.prepareQuery(ctx); err != nil { + return nil, err + } + qr := querierAll[[]*SpokenLanguage, *SpokenLanguageQuery]() + return withInterceptors[[]*SpokenLanguage](ctx, _q, qr, _q.inters) +} + +// AllX is like All, but panics if an error occurs. +func (_q *SpokenLanguageQuery) AllX(ctx context.Context) []*SpokenLanguage { + nodes, err := _q.All(ctx) + if err != nil { + panic(err) + } + return nodes +} + +// IDs executes the query and returns a list of SpokenLanguage IDs. +func (_q *SpokenLanguageQuery) IDs(ctx context.Context) (ids []string, err error) { + if _q.ctx.Unique == nil && _q.path != nil { + _q.Unique(true) + } + ctx = setContextOp(ctx, _q.ctx, ent.OpQueryIDs) + if err = _q.Select(spokenlanguage.FieldID).Scan(ctx, &ids); err != nil { + return nil, err + } + return ids, nil +} + +// IDsX is like IDs, but panics if an error occurs. +func (_q *SpokenLanguageQuery) IDsX(ctx context.Context) []string { + ids, err := _q.IDs(ctx) + if err != nil { + panic(err) + } + return ids +} + +// Count returns the count of the given query. +func (_q *SpokenLanguageQuery) 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[*SpokenLanguageQuery](), _q.inters) +} + +// CountX is like Count, but panics if an error occurs. +func (_q *SpokenLanguageQuery) 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 *SpokenLanguageQuery) 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 *SpokenLanguageQuery) ExistX(ctx context.Context) bool { + exist, err := _q.Exist(ctx) + if err != nil { + panic(err) + } + return exist +} + +// Clone returns a duplicate of the SpokenLanguageQuery 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 *SpokenLanguageQuery) Clone() *SpokenLanguageQuery { + if _q == nil { + return nil + } + return &SpokenLanguageQuery{ + config: _q.config, + ctx: _q.ctx.Clone(), + order: append([]spokenlanguage.OrderOption{}, _q.order...), + inters: append([]Interceptor{}, _q.inters...), + predicates: append([]predicate.SpokenLanguage{}, _q.predicates...), + withLocalizedIn: _q.withLocalizedIn.Clone(), + withProviderProfiles: _q.withProviderProfiles.Clone(), + withTranslations: _q.withTranslations.Clone(), + // clone intermediate query. + sql: _q.sql.Clone(), + path: _q.path, + } +} + +// WithLocalizedIn tells the query-builder to eager-load the nodes that are connected to +// the "localized_in" edge. The optional arguments are used to configure the query builder of the edge. +func (_q *SpokenLanguageQuery) WithLocalizedIn(opts ...func(*SupportedLocaleQuery)) *SpokenLanguageQuery { + query := (&SupportedLocaleClient{config: _q.config}).Query() + for _, opt := range opts { + opt(query) + } + _q.withLocalizedIn = query + return _q +} + +// WithProviderProfiles tells the query-builder to eager-load the nodes that are connected to +// the "provider_profiles" edge. The optional arguments are used to configure the query builder of the edge. +func (_q *SpokenLanguageQuery) WithProviderProfiles(opts ...func(*ProviderProfileQuery)) *SpokenLanguageQuery { + query := (&ProviderProfileClient{config: _q.config}).Query() + for _, opt := range opts { + opt(query) + } + _q.withProviderProfiles = query + return _q +} + +// WithTranslations tells the query-builder to eager-load the nodes that are connected to +// the "translations" edge. The optional arguments are used to configure the query builder of the edge. +func (_q *SpokenLanguageQuery) WithTranslations(opts ...func(*SpokenLanguageTranslationQuery)) *SpokenLanguageQuery { + query := (&SpokenLanguageTranslationClient{config: _q.config}).Query() + for _, opt := range opts { + opt(query) + } + _q.withTranslations = 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 { +// Active bool `json:"active,omitempty"` +// Count int `json:"count,omitempty"` +// } +// +// client.SpokenLanguage.Query(). +// GroupBy(spokenlanguage.FieldActive). +// Aggregate(ent.Count()). +// Scan(ctx, &v) +func (_q *SpokenLanguageQuery) GroupBy(field string, fields ...string) *SpokenLanguageGroupBy { + _q.ctx.Fields = append([]string{field}, fields...) + grbuild := &SpokenLanguageGroupBy{build: _q} + grbuild.flds = &_q.ctx.Fields + grbuild.label = spokenlanguage.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 { +// Active bool `json:"active,omitempty"` +// } +// +// client.SpokenLanguage.Query(). +// Select(spokenlanguage.FieldActive). +// Scan(ctx, &v) +func (_q *SpokenLanguageQuery) Select(fields ...string) *SpokenLanguageSelect { + _q.ctx.Fields = append(_q.ctx.Fields, fields...) + sbuild := &SpokenLanguageSelect{SpokenLanguageQuery: _q} + sbuild.label = spokenlanguage.Label + sbuild.flds, sbuild.scan = &_q.ctx.Fields, sbuild.Scan + return sbuild +} + +// Aggregate returns a SpokenLanguageSelect configured with the given aggregations. +func (_q *SpokenLanguageQuery) Aggregate(fns ...AggregateFunc) *SpokenLanguageSelect { + return _q.Select().Aggregate(fns...) +} + +func (_q *SpokenLanguageQuery) 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 !spokenlanguage.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 *SpokenLanguageQuery) sqlAll(ctx context.Context, hooks ...queryHook) ([]*SpokenLanguage, error) { + var ( + nodes = []*SpokenLanguage{} + _spec = _q.querySpec() + loadedTypes = [3]bool{ + _q.withLocalizedIn != nil, + _q.withProviderProfiles != nil, + _q.withTranslations != nil, + } + ) + _spec.ScanValues = func(columns []string) ([]any, error) { + return (*SpokenLanguage).scanValues(nil, columns) + } + _spec.Assign = func(columns []string, values []any) error { + node := &SpokenLanguage{config: _q.config} + nodes = append(nodes, node) + node.Edges.loadedTypes = loadedTypes + return node.assignValues(columns, values) + } + 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.withLocalizedIn; query != nil { + if err := _q.loadLocalizedIn(ctx, query, nodes, + func(n *SpokenLanguage) { n.Edges.LocalizedIn = []*SupportedLocale{} }, + func(n *SpokenLanguage, e *SupportedLocale) { n.Edges.LocalizedIn = append(n.Edges.LocalizedIn, e) }); err != nil { + return nil, err + } + } + if query := _q.withProviderProfiles; query != nil { + if err := _q.loadProviderProfiles(ctx, query, nodes, + func(n *SpokenLanguage) { n.Edges.ProviderProfiles = []*ProviderProfile{} }, + func(n *SpokenLanguage, e *ProviderProfile) { + n.Edges.ProviderProfiles = append(n.Edges.ProviderProfiles, e) + }); err != nil { + return nil, err + } + } + if query := _q.withTranslations; query != nil { + if err := _q.loadTranslations(ctx, query, nodes, + func(n *SpokenLanguage) { n.Edges.Translations = []*SpokenLanguageTranslation{} }, + func(n *SpokenLanguage, e *SpokenLanguageTranslation) { + n.Edges.Translations = append(n.Edges.Translations, e) + }); err != nil { + return nil, err + } + } + return nodes, nil +} + +func (_q *SpokenLanguageQuery) loadLocalizedIn(ctx context.Context, query *SupportedLocaleQuery, nodes []*SpokenLanguage, init func(*SpokenLanguage), assign func(*SpokenLanguage, *SupportedLocale)) error { + edgeIDs := make([]driver.Value, len(nodes)) + byID := make(map[string]*SpokenLanguage) + nids := make(map[string]map[*SpokenLanguage]struct{}) + for i, node := range nodes { + edgeIDs[i] = node.ID + byID[node.ID] = node + if init != nil { + init(node) + } + } + query.Where(func(s *sql.Selector) { + joinT := sql.Table(spokenlanguage.LocalizedInTable) + s.Join(joinT).On(s.C(supportedlocale.FieldID), joinT.C(spokenlanguage.LocalizedInPrimaryKey[1])) + s.Where(sql.InValues(joinT.C(spokenlanguage.LocalizedInPrimaryKey[0]), edgeIDs...)) + columns := s.SelectedColumns() + s.Select(joinT.C(spokenlanguage.LocalizedInPrimaryKey[0])) + s.AppendSelect(columns...) + s.SetDistinct(false) + }) + if err := query.prepareQuery(ctx); err != nil { + return err + } + qr := QuerierFunc(func(ctx context.Context, q Query) (Value, error) { + return query.sqlAll(ctx, func(_ context.Context, spec *sqlgraph.QuerySpec) { + assign := spec.Assign + values := spec.ScanValues + spec.ScanValues = func(columns []string) ([]any, error) { + values, err := values(columns[1:]) + if err != nil { + return nil, err + } + return append([]any{new(sql.NullString)}, values...), nil + } + spec.Assign = func(columns []string, values []any) error { + outValue := values[0].(*sql.NullString).String + inValue := values[1].(*sql.NullString).String + if nids[inValue] == nil { + nids[inValue] = map[*SpokenLanguage]struct{}{byID[outValue]: {}} + return assign(columns[1:], values[1:]) + } + nids[inValue][byID[outValue]] = struct{}{} + return nil + } + }) + }) + neighbors, err := withInterceptors[[]*SupportedLocale](ctx, query, qr, query.inters) + if err != nil { + return err + } + for _, n := range neighbors { + nodes, ok := nids[n.ID] + if !ok { + return fmt.Errorf(`unexpected "localized_in" node returned %v`, n.ID) + } + for kn := range nodes { + assign(kn, n) + } + } + return nil +} +func (_q *SpokenLanguageQuery) loadProviderProfiles(ctx context.Context, query *ProviderProfileQuery, nodes []*SpokenLanguage, init func(*SpokenLanguage), assign func(*SpokenLanguage, *ProviderProfile)) error { + edgeIDs := make([]driver.Value, len(nodes)) + byID := make(map[string]*SpokenLanguage) + nids := make(map[uuid.UUID]map[*SpokenLanguage]struct{}) + for i, node := range nodes { + edgeIDs[i] = node.ID + byID[node.ID] = node + if init != nil { + init(node) + } + } + query.Where(func(s *sql.Selector) { + joinT := sql.Table(spokenlanguage.ProviderProfilesTable) + s.Join(joinT).On(s.C(providerprofile.FieldID), joinT.C(spokenlanguage.ProviderProfilesPrimaryKey[0])) + s.Where(sql.InValues(joinT.C(spokenlanguage.ProviderProfilesPrimaryKey[1]), edgeIDs...)) + columns := s.SelectedColumns() + s.Select(joinT.C(spokenlanguage.ProviderProfilesPrimaryKey[1])) + s.AppendSelect(columns...) + s.SetDistinct(false) + }) + if err := query.prepareQuery(ctx); err != nil { + return err + } + qr := QuerierFunc(func(ctx context.Context, q Query) (Value, error) { + return query.sqlAll(ctx, func(_ context.Context, spec *sqlgraph.QuerySpec) { + assign := spec.Assign + values := spec.ScanValues + spec.ScanValues = func(columns []string) ([]any, error) { + values, err := values(columns[1:]) + if err != nil { + return nil, err + } + return append([]any{new(sql.NullString)}, values...), nil + } + spec.Assign = func(columns []string, values []any) error { + outValue := values[0].(*sql.NullString).String + inValue := *values[1].(*uuid.UUID) + if nids[inValue] == nil { + nids[inValue] = map[*SpokenLanguage]struct{}{byID[outValue]: {}} + return assign(columns[1:], values[1:]) + } + nids[inValue][byID[outValue]] = struct{}{} + return nil + } + }) + }) + neighbors, err := withInterceptors[[]*ProviderProfile](ctx, query, qr, query.inters) + if err != nil { + return err + } + for _, n := range neighbors { + nodes, ok := nids[n.ID] + if !ok { + return fmt.Errorf(`unexpected "provider_profiles" node returned %v`, n.ID) + } + for kn := range nodes { + assign(kn, n) + } + } + return nil +} +func (_q *SpokenLanguageQuery) loadTranslations(ctx context.Context, query *SpokenLanguageTranslationQuery, nodes []*SpokenLanguage, init func(*SpokenLanguage), assign func(*SpokenLanguage, *SpokenLanguageTranslation)) error { + fks := make([]driver.Value, 0, len(nodes)) + nodeids := make(map[string]*SpokenLanguage) + 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(spokenlanguagetranslation.FieldLanguageCode) + } + query.Where(predicate.SpokenLanguageTranslation(func(s *sql.Selector) { + s.Where(sql.InValues(s.C(spokenlanguage.TranslationsColumn), fks...)) + })) + neighbors, err := query.All(ctx) + if err != nil { + return err + } + for _, n := range neighbors { + fk := n.LanguageCode + node, ok := nodeids[fk] + if !ok { + return fmt.Errorf(`unexpected referenced foreign-key "language_code" returned %v for node %v`, fk, n) + } + assign(node, n) + } + return nil +} + +func (_q *SpokenLanguageQuery) sqlCount(ctx context.Context) (int, error) { + _spec := _q.querySpec() + _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 *SpokenLanguageQuery) querySpec() *sqlgraph.QuerySpec { + _spec := sqlgraph.NewQuerySpec(spokenlanguage.Table, spokenlanguage.Columns, sqlgraph.NewFieldSpec(spokenlanguage.FieldID, field.TypeString)) + _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, spokenlanguage.FieldID) + for i := range fields { + if fields[i] != spokenlanguage.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 *SpokenLanguageQuery) sqlQuery(ctx context.Context) *sql.Selector { + builder := sql.Dialect(_q.driver.Dialect()) + t1 := builder.Table(spokenlanguage.Table) + columns := _q.ctx.Fields + if len(columns) == 0 { + columns = spokenlanguage.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 _, 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 +} + +// SpokenLanguageGroupBy is the group-by builder for SpokenLanguage entities. +type SpokenLanguageGroupBy struct { + selector + build *SpokenLanguageQuery +} + +// Aggregate adds the given aggregation functions to the group-by query. +func (_g *SpokenLanguageGroupBy) Aggregate(fns ...AggregateFunc) *SpokenLanguageGroupBy { + _g.fns = append(_g.fns, fns...) + return _g +} + +// Scan applies the selector query and scans the result into the given value. +func (_g *SpokenLanguageGroupBy) 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[*SpokenLanguageQuery, *SpokenLanguageGroupBy](ctx, _g.build, _g, _g.build.inters, v) +} + +func (_g *SpokenLanguageGroupBy) sqlScan(ctx context.Context, root *SpokenLanguageQuery, 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) +} + +// SpokenLanguageSelect is the builder for selecting fields of SpokenLanguage entities. +type SpokenLanguageSelect struct { + *SpokenLanguageQuery + selector +} + +// Aggregate adds the given aggregation functions to the selector query. +func (_s *SpokenLanguageSelect) Aggregate(fns ...AggregateFunc) *SpokenLanguageSelect { + _s.fns = append(_s.fns, fns...) + return _s +} + +// Scan applies the selector query and scans the result into the given value. +func (_s *SpokenLanguageSelect) 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[*SpokenLanguageQuery, *SpokenLanguageSelect](ctx, _s.SpokenLanguageQuery, _s, _s.inters, v) +} + +func (_s *SpokenLanguageSelect) sqlScan(ctx context.Context, root *SpokenLanguageQuery, 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/backend/ent/spokenlanguage_update.go b/backend/ent/spokenlanguage_update.go new file mode 100644 index 0000000..e9f19fd --- /dev/null +++ b/backend/ent/spokenlanguage_update.go @@ -0,0 +1,616 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "context" + "errors" + "fmt" + + "entgo.io/ent/dialect/sql" + "entgo.io/ent/dialect/sql/sqlgraph" + "entgo.io/ent/schema/field" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/predicate" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/providerprofile" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/spokenlanguage" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/supportedlocale" + "github.com/google/uuid" +) + +// SpokenLanguageUpdate is the builder for updating SpokenLanguage entities. +type SpokenLanguageUpdate struct { + config + hooks []Hook + mutation *SpokenLanguageMutation +} + +// Where appends a list predicates to the SpokenLanguageUpdate builder. +func (_u *SpokenLanguageUpdate) Where(ps ...predicate.SpokenLanguage) *SpokenLanguageUpdate { + _u.mutation.Where(ps...) + return _u +} + +// SetActive sets the "active" field. +func (_u *SpokenLanguageUpdate) SetActive(v bool) *SpokenLanguageUpdate { + _u.mutation.SetActive(v) + return _u +} + +// SetNillableActive sets the "active" field if the given value is not nil. +func (_u *SpokenLanguageUpdate) SetNillableActive(v *bool) *SpokenLanguageUpdate { + if v != nil { + _u.SetActive(*v) + } + return _u +} + +// SetSortOrder sets the "sort_order" field. +func (_u *SpokenLanguageUpdate) SetSortOrder(v int) *SpokenLanguageUpdate { + _u.mutation.ResetSortOrder() + _u.mutation.SetSortOrder(v) + return _u +} + +// SetNillableSortOrder sets the "sort_order" field if the given value is not nil. +func (_u *SpokenLanguageUpdate) SetNillableSortOrder(v *int) *SpokenLanguageUpdate { + if v != nil { + _u.SetSortOrder(*v) + } + return _u +} + +// AddSortOrder adds value to the "sort_order" field. +func (_u *SpokenLanguageUpdate) AddSortOrder(v int) *SpokenLanguageUpdate { + _u.mutation.AddSortOrder(v) + return _u +} + +// AddLocalizedInIDs adds the "localized_in" edge to the SupportedLocale entity by IDs. +func (_u *SpokenLanguageUpdate) AddLocalizedInIDs(ids ...string) *SpokenLanguageUpdate { + _u.mutation.AddLocalizedInIDs(ids...) + return _u +} + +// AddLocalizedIn adds the "localized_in" edges to the SupportedLocale entity. +func (_u *SpokenLanguageUpdate) AddLocalizedIn(v ...*SupportedLocale) *SpokenLanguageUpdate { + ids := make([]string, len(v)) + for i := range v { + ids[i] = v[i].ID + } + return _u.AddLocalizedInIDs(ids...) +} + +// AddProviderProfileIDs adds the "provider_profiles" edge to the ProviderProfile entity by IDs. +func (_u *SpokenLanguageUpdate) AddProviderProfileIDs(ids ...uuid.UUID) *SpokenLanguageUpdate { + _u.mutation.AddProviderProfileIDs(ids...) + return _u +} + +// AddProviderProfiles adds the "provider_profiles" edges to the ProviderProfile entity. +func (_u *SpokenLanguageUpdate) AddProviderProfiles(v ...*ProviderProfile) *SpokenLanguageUpdate { + ids := make([]uuid.UUID, len(v)) + for i := range v { + ids[i] = v[i].ID + } + return _u.AddProviderProfileIDs(ids...) +} + +// Mutation returns the SpokenLanguageMutation object of the builder. +func (_u *SpokenLanguageUpdate) Mutation() *SpokenLanguageMutation { + return _u.mutation +} + +// ClearLocalizedIn clears all "localized_in" edges to the SupportedLocale entity. +func (_u *SpokenLanguageUpdate) ClearLocalizedIn() *SpokenLanguageUpdate { + _u.mutation.ClearLocalizedIn() + return _u +} + +// RemoveLocalizedInIDs removes the "localized_in" edge to SupportedLocale entities by IDs. +func (_u *SpokenLanguageUpdate) RemoveLocalizedInIDs(ids ...string) *SpokenLanguageUpdate { + _u.mutation.RemoveLocalizedInIDs(ids...) + return _u +} + +// RemoveLocalizedIn removes "localized_in" edges to SupportedLocale entities. +func (_u *SpokenLanguageUpdate) RemoveLocalizedIn(v ...*SupportedLocale) *SpokenLanguageUpdate { + ids := make([]string, len(v)) + for i := range v { + ids[i] = v[i].ID + } + return _u.RemoveLocalizedInIDs(ids...) +} + +// ClearProviderProfiles clears all "provider_profiles" edges to the ProviderProfile entity. +func (_u *SpokenLanguageUpdate) ClearProviderProfiles() *SpokenLanguageUpdate { + _u.mutation.ClearProviderProfiles() + return _u +} + +// RemoveProviderProfileIDs removes the "provider_profiles" edge to ProviderProfile entities by IDs. +func (_u *SpokenLanguageUpdate) RemoveProviderProfileIDs(ids ...uuid.UUID) *SpokenLanguageUpdate { + _u.mutation.RemoveProviderProfileIDs(ids...) + return _u +} + +// RemoveProviderProfiles removes "provider_profiles" edges to ProviderProfile entities. +func (_u *SpokenLanguageUpdate) RemoveProviderProfiles(v ...*ProviderProfile) *SpokenLanguageUpdate { + ids := make([]uuid.UUID, len(v)) + for i := range v { + ids[i] = v[i].ID + } + return _u.RemoveProviderProfileIDs(ids...) +} + +// Save executes the query and returns the number of nodes affected by the update operation. +func (_u *SpokenLanguageUpdate) 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 *SpokenLanguageUpdate) SaveX(ctx context.Context) int { + affected, err := _u.Save(ctx) + if err != nil { + panic(err) + } + return affected +} + +// Exec executes the query. +func (_u *SpokenLanguageUpdate) Exec(ctx context.Context) error { + _, err := _u.Save(ctx) + return err +} + +// ExecX is like Exec, but panics if an error occurs. +func (_u *SpokenLanguageUpdate) 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 *SpokenLanguageUpdate) check() error { + if v, ok := _u.mutation.SortOrder(); ok { + if err := spokenlanguage.SortOrderValidator(v); err != nil { + return &ValidationError{Name: "sort_order", err: fmt.Errorf(`ent: validator failed for field "SpokenLanguage.sort_order": %w`, err)} + } + } + return nil +} + +func (_u *SpokenLanguageUpdate) sqlSave(ctx context.Context) (_node int, err error) { + if err := _u.check(); err != nil { + return _node, err + } + _spec := sqlgraph.NewUpdateSpec(spokenlanguage.Table, spokenlanguage.Columns, sqlgraph.NewFieldSpec(spokenlanguage.FieldID, field.TypeString)) + 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.Active(); ok { + _spec.SetField(spokenlanguage.FieldActive, field.TypeBool, value) + } + if value, ok := _u.mutation.SortOrder(); ok { + _spec.SetField(spokenlanguage.FieldSortOrder, field.TypeInt, value) + } + if value, ok := _u.mutation.AddedSortOrder(); ok { + _spec.AddField(spokenlanguage.FieldSortOrder, field.TypeInt, value) + } + if _u.mutation.LocalizedInCleared() { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.M2M, + Inverse: false, + Table: spokenlanguage.LocalizedInTable, + Columns: spokenlanguage.LocalizedInPrimaryKey, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(supportedlocale.FieldID, field.TypeString), + }, + } + _spec.Edges.Clear = append(_spec.Edges.Clear, edge) + } + if nodes := _u.mutation.RemovedLocalizedInIDs(); len(nodes) > 0 && !_u.mutation.LocalizedInCleared() { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.M2M, + Inverse: false, + Table: spokenlanguage.LocalizedInTable, + Columns: spokenlanguage.LocalizedInPrimaryKey, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(supportedlocale.FieldID, field.TypeString), + }, + } + for _, k := range nodes { + edge.Target.Nodes = append(edge.Target.Nodes, k) + } + _spec.Edges.Clear = append(_spec.Edges.Clear, edge) + } + if nodes := _u.mutation.LocalizedInIDs(); len(nodes) > 0 { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.M2M, + Inverse: false, + Table: spokenlanguage.LocalizedInTable, + Columns: spokenlanguage.LocalizedInPrimaryKey, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(supportedlocale.FieldID, field.TypeString), + }, + } + for _, k := range nodes { + edge.Target.Nodes = append(edge.Target.Nodes, k) + } + _spec.Edges.Add = append(_spec.Edges.Add, edge) + } + if _u.mutation.ProviderProfilesCleared() { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.M2M, + Inverse: true, + Table: spokenlanguage.ProviderProfilesTable, + Columns: spokenlanguage.ProviderProfilesPrimaryKey, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(providerprofile.FieldID, field.TypeUUID), + }, + } + _spec.Edges.Clear = append(_spec.Edges.Clear, edge) + } + if nodes := _u.mutation.RemovedProviderProfilesIDs(); len(nodes) > 0 && !_u.mutation.ProviderProfilesCleared() { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.M2M, + Inverse: true, + Table: spokenlanguage.ProviderProfilesTable, + Columns: spokenlanguage.ProviderProfilesPrimaryKey, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(providerprofile.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.ProviderProfilesIDs(); len(nodes) > 0 { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.M2M, + Inverse: true, + Table: spokenlanguage.ProviderProfilesTable, + Columns: spokenlanguage.ProviderProfilesPrimaryKey, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(providerprofile.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{spokenlanguage.Label} + } else if sqlgraph.IsConstraintError(err) { + err = &ConstraintError{msg: err.Error(), wrap: err} + } + return 0, err + } + _u.mutation.done = true + return _node, nil +} + +// SpokenLanguageUpdateOne is the builder for updating a single SpokenLanguage entity. +type SpokenLanguageUpdateOne struct { + config + fields []string + hooks []Hook + mutation *SpokenLanguageMutation +} + +// SetActive sets the "active" field. +func (_u *SpokenLanguageUpdateOne) SetActive(v bool) *SpokenLanguageUpdateOne { + _u.mutation.SetActive(v) + return _u +} + +// SetNillableActive sets the "active" field if the given value is not nil. +func (_u *SpokenLanguageUpdateOne) SetNillableActive(v *bool) *SpokenLanguageUpdateOne { + if v != nil { + _u.SetActive(*v) + } + return _u +} + +// SetSortOrder sets the "sort_order" field. +func (_u *SpokenLanguageUpdateOne) SetSortOrder(v int) *SpokenLanguageUpdateOne { + _u.mutation.ResetSortOrder() + _u.mutation.SetSortOrder(v) + return _u +} + +// SetNillableSortOrder sets the "sort_order" field if the given value is not nil. +func (_u *SpokenLanguageUpdateOne) SetNillableSortOrder(v *int) *SpokenLanguageUpdateOne { + if v != nil { + _u.SetSortOrder(*v) + } + return _u +} + +// AddSortOrder adds value to the "sort_order" field. +func (_u *SpokenLanguageUpdateOne) AddSortOrder(v int) *SpokenLanguageUpdateOne { + _u.mutation.AddSortOrder(v) + return _u +} + +// AddLocalizedInIDs adds the "localized_in" edge to the SupportedLocale entity by IDs. +func (_u *SpokenLanguageUpdateOne) AddLocalizedInIDs(ids ...string) *SpokenLanguageUpdateOne { + _u.mutation.AddLocalizedInIDs(ids...) + return _u +} + +// AddLocalizedIn adds the "localized_in" edges to the SupportedLocale entity. +func (_u *SpokenLanguageUpdateOne) AddLocalizedIn(v ...*SupportedLocale) *SpokenLanguageUpdateOne { + ids := make([]string, len(v)) + for i := range v { + ids[i] = v[i].ID + } + return _u.AddLocalizedInIDs(ids...) +} + +// AddProviderProfileIDs adds the "provider_profiles" edge to the ProviderProfile entity by IDs. +func (_u *SpokenLanguageUpdateOne) AddProviderProfileIDs(ids ...uuid.UUID) *SpokenLanguageUpdateOne { + _u.mutation.AddProviderProfileIDs(ids...) + return _u +} + +// AddProviderProfiles adds the "provider_profiles" edges to the ProviderProfile entity. +func (_u *SpokenLanguageUpdateOne) AddProviderProfiles(v ...*ProviderProfile) *SpokenLanguageUpdateOne { + ids := make([]uuid.UUID, len(v)) + for i := range v { + ids[i] = v[i].ID + } + return _u.AddProviderProfileIDs(ids...) +} + +// Mutation returns the SpokenLanguageMutation object of the builder. +func (_u *SpokenLanguageUpdateOne) Mutation() *SpokenLanguageMutation { + return _u.mutation +} + +// ClearLocalizedIn clears all "localized_in" edges to the SupportedLocale entity. +func (_u *SpokenLanguageUpdateOne) ClearLocalizedIn() *SpokenLanguageUpdateOne { + _u.mutation.ClearLocalizedIn() + return _u +} + +// RemoveLocalizedInIDs removes the "localized_in" edge to SupportedLocale entities by IDs. +func (_u *SpokenLanguageUpdateOne) RemoveLocalizedInIDs(ids ...string) *SpokenLanguageUpdateOne { + _u.mutation.RemoveLocalizedInIDs(ids...) + return _u +} + +// RemoveLocalizedIn removes "localized_in" edges to SupportedLocale entities. +func (_u *SpokenLanguageUpdateOne) RemoveLocalizedIn(v ...*SupportedLocale) *SpokenLanguageUpdateOne { + ids := make([]string, len(v)) + for i := range v { + ids[i] = v[i].ID + } + return _u.RemoveLocalizedInIDs(ids...) +} + +// ClearProviderProfiles clears all "provider_profiles" edges to the ProviderProfile entity. +func (_u *SpokenLanguageUpdateOne) ClearProviderProfiles() *SpokenLanguageUpdateOne { + _u.mutation.ClearProviderProfiles() + return _u +} + +// RemoveProviderProfileIDs removes the "provider_profiles" edge to ProviderProfile entities by IDs. +func (_u *SpokenLanguageUpdateOne) RemoveProviderProfileIDs(ids ...uuid.UUID) *SpokenLanguageUpdateOne { + _u.mutation.RemoveProviderProfileIDs(ids...) + return _u +} + +// RemoveProviderProfiles removes "provider_profiles" edges to ProviderProfile entities. +func (_u *SpokenLanguageUpdateOne) RemoveProviderProfiles(v ...*ProviderProfile) *SpokenLanguageUpdateOne { + ids := make([]uuid.UUID, len(v)) + for i := range v { + ids[i] = v[i].ID + } + return _u.RemoveProviderProfileIDs(ids...) +} + +// Where appends a list predicates to the SpokenLanguageUpdate builder. +func (_u *SpokenLanguageUpdateOne) Where(ps ...predicate.SpokenLanguage) *SpokenLanguageUpdateOne { + _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 *SpokenLanguageUpdateOne) Select(field string, fields ...string) *SpokenLanguageUpdateOne { + _u.fields = append([]string{field}, fields...) + return _u +} + +// Save executes the query and returns the updated SpokenLanguage entity. +func (_u *SpokenLanguageUpdateOne) Save(ctx context.Context) (*SpokenLanguage, error) { + return withHooks(ctx, _u.sqlSave, _u.mutation, _u.hooks) +} + +// SaveX is like Save, but panics if an error occurs. +func (_u *SpokenLanguageUpdateOne) SaveX(ctx context.Context) *SpokenLanguage { + node, err := _u.Save(ctx) + if err != nil { + panic(err) + } + return node +} + +// Exec executes the query on the entity. +func (_u *SpokenLanguageUpdateOne) Exec(ctx context.Context) error { + _, err := _u.Save(ctx) + return err +} + +// ExecX is like Exec, but panics if an error occurs. +func (_u *SpokenLanguageUpdateOne) 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 *SpokenLanguageUpdateOne) check() error { + if v, ok := _u.mutation.SortOrder(); ok { + if err := spokenlanguage.SortOrderValidator(v); err != nil { + return &ValidationError{Name: "sort_order", err: fmt.Errorf(`ent: validator failed for field "SpokenLanguage.sort_order": %w`, err)} + } + } + return nil +} + +func (_u *SpokenLanguageUpdateOne) sqlSave(ctx context.Context) (_node *SpokenLanguage, err error) { + if err := _u.check(); err != nil { + return _node, err + } + _spec := sqlgraph.NewUpdateSpec(spokenlanguage.Table, spokenlanguage.Columns, sqlgraph.NewFieldSpec(spokenlanguage.FieldID, field.TypeString)) + id, ok := _u.mutation.ID() + if !ok { + return nil, &ValidationError{Name: "id", err: errors.New(`ent: missing "SpokenLanguage.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, spokenlanguage.FieldID) + for _, f := range fields { + if !spokenlanguage.ValidColumn(f) { + return nil, &ValidationError{Name: f, err: fmt.Errorf("ent: invalid field %q for query", f)} + } + if f != spokenlanguage.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.Active(); ok { + _spec.SetField(spokenlanguage.FieldActive, field.TypeBool, value) + } + if value, ok := _u.mutation.SortOrder(); ok { + _spec.SetField(spokenlanguage.FieldSortOrder, field.TypeInt, value) + } + if value, ok := _u.mutation.AddedSortOrder(); ok { + _spec.AddField(spokenlanguage.FieldSortOrder, field.TypeInt, value) + } + if _u.mutation.LocalizedInCleared() { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.M2M, + Inverse: false, + Table: spokenlanguage.LocalizedInTable, + Columns: spokenlanguage.LocalizedInPrimaryKey, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(supportedlocale.FieldID, field.TypeString), + }, + } + _spec.Edges.Clear = append(_spec.Edges.Clear, edge) + } + if nodes := _u.mutation.RemovedLocalizedInIDs(); len(nodes) > 0 && !_u.mutation.LocalizedInCleared() { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.M2M, + Inverse: false, + Table: spokenlanguage.LocalizedInTable, + Columns: spokenlanguage.LocalizedInPrimaryKey, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(supportedlocale.FieldID, field.TypeString), + }, + } + for _, k := range nodes { + edge.Target.Nodes = append(edge.Target.Nodes, k) + } + _spec.Edges.Clear = append(_spec.Edges.Clear, edge) + } + if nodes := _u.mutation.LocalizedInIDs(); len(nodes) > 0 { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.M2M, + Inverse: false, + Table: spokenlanguage.LocalizedInTable, + Columns: spokenlanguage.LocalizedInPrimaryKey, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(supportedlocale.FieldID, field.TypeString), + }, + } + for _, k := range nodes { + edge.Target.Nodes = append(edge.Target.Nodes, k) + } + _spec.Edges.Add = append(_spec.Edges.Add, edge) + } + if _u.mutation.ProviderProfilesCleared() { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.M2M, + Inverse: true, + Table: spokenlanguage.ProviderProfilesTable, + Columns: spokenlanguage.ProviderProfilesPrimaryKey, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(providerprofile.FieldID, field.TypeUUID), + }, + } + _spec.Edges.Clear = append(_spec.Edges.Clear, edge) + } + if nodes := _u.mutation.RemovedProviderProfilesIDs(); len(nodes) > 0 && !_u.mutation.ProviderProfilesCleared() { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.M2M, + Inverse: true, + Table: spokenlanguage.ProviderProfilesTable, + Columns: spokenlanguage.ProviderProfilesPrimaryKey, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(providerprofile.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.ProviderProfilesIDs(); len(nodes) > 0 { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.M2M, + Inverse: true, + Table: spokenlanguage.ProviderProfilesTable, + Columns: spokenlanguage.ProviderProfilesPrimaryKey, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(providerprofile.FieldID, field.TypeUUID), + }, + } + for _, k := range nodes { + edge.Target.Nodes = append(edge.Target.Nodes, k) + } + _spec.Edges.Add = append(_spec.Edges.Add, edge) + } + _node = &SpokenLanguage{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{spokenlanguage.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/backend/ent/spokenlanguagetranslation.go b/backend/ent/spokenlanguagetranslation.go new file mode 100644 index 0000000..33b157c --- /dev/null +++ b/backend/ent/spokenlanguagetranslation.go @@ -0,0 +1,162 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "fmt" + "strings" + + "entgo.io/ent" + "entgo.io/ent/dialect/sql" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/spokenlanguage" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/spokenlanguagetranslation" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/supportedlocale" +) + +// SpokenLanguageTranslation is the model entity for the SpokenLanguageTranslation schema. +type SpokenLanguageTranslation struct { + config `json:"-"` + // LanguageCode holds the value of the "language_code" field. + LanguageCode string `json:"language_code,omitempty"` + // Locale holds the value of the "locale" field. + Locale string `json:"locale,omitempty"` + // Name holds the value of the "name" field. + Name string `json:"name,omitempty"` + // Edges holds the relations/edges for other nodes in the graph. + // The values are being populated by the SpokenLanguageTranslationQuery when eager-loading is set. + Edges SpokenLanguageTranslationEdges `json:"edges"` + selectValues sql.SelectValues +} + +// SpokenLanguageTranslationEdges holds the relations/edges for other nodes in the graph. +type SpokenLanguageTranslationEdges struct { + // Language holds the value of the language edge. + Language *SpokenLanguage `json:"language,omitempty"` + // LocaleRecord holds the value of the locale_record edge. + LocaleRecord *SupportedLocale `json:"locale_record,omitempty"` + // loadedTypes holds the information for reporting if a + // type was loaded (or requested) in eager-loading or not. + loadedTypes [2]bool +} + +// LanguageOrErr returns the Language value or an error if the edge +// was not loaded in eager-loading, or loaded but was not found. +func (e SpokenLanguageTranslationEdges) LanguageOrErr() (*SpokenLanguage, error) { + if e.Language != nil { + return e.Language, nil + } else if e.loadedTypes[0] { + return nil, &NotFoundError{label: spokenlanguage.Label} + } + return nil, &NotLoadedError{edge: "language"} +} + +// LocaleRecordOrErr returns the LocaleRecord value or an error if the edge +// was not loaded in eager-loading, or loaded but was not found. +func (e SpokenLanguageTranslationEdges) LocaleRecordOrErr() (*SupportedLocale, error) { + if e.LocaleRecord != nil { + return e.LocaleRecord, nil + } else if e.loadedTypes[1] { + return nil, &NotFoundError{label: supportedlocale.Label} + } + return nil, &NotLoadedError{edge: "locale_record"} +} + +// scanValues returns the types for scanning values from sql.Rows. +func (*SpokenLanguageTranslation) scanValues(columns []string) ([]any, error) { + values := make([]any, len(columns)) + for i := range columns { + switch columns[i] { + case spokenlanguagetranslation.FieldLanguageCode, spokenlanguagetranslation.FieldLocale, spokenlanguagetranslation.FieldName: + values[i] = new(sql.NullString) + default: + values[i] = new(sql.UnknownType) + } + } + return values, nil +} + +// assignValues assigns the values that were returned from sql.Rows (after scanning) +// to the SpokenLanguageTranslation fields. +func (_m *SpokenLanguageTranslation) 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 spokenlanguagetranslation.FieldLanguageCode: + if value, ok := values[i].(*sql.NullString); !ok { + return fmt.Errorf("unexpected type %T for field language_code", values[i]) + } else if value.Valid { + _m.LanguageCode = value.String + } + case spokenlanguagetranslation.FieldLocale: + if value, ok := values[i].(*sql.NullString); !ok { + return fmt.Errorf("unexpected type %T for field locale", values[i]) + } else if value.Valid { + _m.Locale = value.String + } + case spokenlanguagetranslation.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 + } + default: + _m.selectValues.Set(columns[i], values[i]) + } + } + return nil +} + +// Value returns the ent.Value that was dynamically selected and assigned to the SpokenLanguageTranslation. +// This includes values selected through modifiers, order, etc. +func (_m *SpokenLanguageTranslation) Value(name string) (ent.Value, error) { + return _m.selectValues.Get(name) +} + +// QueryLanguage queries the "language" edge of the SpokenLanguageTranslation entity. +func (_m *SpokenLanguageTranslation) QueryLanguage() *SpokenLanguageQuery { + return NewSpokenLanguageTranslationClient(_m.config).QueryLanguage(_m) +} + +// QueryLocaleRecord queries the "locale_record" edge of the SpokenLanguageTranslation entity. +func (_m *SpokenLanguageTranslation) QueryLocaleRecord() *SupportedLocaleQuery { + return NewSpokenLanguageTranslationClient(_m.config).QueryLocaleRecord(_m) +} + +// Update returns a builder for updating this SpokenLanguageTranslation. +// Note that you need to call SpokenLanguageTranslation.Unwrap() before calling this method if this SpokenLanguageTranslation +// was returned from a transaction, and the transaction was committed or rolled back. +func (_m *SpokenLanguageTranslation) Update() *SpokenLanguageTranslationUpdateOne { + return NewSpokenLanguageTranslationClient(_m.config).UpdateOne(_m) +} + +// Unwrap unwraps the SpokenLanguageTranslation 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 *SpokenLanguageTranslation) Unwrap() *SpokenLanguageTranslation { + _tx, ok := _m.config.driver.(*txDriver) + if !ok { + panic("ent: SpokenLanguageTranslation is not a transactional entity") + } + _m.config.driver = _tx.drv + return _m +} + +// String implements the fmt.Stringer. +func (_m *SpokenLanguageTranslation) String() string { + var builder strings.Builder + builder.WriteString("SpokenLanguageTranslation(") + builder.WriteString("language_code=") + builder.WriteString(_m.LanguageCode) + builder.WriteString(", ") + builder.WriteString("locale=") + builder.WriteString(_m.Locale) + builder.WriteString(", ") + builder.WriteString("name=") + builder.WriteString(_m.Name) + builder.WriteByte(')') + return builder.String() +} + +// SpokenLanguageTranslations is a parsable slice of SpokenLanguageTranslation. +type SpokenLanguageTranslations []*SpokenLanguageTranslation diff --git a/backend/ent/spokenlanguagetranslation/spokenlanguagetranslation.go b/backend/ent/spokenlanguagetranslation/spokenlanguagetranslation.go new file mode 100644 index 0000000..65adebc --- /dev/null +++ b/backend/ent/spokenlanguagetranslation/spokenlanguagetranslation.go @@ -0,0 +1,115 @@ +// Code generated by ent, DO NOT EDIT. + +package spokenlanguagetranslation + +import ( + "entgo.io/ent/dialect/sql" + "entgo.io/ent/dialect/sql/sqlgraph" +) + +const ( + // Label holds the string label denoting the spokenlanguagetranslation type in the database. + Label = "spoken_language_translation" + // FieldLanguageCode holds the string denoting the language_code field in the database. + FieldLanguageCode = "language_code" + // FieldLocale holds the string denoting the locale field in the database. + FieldLocale = "locale" + // FieldName holds the string denoting the name field in the database. + FieldName = "name" + // EdgeLanguage holds the string denoting the language edge name in mutations. + EdgeLanguage = "language" + // EdgeLocaleRecord holds the string denoting the locale_record edge name in mutations. + EdgeLocaleRecord = "locale_record" + // SpokenLanguageFieldID holds the string denoting the ID field of the SpokenLanguage. + SpokenLanguageFieldID = "id" + // SupportedLocaleFieldID holds the string denoting the ID field of the SupportedLocale. + SupportedLocaleFieldID = "id" + // Table holds the table name of the spokenlanguagetranslation in the database. + Table = "spoken_language_translations" + // LanguageTable is the table that holds the language relation/edge. + LanguageTable = "spoken_language_translations" + // LanguageInverseTable is the table name for the SpokenLanguage entity. + // It exists in this package in order to avoid circular dependency with the "spokenlanguage" package. + LanguageInverseTable = "spoken_languages" + // LanguageColumn is the table column denoting the language relation/edge. + LanguageColumn = "language_code" + // LocaleRecordTable is the table that holds the locale_record relation/edge. + LocaleRecordTable = "spoken_language_translations" + // LocaleRecordInverseTable is the table name for the SupportedLocale entity. + // It exists in this package in order to avoid circular dependency with the "supportedlocale" package. + LocaleRecordInverseTable = "supported_locales" + // LocaleRecordColumn is the table column denoting the locale_record relation/edge. + LocaleRecordColumn = "locale" +) + +// Columns holds all SQL columns for spokenlanguagetranslation fields. +var Columns = []string{ + FieldLanguageCode, + FieldLocale, + FieldName, +} + +// 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 ( + // LanguageCodeValidator is a validator for the "language_code" field. It is called by the builders before save. + LanguageCodeValidator func(string) error + // LocaleValidator is a validator for the "locale" field. It is called by the builders before save. + LocaleValidator func(string) error + // NameValidator is a validator for the "name" field. It is called by the builders before save. + NameValidator func(string) error +) + +// OrderOption defines the ordering options for the SpokenLanguageTranslation queries. +type OrderOption func(*sql.Selector) + +// ByLanguageCode orders the results by the language_code field. +func ByLanguageCode(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldLanguageCode, opts...).ToFunc() +} + +// ByLocale orders the results by the locale field. +func ByLocale(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldLocale, opts...).ToFunc() +} + +// ByName orders the results by the name field. +func ByName(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldName, opts...).ToFunc() +} + +// ByLanguageField orders the results by language field. +func ByLanguageField(field string, opts ...sql.OrderTermOption) OrderOption { + return func(s *sql.Selector) { + sqlgraph.OrderByNeighborTerms(s, newLanguageStep(), sql.OrderByField(field, opts...)) + } +} + +// ByLocaleRecordField orders the results by locale_record field. +func ByLocaleRecordField(field string, opts ...sql.OrderTermOption) OrderOption { + return func(s *sql.Selector) { + sqlgraph.OrderByNeighborTerms(s, newLocaleRecordStep(), sql.OrderByField(field, opts...)) + } +} +func newLanguageStep() *sqlgraph.Step { + return sqlgraph.NewStep( + sqlgraph.From(Table, LanguageColumn), + sqlgraph.To(LanguageInverseTable, SpokenLanguageFieldID), + sqlgraph.Edge(sqlgraph.M2O, false, LanguageTable, LanguageColumn), + ) +} +func newLocaleRecordStep() *sqlgraph.Step { + return sqlgraph.NewStep( + sqlgraph.From(Table, LocaleRecordColumn), + sqlgraph.To(LocaleRecordInverseTable, SupportedLocaleFieldID), + sqlgraph.Edge(sqlgraph.M2O, false, LocaleRecordTable, LocaleRecordColumn), + ) +} diff --git a/backend/ent/spokenlanguagetranslation/where.go b/backend/ent/spokenlanguagetranslation/where.go new file mode 100644 index 0000000..769ee1a --- /dev/null +++ b/backend/ent/spokenlanguagetranslation/where.go @@ -0,0 +1,280 @@ +// Code generated by ent, DO NOT EDIT. + +package spokenlanguagetranslation + +import ( + "entgo.io/ent/dialect/sql" + "entgo.io/ent/dialect/sql/sqlgraph" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/predicate" +) + +// LanguageCode applies equality check predicate on the "language_code" field. It's identical to LanguageCodeEQ. +func LanguageCode(v string) predicate.SpokenLanguageTranslation { + return predicate.SpokenLanguageTranslation(sql.FieldEQ(FieldLanguageCode, v)) +} + +// Locale applies equality check predicate on the "locale" field. It's identical to LocaleEQ. +func Locale(v string) predicate.SpokenLanguageTranslation { + return predicate.SpokenLanguageTranslation(sql.FieldEQ(FieldLocale, v)) +} + +// Name applies equality check predicate on the "name" field. It's identical to NameEQ. +func Name(v string) predicate.SpokenLanguageTranslation { + return predicate.SpokenLanguageTranslation(sql.FieldEQ(FieldName, v)) +} + +// LanguageCodeEQ applies the EQ predicate on the "language_code" field. +func LanguageCodeEQ(v string) predicate.SpokenLanguageTranslation { + return predicate.SpokenLanguageTranslation(sql.FieldEQ(FieldLanguageCode, v)) +} + +// LanguageCodeNEQ applies the NEQ predicate on the "language_code" field. +func LanguageCodeNEQ(v string) predicate.SpokenLanguageTranslation { + return predicate.SpokenLanguageTranslation(sql.FieldNEQ(FieldLanguageCode, v)) +} + +// LanguageCodeIn applies the In predicate on the "language_code" field. +func LanguageCodeIn(vs ...string) predicate.SpokenLanguageTranslation { + return predicate.SpokenLanguageTranslation(sql.FieldIn(FieldLanguageCode, vs...)) +} + +// LanguageCodeNotIn applies the NotIn predicate on the "language_code" field. +func LanguageCodeNotIn(vs ...string) predicate.SpokenLanguageTranslation { + return predicate.SpokenLanguageTranslation(sql.FieldNotIn(FieldLanguageCode, vs...)) +} + +// LanguageCodeGT applies the GT predicate on the "language_code" field. +func LanguageCodeGT(v string) predicate.SpokenLanguageTranslation { + return predicate.SpokenLanguageTranslation(sql.FieldGT(FieldLanguageCode, v)) +} + +// LanguageCodeGTE applies the GTE predicate on the "language_code" field. +func LanguageCodeGTE(v string) predicate.SpokenLanguageTranslation { + return predicate.SpokenLanguageTranslation(sql.FieldGTE(FieldLanguageCode, v)) +} + +// LanguageCodeLT applies the LT predicate on the "language_code" field. +func LanguageCodeLT(v string) predicate.SpokenLanguageTranslation { + return predicate.SpokenLanguageTranslation(sql.FieldLT(FieldLanguageCode, v)) +} + +// LanguageCodeLTE applies the LTE predicate on the "language_code" field. +func LanguageCodeLTE(v string) predicate.SpokenLanguageTranslation { + return predicate.SpokenLanguageTranslation(sql.FieldLTE(FieldLanguageCode, v)) +} + +// LanguageCodeContains applies the Contains predicate on the "language_code" field. +func LanguageCodeContains(v string) predicate.SpokenLanguageTranslation { + return predicate.SpokenLanguageTranslation(sql.FieldContains(FieldLanguageCode, v)) +} + +// LanguageCodeHasPrefix applies the HasPrefix predicate on the "language_code" field. +func LanguageCodeHasPrefix(v string) predicate.SpokenLanguageTranslation { + return predicate.SpokenLanguageTranslation(sql.FieldHasPrefix(FieldLanguageCode, v)) +} + +// LanguageCodeHasSuffix applies the HasSuffix predicate on the "language_code" field. +func LanguageCodeHasSuffix(v string) predicate.SpokenLanguageTranslation { + return predicate.SpokenLanguageTranslation(sql.FieldHasSuffix(FieldLanguageCode, v)) +} + +// LanguageCodeEqualFold applies the EqualFold predicate on the "language_code" field. +func LanguageCodeEqualFold(v string) predicate.SpokenLanguageTranslation { + return predicate.SpokenLanguageTranslation(sql.FieldEqualFold(FieldLanguageCode, v)) +} + +// LanguageCodeContainsFold applies the ContainsFold predicate on the "language_code" field. +func LanguageCodeContainsFold(v string) predicate.SpokenLanguageTranslation { + return predicate.SpokenLanguageTranslation(sql.FieldContainsFold(FieldLanguageCode, v)) +} + +// LocaleEQ applies the EQ predicate on the "locale" field. +func LocaleEQ(v string) predicate.SpokenLanguageTranslation { + return predicate.SpokenLanguageTranslation(sql.FieldEQ(FieldLocale, v)) +} + +// LocaleNEQ applies the NEQ predicate on the "locale" field. +func LocaleNEQ(v string) predicate.SpokenLanguageTranslation { + return predicate.SpokenLanguageTranslation(sql.FieldNEQ(FieldLocale, v)) +} + +// LocaleIn applies the In predicate on the "locale" field. +func LocaleIn(vs ...string) predicate.SpokenLanguageTranslation { + return predicate.SpokenLanguageTranslation(sql.FieldIn(FieldLocale, vs...)) +} + +// LocaleNotIn applies the NotIn predicate on the "locale" field. +func LocaleNotIn(vs ...string) predicate.SpokenLanguageTranslation { + return predicate.SpokenLanguageTranslation(sql.FieldNotIn(FieldLocale, vs...)) +} + +// LocaleGT applies the GT predicate on the "locale" field. +func LocaleGT(v string) predicate.SpokenLanguageTranslation { + return predicate.SpokenLanguageTranslation(sql.FieldGT(FieldLocale, v)) +} + +// LocaleGTE applies the GTE predicate on the "locale" field. +func LocaleGTE(v string) predicate.SpokenLanguageTranslation { + return predicate.SpokenLanguageTranslation(sql.FieldGTE(FieldLocale, v)) +} + +// LocaleLT applies the LT predicate on the "locale" field. +func LocaleLT(v string) predicate.SpokenLanguageTranslation { + return predicate.SpokenLanguageTranslation(sql.FieldLT(FieldLocale, v)) +} + +// LocaleLTE applies the LTE predicate on the "locale" field. +func LocaleLTE(v string) predicate.SpokenLanguageTranslation { + return predicate.SpokenLanguageTranslation(sql.FieldLTE(FieldLocale, v)) +} + +// LocaleContains applies the Contains predicate on the "locale" field. +func LocaleContains(v string) predicate.SpokenLanguageTranslation { + return predicate.SpokenLanguageTranslation(sql.FieldContains(FieldLocale, v)) +} + +// LocaleHasPrefix applies the HasPrefix predicate on the "locale" field. +func LocaleHasPrefix(v string) predicate.SpokenLanguageTranslation { + return predicate.SpokenLanguageTranslation(sql.FieldHasPrefix(FieldLocale, v)) +} + +// LocaleHasSuffix applies the HasSuffix predicate on the "locale" field. +func LocaleHasSuffix(v string) predicate.SpokenLanguageTranslation { + return predicate.SpokenLanguageTranslation(sql.FieldHasSuffix(FieldLocale, v)) +} + +// LocaleEqualFold applies the EqualFold predicate on the "locale" field. +func LocaleEqualFold(v string) predicate.SpokenLanguageTranslation { + return predicate.SpokenLanguageTranslation(sql.FieldEqualFold(FieldLocale, v)) +} + +// LocaleContainsFold applies the ContainsFold predicate on the "locale" field. +func LocaleContainsFold(v string) predicate.SpokenLanguageTranslation { + return predicate.SpokenLanguageTranslation(sql.FieldContainsFold(FieldLocale, v)) +} + +// NameEQ applies the EQ predicate on the "name" field. +func NameEQ(v string) predicate.SpokenLanguageTranslation { + return predicate.SpokenLanguageTranslation(sql.FieldEQ(FieldName, v)) +} + +// NameNEQ applies the NEQ predicate on the "name" field. +func NameNEQ(v string) predicate.SpokenLanguageTranslation { + return predicate.SpokenLanguageTranslation(sql.FieldNEQ(FieldName, v)) +} + +// NameIn applies the In predicate on the "name" field. +func NameIn(vs ...string) predicate.SpokenLanguageTranslation { + return predicate.SpokenLanguageTranslation(sql.FieldIn(FieldName, vs...)) +} + +// NameNotIn applies the NotIn predicate on the "name" field. +func NameNotIn(vs ...string) predicate.SpokenLanguageTranslation { + return predicate.SpokenLanguageTranslation(sql.FieldNotIn(FieldName, vs...)) +} + +// NameGT applies the GT predicate on the "name" field. +func NameGT(v string) predicate.SpokenLanguageTranslation { + return predicate.SpokenLanguageTranslation(sql.FieldGT(FieldName, v)) +} + +// NameGTE applies the GTE predicate on the "name" field. +func NameGTE(v string) predicate.SpokenLanguageTranslation { + return predicate.SpokenLanguageTranslation(sql.FieldGTE(FieldName, v)) +} + +// NameLT applies the LT predicate on the "name" field. +func NameLT(v string) predicate.SpokenLanguageTranslation { + return predicate.SpokenLanguageTranslation(sql.FieldLT(FieldName, v)) +} + +// NameLTE applies the LTE predicate on the "name" field. +func NameLTE(v string) predicate.SpokenLanguageTranslation { + return predicate.SpokenLanguageTranslation(sql.FieldLTE(FieldName, v)) +} + +// NameContains applies the Contains predicate on the "name" field. +func NameContains(v string) predicate.SpokenLanguageTranslation { + return predicate.SpokenLanguageTranslation(sql.FieldContains(FieldName, v)) +} + +// NameHasPrefix applies the HasPrefix predicate on the "name" field. +func NameHasPrefix(v string) predicate.SpokenLanguageTranslation { + return predicate.SpokenLanguageTranslation(sql.FieldHasPrefix(FieldName, v)) +} + +// NameHasSuffix applies the HasSuffix predicate on the "name" field. +func NameHasSuffix(v string) predicate.SpokenLanguageTranslation { + return predicate.SpokenLanguageTranslation(sql.FieldHasSuffix(FieldName, v)) +} + +// NameEqualFold applies the EqualFold predicate on the "name" field. +func NameEqualFold(v string) predicate.SpokenLanguageTranslation { + return predicate.SpokenLanguageTranslation(sql.FieldEqualFold(FieldName, v)) +} + +// NameContainsFold applies the ContainsFold predicate on the "name" field. +func NameContainsFold(v string) predicate.SpokenLanguageTranslation { + return predicate.SpokenLanguageTranslation(sql.FieldContainsFold(FieldName, v)) +} + +// HasLanguage applies the HasEdge predicate on the "language" edge. +func HasLanguage() predicate.SpokenLanguageTranslation { + return predicate.SpokenLanguageTranslation(func(s *sql.Selector) { + step := sqlgraph.NewStep( + sqlgraph.From(Table, LanguageColumn), + sqlgraph.Edge(sqlgraph.M2O, false, LanguageTable, LanguageColumn), + ) + sqlgraph.HasNeighbors(s, step) + }) +} + +// HasLanguageWith applies the HasEdge predicate on the "language" edge with a given conditions (other predicates). +func HasLanguageWith(preds ...predicate.SpokenLanguage) predicate.SpokenLanguageTranslation { + return predicate.SpokenLanguageTranslation(func(s *sql.Selector) { + step := newLanguageStep() + sqlgraph.HasNeighborsWith(s, step, func(s *sql.Selector) { + for _, p := range preds { + p(s) + } + }) + }) +} + +// HasLocaleRecord applies the HasEdge predicate on the "locale_record" edge. +func HasLocaleRecord() predicate.SpokenLanguageTranslation { + return predicate.SpokenLanguageTranslation(func(s *sql.Selector) { + step := sqlgraph.NewStep( + sqlgraph.From(Table, LocaleRecordColumn), + sqlgraph.Edge(sqlgraph.M2O, false, LocaleRecordTable, LocaleRecordColumn), + ) + sqlgraph.HasNeighbors(s, step) + }) +} + +// HasLocaleRecordWith applies the HasEdge predicate on the "locale_record" edge with a given conditions (other predicates). +func HasLocaleRecordWith(preds ...predicate.SupportedLocale) predicate.SpokenLanguageTranslation { + return predicate.SpokenLanguageTranslation(func(s *sql.Selector) { + step := newLocaleRecordStep() + 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.SpokenLanguageTranslation) predicate.SpokenLanguageTranslation { + return predicate.SpokenLanguageTranslation(sql.AndPredicates(predicates...)) +} + +// Or groups predicates with the OR operator between them. +func Or(predicates ...predicate.SpokenLanguageTranslation) predicate.SpokenLanguageTranslation { + return predicate.SpokenLanguageTranslation(sql.OrPredicates(predicates...)) +} + +// Not applies the not operator on the given predicate. +func Not(p predicate.SpokenLanguageTranslation) predicate.SpokenLanguageTranslation { + return predicate.SpokenLanguageTranslation(sql.NotPredicates(p)) +} diff --git a/backend/ent/spokenlanguagetranslation_create.go b/backend/ent/spokenlanguagetranslation_create.go new file mode 100644 index 0000000..c7ead63 --- /dev/null +++ b/backend/ent/spokenlanguagetranslation_create.go @@ -0,0 +1,271 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "context" + "errors" + "fmt" + + "entgo.io/ent/dialect/sql/sqlgraph" + "entgo.io/ent/schema/field" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/spokenlanguage" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/spokenlanguagetranslation" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/supportedlocale" +) + +// SpokenLanguageTranslationCreate is the builder for creating a SpokenLanguageTranslation entity. +type SpokenLanguageTranslationCreate struct { + config + mutation *SpokenLanguageTranslationMutation + hooks []Hook +} + +// SetLanguageCode sets the "language_code" field. +func (_c *SpokenLanguageTranslationCreate) SetLanguageCode(v string) *SpokenLanguageTranslationCreate { + _c.mutation.SetLanguageCode(v) + return _c +} + +// SetLocale sets the "locale" field. +func (_c *SpokenLanguageTranslationCreate) SetLocale(v string) *SpokenLanguageTranslationCreate { + _c.mutation.SetLocale(v) + return _c +} + +// SetName sets the "name" field. +func (_c *SpokenLanguageTranslationCreate) SetName(v string) *SpokenLanguageTranslationCreate { + _c.mutation.SetName(v) + return _c +} + +// SetLanguageID sets the "language" edge to the SpokenLanguage entity by ID. +func (_c *SpokenLanguageTranslationCreate) SetLanguageID(id string) *SpokenLanguageTranslationCreate { + _c.mutation.SetLanguageID(id) + return _c +} + +// SetLanguage sets the "language" edge to the SpokenLanguage entity. +func (_c *SpokenLanguageTranslationCreate) SetLanguage(v *SpokenLanguage) *SpokenLanguageTranslationCreate { + return _c.SetLanguageID(v.ID) +} + +// SetLocaleRecordID sets the "locale_record" edge to the SupportedLocale entity by ID. +func (_c *SpokenLanguageTranslationCreate) SetLocaleRecordID(id string) *SpokenLanguageTranslationCreate { + _c.mutation.SetLocaleRecordID(id) + return _c +} + +// SetLocaleRecord sets the "locale_record" edge to the SupportedLocale entity. +func (_c *SpokenLanguageTranslationCreate) SetLocaleRecord(v *SupportedLocale) *SpokenLanguageTranslationCreate { + return _c.SetLocaleRecordID(v.ID) +} + +// Mutation returns the SpokenLanguageTranslationMutation object of the builder. +func (_c *SpokenLanguageTranslationCreate) Mutation() *SpokenLanguageTranslationMutation { + return _c.mutation +} + +// Save creates the SpokenLanguageTranslation in the database. +func (_c *SpokenLanguageTranslationCreate) Save(ctx context.Context) (*SpokenLanguageTranslation, error) { + return withHooks(ctx, _c.sqlSave, _c.mutation, _c.hooks) +} + +// SaveX calls Save and panics if Save returns an error. +func (_c *SpokenLanguageTranslationCreate) SaveX(ctx context.Context) *SpokenLanguageTranslation { + v, err := _c.Save(ctx) + if err != nil { + panic(err) + } + return v +} + +// Exec executes the query. +func (_c *SpokenLanguageTranslationCreate) Exec(ctx context.Context) error { + _, err := _c.Save(ctx) + return err +} + +// ExecX is like Exec, but panics if an error occurs. +func (_c *SpokenLanguageTranslationCreate) 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 *SpokenLanguageTranslationCreate) check() error { + if _, ok := _c.mutation.LanguageCode(); !ok { + return &ValidationError{Name: "language_code", err: errors.New(`ent: missing required field "SpokenLanguageTranslation.language_code"`)} + } + if v, ok := _c.mutation.LanguageCode(); ok { + if err := spokenlanguagetranslation.LanguageCodeValidator(v); err != nil { + return &ValidationError{Name: "language_code", err: fmt.Errorf(`ent: validator failed for field "SpokenLanguageTranslation.language_code": %w`, err)} + } + } + if _, ok := _c.mutation.Locale(); !ok { + return &ValidationError{Name: "locale", err: errors.New(`ent: missing required field "SpokenLanguageTranslation.locale"`)} + } + if v, ok := _c.mutation.Locale(); ok { + if err := spokenlanguagetranslation.LocaleValidator(v); err != nil { + return &ValidationError{Name: "locale", err: fmt.Errorf(`ent: validator failed for field "SpokenLanguageTranslation.locale": %w`, err)} + } + } + if _, ok := _c.mutation.Name(); !ok { + return &ValidationError{Name: "name", err: errors.New(`ent: missing required field "SpokenLanguageTranslation.name"`)} + } + if v, ok := _c.mutation.Name(); ok { + if err := spokenlanguagetranslation.NameValidator(v); err != nil { + return &ValidationError{Name: "name", err: fmt.Errorf(`ent: validator failed for field "SpokenLanguageTranslation.name": %w`, err)} + } + } + if len(_c.mutation.LanguageIDs()) == 0 { + return &ValidationError{Name: "language", err: errors.New(`ent: missing required edge "SpokenLanguageTranslation.language"`)} + } + if len(_c.mutation.LocaleRecordIDs()) == 0 { + return &ValidationError{Name: "locale_record", err: errors.New(`ent: missing required edge "SpokenLanguageTranslation.locale_record"`)} + } + return nil +} + +func (_c *SpokenLanguageTranslationCreate) sqlSave(ctx context.Context) (*SpokenLanguageTranslation, 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 + } + return _node, nil +} + +func (_c *SpokenLanguageTranslationCreate) createSpec() (*SpokenLanguageTranslation, *sqlgraph.CreateSpec) { + var ( + _node = &SpokenLanguageTranslation{config: _c.config} + _spec = sqlgraph.NewCreateSpec(spokenlanguagetranslation.Table, nil) + ) + if value, ok := _c.mutation.Name(); ok { + _spec.SetField(spokenlanguagetranslation.FieldName, field.TypeString, value) + _node.Name = value + } + if nodes := _c.mutation.LanguageIDs(); len(nodes) > 0 { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.M2O, + Inverse: false, + Table: spokenlanguagetranslation.LanguageTable, + Columns: []string{spokenlanguagetranslation.LanguageColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(spokenlanguage.FieldID, field.TypeString), + }, + } + for _, k := range nodes { + edge.Target.Nodes = append(edge.Target.Nodes, k) + } + _node.LanguageCode = nodes[0] + _spec.Edges = append(_spec.Edges, edge) + } + if nodes := _c.mutation.LocaleRecordIDs(); len(nodes) > 0 { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.M2O, + Inverse: false, + Table: spokenlanguagetranslation.LocaleRecordTable, + Columns: []string{spokenlanguagetranslation.LocaleRecordColumn}, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(supportedlocale.FieldID, field.TypeString), + }, + } + for _, k := range nodes { + edge.Target.Nodes = append(edge.Target.Nodes, k) + } + _node.Locale = nodes[0] + _spec.Edges = append(_spec.Edges, edge) + } + return _node, _spec +} + +// SpokenLanguageTranslationCreateBulk is the builder for creating many SpokenLanguageTranslation entities in bulk. +type SpokenLanguageTranslationCreateBulk struct { + config + err error + builders []*SpokenLanguageTranslationCreate +} + +// Save creates the SpokenLanguageTranslation entities in the database. +func (_c *SpokenLanguageTranslationCreateBulk) Save(ctx context.Context) ([]*SpokenLanguageTranslation, error) { + if _c.err != nil { + return nil, _c.err + } + specs := make([]*sqlgraph.CreateSpec, len(_c.builders)) + nodes := make([]*SpokenLanguageTranslation, 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.(*SpokenLanguageTranslationMutation) + 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.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 *SpokenLanguageTranslationCreateBulk) SaveX(ctx context.Context) []*SpokenLanguageTranslation { + v, err := _c.Save(ctx) + if err != nil { + panic(err) + } + return v +} + +// Exec executes the query. +func (_c *SpokenLanguageTranslationCreateBulk) Exec(ctx context.Context) error { + _, err := _c.Save(ctx) + return err +} + +// ExecX is like Exec, but panics if an error occurs. +func (_c *SpokenLanguageTranslationCreateBulk) ExecX(ctx context.Context) { + if err := _c.Exec(ctx); err != nil { + panic(err) + } +} diff --git a/backend/ent/spokenlanguagetranslation_delete.go b/backend/ent/spokenlanguagetranslation_delete.go new file mode 100644 index 0000000..2e7ce4d --- /dev/null +++ b/backend/ent/spokenlanguagetranslation_delete.go @@ -0,0 +1,87 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "context" + + "entgo.io/ent/dialect/sql" + "entgo.io/ent/dialect/sql/sqlgraph" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/predicate" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/spokenlanguagetranslation" +) + +// SpokenLanguageTranslationDelete is the builder for deleting a SpokenLanguageTranslation entity. +type SpokenLanguageTranslationDelete struct { + config + hooks []Hook + mutation *SpokenLanguageTranslationMutation +} + +// Where appends a list predicates to the SpokenLanguageTranslationDelete builder. +func (_d *SpokenLanguageTranslationDelete) Where(ps ...predicate.SpokenLanguageTranslation) *SpokenLanguageTranslationDelete { + _d.mutation.Where(ps...) + return _d +} + +// Exec executes the deletion query and returns how many vertices were deleted. +func (_d *SpokenLanguageTranslationDelete) 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 *SpokenLanguageTranslationDelete) ExecX(ctx context.Context) int { + n, err := _d.Exec(ctx) + if err != nil { + panic(err) + } + return n +} + +func (_d *SpokenLanguageTranslationDelete) sqlExec(ctx context.Context) (int, error) { + _spec := sqlgraph.NewDeleteSpec(spokenlanguagetranslation.Table, nil) + 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 +} + +// SpokenLanguageTranslationDeleteOne is the builder for deleting a single SpokenLanguageTranslation entity. +type SpokenLanguageTranslationDeleteOne struct { + _d *SpokenLanguageTranslationDelete +} + +// Where appends a list predicates to the SpokenLanguageTranslationDelete builder. +func (_d *SpokenLanguageTranslationDeleteOne) Where(ps ...predicate.SpokenLanguageTranslation) *SpokenLanguageTranslationDeleteOne { + _d._d.mutation.Where(ps...) + return _d +} + +// Exec executes the deletion query. +func (_d *SpokenLanguageTranslationDeleteOne) Exec(ctx context.Context) error { + n, err := _d._d.Exec(ctx) + switch { + case err != nil: + return err + case n == 0: + return &NotFoundError{spokenlanguagetranslation.Label} + default: + return nil + } +} + +// ExecX is like Exec, but panics if an error occurs. +func (_d *SpokenLanguageTranslationDeleteOne) ExecX(ctx context.Context) { + if err := _d.Exec(ctx); err != nil { + panic(err) + } +} diff --git a/backend/ent/spokenlanguagetranslation_query.go b/backend/ent/spokenlanguagetranslation_query.go new file mode 100644 index 0000000..2b433fe --- /dev/null +++ b/backend/ent/spokenlanguagetranslation_query.go @@ -0,0 +1,603 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "context" + "fmt" + "math" + + "entgo.io/ent" + "entgo.io/ent/dialect/sql" + "entgo.io/ent/dialect/sql/sqlgraph" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/predicate" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/spokenlanguage" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/spokenlanguagetranslation" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/supportedlocale" +) + +// SpokenLanguageTranslationQuery is the builder for querying SpokenLanguageTranslation entities. +type SpokenLanguageTranslationQuery struct { + config + ctx *QueryContext + order []spokenlanguagetranslation.OrderOption + inters []Interceptor + predicates []predicate.SpokenLanguageTranslation + withLanguage *SpokenLanguageQuery + withLocaleRecord *SupportedLocaleQuery + // intermediate query (i.e. traversal path). + sql *sql.Selector + path func(context.Context) (*sql.Selector, error) +} + +// Where adds a new predicate for the SpokenLanguageTranslationQuery builder. +func (_q *SpokenLanguageTranslationQuery) Where(ps ...predicate.SpokenLanguageTranslation) *SpokenLanguageTranslationQuery { + _q.predicates = append(_q.predicates, ps...) + return _q +} + +// Limit the number of records to be returned by this query. +func (_q *SpokenLanguageTranslationQuery) Limit(limit int) *SpokenLanguageTranslationQuery { + _q.ctx.Limit = &limit + return _q +} + +// Offset to start from. +func (_q *SpokenLanguageTranslationQuery) Offset(offset int) *SpokenLanguageTranslationQuery { + _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 *SpokenLanguageTranslationQuery) Unique(unique bool) *SpokenLanguageTranslationQuery { + _q.ctx.Unique = &unique + return _q +} + +// Order specifies how the records should be ordered. +func (_q *SpokenLanguageTranslationQuery) Order(o ...spokenlanguagetranslation.OrderOption) *SpokenLanguageTranslationQuery { + _q.order = append(_q.order, o...) + return _q +} + +// QueryLanguage chains the current query on the "language" edge. +func (_q *SpokenLanguageTranslationQuery) QueryLanguage() *SpokenLanguageQuery { + query := (&SpokenLanguageClient{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(spokenlanguagetranslation.Table, spokenlanguagetranslation.LanguageColumn, selector), + sqlgraph.To(spokenlanguage.Table, spokenlanguage.FieldID), + sqlgraph.Edge(sqlgraph.M2O, false, spokenlanguagetranslation.LanguageTable, spokenlanguagetranslation.LanguageColumn), + ) + fromU = sqlgraph.SetNeighbors(_q.driver.Dialect(), step) + return fromU, nil + } + return query +} + +// QueryLocaleRecord chains the current query on the "locale_record" edge. +func (_q *SpokenLanguageTranslationQuery) QueryLocaleRecord() *SupportedLocaleQuery { + query := (&SupportedLocaleClient{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(spokenlanguagetranslation.Table, spokenlanguagetranslation.LocaleRecordColumn, selector), + sqlgraph.To(supportedlocale.Table, supportedlocale.FieldID), + sqlgraph.Edge(sqlgraph.M2O, false, spokenlanguagetranslation.LocaleRecordTable, spokenlanguagetranslation.LocaleRecordColumn), + ) + fromU = sqlgraph.SetNeighbors(_q.driver.Dialect(), step) + return fromU, nil + } + return query +} + +// First returns the first SpokenLanguageTranslation entity from the query. +// Returns a *NotFoundError when no SpokenLanguageTranslation was found. +func (_q *SpokenLanguageTranslationQuery) First(ctx context.Context) (*SpokenLanguageTranslation, 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{spokenlanguagetranslation.Label} + } + return nodes[0], nil +} + +// FirstX is like First, but panics if an error occurs. +func (_q *SpokenLanguageTranslationQuery) FirstX(ctx context.Context) *SpokenLanguageTranslation { + node, err := _q.First(ctx) + if err != nil && !IsNotFound(err) { + panic(err) + } + return node +} + +// Only returns a single SpokenLanguageTranslation entity found by the query, ensuring it only returns one. +// Returns a *NotSingularError when more than one SpokenLanguageTranslation entity is found. +// Returns a *NotFoundError when no SpokenLanguageTranslation entities are found. +func (_q *SpokenLanguageTranslationQuery) Only(ctx context.Context) (*SpokenLanguageTranslation, 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{spokenlanguagetranslation.Label} + default: + return nil, &NotSingularError{spokenlanguagetranslation.Label} + } +} + +// OnlyX is like Only, but panics if an error occurs. +func (_q *SpokenLanguageTranslationQuery) OnlyX(ctx context.Context) *SpokenLanguageTranslation { + node, err := _q.Only(ctx) + if err != nil { + panic(err) + } + return node +} + +// All executes the query and returns a list of SpokenLanguageTranslations. +func (_q *SpokenLanguageTranslationQuery) All(ctx context.Context) ([]*SpokenLanguageTranslation, error) { + ctx = setContextOp(ctx, _q.ctx, ent.OpQueryAll) + if err := _q.prepareQuery(ctx); err != nil { + return nil, err + } + qr := querierAll[[]*SpokenLanguageTranslation, *SpokenLanguageTranslationQuery]() + return withInterceptors[[]*SpokenLanguageTranslation](ctx, _q, qr, _q.inters) +} + +// AllX is like All, but panics if an error occurs. +func (_q *SpokenLanguageTranslationQuery) AllX(ctx context.Context) []*SpokenLanguageTranslation { + nodes, err := _q.All(ctx) + if err != nil { + panic(err) + } + return nodes +} + +// Count returns the count of the given query. +func (_q *SpokenLanguageTranslationQuery) 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[*SpokenLanguageTranslationQuery](), _q.inters) +} + +// CountX is like Count, but panics if an error occurs. +func (_q *SpokenLanguageTranslationQuery) 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 *SpokenLanguageTranslationQuery) Exist(ctx context.Context) (bool, error) { + ctx = setContextOp(ctx, _q.ctx, ent.OpQueryExist) + switch _, err := _q.First(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 *SpokenLanguageTranslationQuery) ExistX(ctx context.Context) bool { + exist, err := _q.Exist(ctx) + if err != nil { + panic(err) + } + return exist +} + +// Clone returns a duplicate of the SpokenLanguageTranslationQuery 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 *SpokenLanguageTranslationQuery) Clone() *SpokenLanguageTranslationQuery { + if _q == nil { + return nil + } + return &SpokenLanguageTranslationQuery{ + config: _q.config, + ctx: _q.ctx.Clone(), + order: append([]spokenlanguagetranslation.OrderOption{}, _q.order...), + inters: append([]Interceptor{}, _q.inters...), + predicates: append([]predicate.SpokenLanguageTranslation{}, _q.predicates...), + withLanguage: _q.withLanguage.Clone(), + withLocaleRecord: _q.withLocaleRecord.Clone(), + // clone intermediate query. + sql: _q.sql.Clone(), + path: _q.path, + } +} + +// WithLanguage tells the query-builder to eager-load the nodes that are connected to +// the "language" edge. The optional arguments are used to configure the query builder of the edge. +func (_q *SpokenLanguageTranslationQuery) WithLanguage(opts ...func(*SpokenLanguageQuery)) *SpokenLanguageTranslationQuery { + query := (&SpokenLanguageClient{config: _q.config}).Query() + for _, opt := range opts { + opt(query) + } + _q.withLanguage = query + return _q +} + +// WithLocaleRecord tells the query-builder to eager-load the nodes that are connected to +// the "locale_record" edge. The optional arguments are used to configure the query builder of the edge. +func (_q *SpokenLanguageTranslationQuery) WithLocaleRecord(opts ...func(*SupportedLocaleQuery)) *SpokenLanguageTranslationQuery { + query := (&SupportedLocaleClient{config: _q.config}).Query() + for _, opt := range opts { + opt(query) + } + _q.withLocaleRecord = 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 { +// LanguageCode string `json:"language_code,omitempty"` +// Count int `json:"count,omitempty"` +// } +// +// client.SpokenLanguageTranslation.Query(). +// GroupBy(spokenlanguagetranslation.FieldLanguageCode). +// Aggregate(ent.Count()). +// Scan(ctx, &v) +func (_q *SpokenLanguageTranslationQuery) GroupBy(field string, fields ...string) *SpokenLanguageTranslationGroupBy { + _q.ctx.Fields = append([]string{field}, fields...) + grbuild := &SpokenLanguageTranslationGroupBy{build: _q} + grbuild.flds = &_q.ctx.Fields + grbuild.label = spokenlanguagetranslation.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 { +// LanguageCode string `json:"language_code,omitempty"` +// } +// +// client.SpokenLanguageTranslation.Query(). +// Select(spokenlanguagetranslation.FieldLanguageCode). +// Scan(ctx, &v) +func (_q *SpokenLanguageTranslationQuery) Select(fields ...string) *SpokenLanguageTranslationSelect { + _q.ctx.Fields = append(_q.ctx.Fields, fields...) + sbuild := &SpokenLanguageTranslationSelect{SpokenLanguageTranslationQuery: _q} + sbuild.label = spokenlanguagetranslation.Label + sbuild.flds, sbuild.scan = &_q.ctx.Fields, sbuild.Scan + return sbuild +} + +// Aggregate returns a SpokenLanguageTranslationSelect configured with the given aggregations. +func (_q *SpokenLanguageTranslationQuery) Aggregate(fns ...AggregateFunc) *SpokenLanguageTranslationSelect { + return _q.Select().Aggregate(fns...) +} + +func (_q *SpokenLanguageTranslationQuery) 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 !spokenlanguagetranslation.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 *SpokenLanguageTranslationQuery) sqlAll(ctx context.Context, hooks ...queryHook) ([]*SpokenLanguageTranslation, error) { + var ( + nodes = []*SpokenLanguageTranslation{} + _spec = _q.querySpec() + loadedTypes = [2]bool{ + _q.withLanguage != nil, + _q.withLocaleRecord != nil, + } + ) + _spec.ScanValues = func(columns []string) ([]any, error) { + return (*SpokenLanguageTranslation).scanValues(nil, columns) + } + _spec.Assign = func(columns []string, values []any) error { + node := &SpokenLanguageTranslation{config: _q.config} + nodes = append(nodes, node) + node.Edges.loadedTypes = loadedTypes + return node.assignValues(columns, values) + } + 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.withLanguage; query != nil { + if err := _q.loadLanguage(ctx, query, nodes, nil, + func(n *SpokenLanguageTranslation, e *SpokenLanguage) { n.Edges.Language = e }); err != nil { + return nil, err + } + } + if query := _q.withLocaleRecord; query != nil { + if err := _q.loadLocaleRecord(ctx, query, nodes, nil, + func(n *SpokenLanguageTranslation, e *SupportedLocale) { n.Edges.LocaleRecord = e }); err != nil { + return nil, err + } + } + return nodes, nil +} + +func (_q *SpokenLanguageTranslationQuery) loadLanguage(ctx context.Context, query *SpokenLanguageQuery, nodes []*SpokenLanguageTranslation, init func(*SpokenLanguageTranslation), assign func(*SpokenLanguageTranslation, *SpokenLanguage)) error { + ids := make([]string, 0, len(nodes)) + nodeids := make(map[string][]*SpokenLanguageTranslation) + for i := range nodes { + fk := nodes[i].LanguageCode + if _, ok := nodeids[fk]; !ok { + ids = append(ids, fk) + } + nodeids[fk] = append(nodeids[fk], nodes[i]) + } + if len(ids) == 0 { + return nil + } + query.Where(spokenlanguage.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 "language_code" returned %v`, n.ID) + } + for i := range nodes { + assign(nodes[i], n) + } + } + return nil +} +func (_q *SpokenLanguageTranslationQuery) loadLocaleRecord(ctx context.Context, query *SupportedLocaleQuery, nodes []*SpokenLanguageTranslation, init func(*SpokenLanguageTranslation), assign func(*SpokenLanguageTranslation, *SupportedLocale)) error { + ids := make([]string, 0, len(nodes)) + nodeids := make(map[string][]*SpokenLanguageTranslation) + for i := range nodes { + fk := nodes[i].Locale + if _, ok := nodeids[fk]; !ok { + ids = append(ids, fk) + } + nodeids[fk] = append(nodeids[fk], nodes[i]) + } + if len(ids) == 0 { + return nil + } + query.Where(supportedlocale.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 "locale" returned %v`, n.ID) + } + for i := range nodes { + assign(nodes[i], n) + } + } + return nil +} + +func (_q *SpokenLanguageTranslationQuery) sqlCount(ctx context.Context) (int, error) { + _spec := _q.querySpec() + _spec.Unique = false + _spec.Node.Columns = nil + return sqlgraph.CountNodes(ctx, _q.driver, _spec) +} + +func (_q *SpokenLanguageTranslationQuery) querySpec() *sqlgraph.QuerySpec { + _spec := sqlgraph.NewQuerySpec(spokenlanguagetranslation.Table, spokenlanguagetranslation.Columns, nil) + _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)) + for i := range fields { + _spec.Node.Columns = append(_spec.Node.Columns, fields[i]) + } + if _q.withLanguage != nil { + _spec.Node.AddColumnOnce(spokenlanguagetranslation.FieldLanguageCode) + } + if _q.withLocaleRecord != nil { + _spec.Node.AddColumnOnce(spokenlanguagetranslation.FieldLocale) + } + } + 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 *SpokenLanguageTranslationQuery) sqlQuery(ctx context.Context) *sql.Selector { + builder := sql.Dialect(_q.driver.Dialect()) + t1 := builder.Table(spokenlanguagetranslation.Table) + columns := _q.ctx.Fields + if len(columns) == 0 { + columns = spokenlanguagetranslation.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 _, 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 +} + +// SpokenLanguageTranslationGroupBy is the group-by builder for SpokenLanguageTranslation entities. +type SpokenLanguageTranslationGroupBy struct { + selector + build *SpokenLanguageTranslationQuery +} + +// Aggregate adds the given aggregation functions to the group-by query. +func (_g *SpokenLanguageTranslationGroupBy) Aggregate(fns ...AggregateFunc) *SpokenLanguageTranslationGroupBy { + _g.fns = append(_g.fns, fns...) + return _g +} + +// Scan applies the selector query and scans the result into the given value. +func (_g *SpokenLanguageTranslationGroupBy) 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[*SpokenLanguageTranslationQuery, *SpokenLanguageTranslationGroupBy](ctx, _g.build, _g, _g.build.inters, v) +} + +func (_g *SpokenLanguageTranslationGroupBy) sqlScan(ctx context.Context, root *SpokenLanguageTranslationQuery, 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) +} + +// SpokenLanguageTranslationSelect is the builder for selecting fields of SpokenLanguageTranslation entities. +type SpokenLanguageTranslationSelect struct { + *SpokenLanguageTranslationQuery + selector +} + +// Aggregate adds the given aggregation functions to the selector query. +func (_s *SpokenLanguageTranslationSelect) Aggregate(fns ...AggregateFunc) *SpokenLanguageTranslationSelect { + _s.fns = append(_s.fns, fns...) + return _s +} + +// Scan applies the selector query and scans the result into the given value. +func (_s *SpokenLanguageTranslationSelect) 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[*SpokenLanguageTranslationQuery, *SpokenLanguageTranslationSelect](ctx, _s.SpokenLanguageTranslationQuery, _s, _s.inters, v) +} + +func (_s *SpokenLanguageTranslationSelect) sqlScan(ctx context.Context, root *SpokenLanguageTranslationQuery, 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/backend/ent/spokenlanguagetranslation_update.go b/backend/ent/spokenlanguagetranslation_update.go new file mode 100644 index 0000000..57d02d0 --- /dev/null +++ b/backend/ent/spokenlanguagetranslation_update.go @@ -0,0 +1,249 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "context" + "errors" + "fmt" + + "entgo.io/ent/dialect/sql" + "entgo.io/ent/dialect/sql/sqlgraph" + "entgo.io/ent/schema/field" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/predicate" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/spokenlanguagetranslation" +) + +// SpokenLanguageTranslationUpdate is the builder for updating SpokenLanguageTranslation entities. +type SpokenLanguageTranslationUpdate struct { + config + hooks []Hook + mutation *SpokenLanguageTranslationMutation +} + +// Where appends a list predicates to the SpokenLanguageTranslationUpdate builder. +func (_u *SpokenLanguageTranslationUpdate) Where(ps ...predicate.SpokenLanguageTranslation) *SpokenLanguageTranslationUpdate { + _u.mutation.Where(ps...) + return _u +} + +// SetName sets the "name" field. +func (_u *SpokenLanguageTranslationUpdate) SetName(v string) *SpokenLanguageTranslationUpdate { + _u.mutation.SetName(v) + return _u +} + +// SetNillableName sets the "name" field if the given value is not nil. +func (_u *SpokenLanguageTranslationUpdate) SetNillableName(v *string) *SpokenLanguageTranslationUpdate { + if v != nil { + _u.SetName(*v) + } + return _u +} + +// Mutation returns the SpokenLanguageTranslationMutation object of the builder. +func (_u *SpokenLanguageTranslationUpdate) Mutation() *SpokenLanguageTranslationMutation { + return _u.mutation +} + +// Save executes the query and returns the number of nodes affected by the update operation. +func (_u *SpokenLanguageTranslationUpdate) 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 *SpokenLanguageTranslationUpdate) SaveX(ctx context.Context) int { + affected, err := _u.Save(ctx) + if err != nil { + panic(err) + } + return affected +} + +// Exec executes the query. +func (_u *SpokenLanguageTranslationUpdate) Exec(ctx context.Context) error { + _, err := _u.Save(ctx) + return err +} + +// ExecX is like Exec, but panics if an error occurs. +func (_u *SpokenLanguageTranslationUpdate) 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 *SpokenLanguageTranslationUpdate) check() error { + if v, ok := _u.mutation.Name(); ok { + if err := spokenlanguagetranslation.NameValidator(v); err != nil { + return &ValidationError{Name: "name", err: fmt.Errorf(`ent: validator failed for field "SpokenLanguageTranslation.name": %w`, err)} + } + } + if _u.mutation.LanguageCleared() && len(_u.mutation.LanguageIDs()) > 0 { + return errors.New(`ent: clearing a required unique edge "SpokenLanguageTranslation.language"`) + } + if _u.mutation.LocaleRecordCleared() && len(_u.mutation.LocaleRecordIDs()) > 0 { + return errors.New(`ent: clearing a required unique edge "SpokenLanguageTranslation.locale_record"`) + } + return nil +} + +func (_u *SpokenLanguageTranslationUpdate) sqlSave(ctx context.Context) (_node int, err error) { + if err := _u.check(); err != nil { + return _node, err + } + _spec := sqlgraph.NewUpdateSpec(spokenlanguagetranslation.Table, spokenlanguagetranslation.Columns, sqlgraph.NewFieldSpec(spokenlanguagetranslation.FieldLanguageCode, field.TypeString), sqlgraph.NewFieldSpec(spokenlanguagetranslation.FieldLocale, field.TypeString)) + 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(spokenlanguagetranslation.FieldName, field.TypeString, value) + } + if _node, err = sqlgraph.UpdateNodes(ctx, _u.driver, _spec); err != nil { + if _, ok := err.(*sqlgraph.NotFoundError); ok { + err = &NotFoundError{spokenlanguagetranslation.Label} + } else if sqlgraph.IsConstraintError(err) { + err = &ConstraintError{msg: err.Error(), wrap: err} + } + return 0, err + } + _u.mutation.done = true + return _node, nil +} + +// SpokenLanguageTranslationUpdateOne is the builder for updating a single SpokenLanguageTranslation entity. +type SpokenLanguageTranslationUpdateOne struct { + config + fields []string + hooks []Hook + mutation *SpokenLanguageTranslationMutation +} + +// SetName sets the "name" field. +func (_u *SpokenLanguageTranslationUpdateOne) SetName(v string) *SpokenLanguageTranslationUpdateOne { + _u.mutation.SetName(v) + return _u +} + +// SetNillableName sets the "name" field if the given value is not nil. +func (_u *SpokenLanguageTranslationUpdateOne) SetNillableName(v *string) *SpokenLanguageTranslationUpdateOne { + if v != nil { + _u.SetName(*v) + } + return _u +} + +// Mutation returns the SpokenLanguageTranslationMutation object of the builder. +func (_u *SpokenLanguageTranslationUpdateOne) Mutation() *SpokenLanguageTranslationMutation { + return _u.mutation +} + +// Where appends a list predicates to the SpokenLanguageTranslationUpdate builder. +func (_u *SpokenLanguageTranslationUpdateOne) Where(ps ...predicate.SpokenLanguageTranslation) *SpokenLanguageTranslationUpdateOne { + _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 *SpokenLanguageTranslationUpdateOne) Select(field string, fields ...string) *SpokenLanguageTranslationUpdateOne { + _u.fields = append([]string{field}, fields...) + return _u +} + +// Save executes the query and returns the updated SpokenLanguageTranslation entity. +func (_u *SpokenLanguageTranslationUpdateOne) Save(ctx context.Context) (*SpokenLanguageTranslation, error) { + return withHooks(ctx, _u.sqlSave, _u.mutation, _u.hooks) +} + +// SaveX is like Save, but panics if an error occurs. +func (_u *SpokenLanguageTranslationUpdateOne) SaveX(ctx context.Context) *SpokenLanguageTranslation { + node, err := _u.Save(ctx) + if err != nil { + panic(err) + } + return node +} + +// Exec executes the query on the entity. +func (_u *SpokenLanguageTranslationUpdateOne) Exec(ctx context.Context) error { + _, err := _u.Save(ctx) + return err +} + +// ExecX is like Exec, but panics if an error occurs. +func (_u *SpokenLanguageTranslationUpdateOne) 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 *SpokenLanguageTranslationUpdateOne) check() error { + if v, ok := _u.mutation.Name(); ok { + if err := spokenlanguagetranslation.NameValidator(v); err != nil { + return &ValidationError{Name: "name", err: fmt.Errorf(`ent: validator failed for field "SpokenLanguageTranslation.name": %w`, err)} + } + } + if _u.mutation.LanguageCleared() && len(_u.mutation.LanguageIDs()) > 0 { + return errors.New(`ent: clearing a required unique edge "SpokenLanguageTranslation.language"`) + } + if _u.mutation.LocaleRecordCleared() && len(_u.mutation.LocaleRecordIDs()) > 0 { + return errors.New(`ent: clearing a required unique edge "SpokenLanguageTranslation.locale_record"`) + } + return nil +} + +func (_u *SpokenLanguageTranslationUpdateOne) sqlSave(ctx context.Context) (_node *SpokenLanguageTranslation, err error) { + if err := _u.check(); err != nil { + return _node, err + } + _spec := sqlgraph.NewUpdateSpec(spokenlanguagetranslation.Table, spokenlanguagetranslation.Columns, sqlgraph.NewFieldSpec(spokenlanguagetranslation.FieldLanguageCode, field.TypeString), sqlgraph.NewFieldSpec(spokenlanguagetranslation.FieldLocale, field.TypeString)) + if id, ok := _u.mutation.LanguageCode(); !ok { + return nil, &ValidationError{Name: "language_code", err: errors.New(`ent: missing "SpokenLanguageTranslation.language_code" for update`)} + } else { + _spec.Node.CompositeID[0].Value = id + } + if id, ok := _u.mutation.Locale(); !ok { + return nil, &ValidationError{Name: "locale", err: errors.New(`ent: missing "SpokenLanguageTranslation.locale" for update`)} + } else { + _spec.Node.CompositeID[1].Value = id + } + if fields := _u.fields; len(fields) > 0 { + _spec.Node.Columns = make([]string, len(fields)) + for i, f := range fields { + if !spokenlanguagetranslation.ValidColumn(f) { + return nil, &ValidationError{Name: f, err: fmt.Errorf("ent: invalid field %q for query", f)} + } + _spec.Node.Columns[i] = 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(spokenlanguagetranslation.FieldName, field.TypeString, value) + } + _node = &SpokenLanguageTranslation{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{spokenlanguagetranslation.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/backend/ent/supportedlocale.go b/backend/ent/supportedlocale.go new file mode 100644 index 0000000..480c871 --- /dev/null +++ b/backend/ent/supportedlocale.go @@ -0,0 +1,158 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "fmt" + "strings" + + "entgo.io/ent" + "entgo.io/ent/dialect/sql" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/supportedlocale" +) + +// SupportedLocale is the model entity for the SupportedLocale schema. +type SupportedLocale struct { + config `json:"-"` + // ID of the ent. + ID string `json:"id,omitempty"` + // Active holds the value of the "active" field. + Active bool `json:"active,omitempty"` + // SortOrder holds the value of the "sort_order" field. + SortOrder int `json:"sort_order,omitempty"` + // Edges holds the relations/edges for other nodes in the graph. + // The values are being populated by the SupportedLocaleQuery when eager-loading is set. + Edges SupportedLocaleEdges `json:"edges"` + selectValues sql.SelectValues +} + +// SupportedLocaleEdges holds the relations/edges for other nodes in the graph. +type SupportedLocaleEdges struct { + // TranslatedCategories holds the value of the translated_categories edge. + TranslatedCategories []*ServiceCategory `json:"translated_categories,omitempty"` + // TranslatedLanguages holds the value of the translated_languages edge. + TranslatedLanguages []*SpokenLanguage `json:"translated_languages,omitempty"` + // loadedTypes holds the information for reporting if a + // type was loaded (or requested) in eager-loading or not. + loadedTypes [2]bool +} + +// TranslatedCategoriesOrErr returns the TranslatedCategories value or an error if the edge +// was not loaded in eager-loading. +func (e SupportedLocaleEdges) TranslatedCategoriesOrErr() ([]*ServiceCategory, error) { + if e.loadedTypes[0] { + return e.TranslatedCategories, nil + } + return nil, &NotLoadedError{edge: "translated_categories"} +} + +// TranslatedLanguagesOrErr returns the TranslatedLanguages value or an error if the edge +// was not loaded in eager-loading. +func (e SupportedLocaleEdges) TranslatedLanguagesOrErr() ([]*SpokenLanguage, error) { + if e.loadedTypes[1] { + return e.TranslatedLanguages, nil + } + return nil, &NotLoadedError{edge: "translated_languages"} +} + +// scanValues returns the types for scanning values from sql.Rows. +func (*SupportedLocale) scanValues(columns []string) ([]any, error) { + values := make([]any, len(columns)) + for i := range columns { + switch columns[i] { + case supportedlocale.FieldActive: + values[i] = new(sql.NullBool) + case supportedlocale.FieldSortOrder: + values[i] = new(sql.NullInt64) + case supportedlocale.FieldID: + values[i] = new(sql.NullString) + default: + values[i] = new(sql.UnknownType) + } + } + return values, nil +} + +// assignValues assigns the values that were returned from sql.Rows (after scanning) +// to the SupportedLocale fields. +func (_m *SupportedLocale) 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 supportedlocale.FieldID: + if value, ok := values[i].(*sql.NullString); !ok { + return fmt.Errorf("unexpected type %T for field id", values[i]) + } else if value.Valid { + _m.ID = value.String + } + case supportedlocale.FieldActive: + if value, ok := values[i].(*sql.NullBool); !ok { + return fmt.Errorf("unexpected type %T for field active", values[i]) + } else if value.Valid { + _m.Active = value.Bool + } + case supportedlocale.FieldSortOrder: + if value, ok := values[i].(*sql.NullInt64); !ok { + return fmt.Errorf("unexpected type %T for field sort_order", values[i]) + } else if value.Valid { + _m.SortOrder = 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 SupportedLocale. +// This includes values selected through modifiers, order, etc. +func (_m *SupportedLocale) Value(name string) (ent.Value, error) { + return _m.selectValues.Get(name) +} + +// QueryTranslatedCategories queries the "translated_categories" edge of the SupportedLocale entity. +func (_m *SupportedLocale) QueryTranslatedCategories() *ServiceCategoryQuery { + return NewSupportedLocaleClient(_m.config).QueryTranslatedCategories(_m) +} + +// QueryTranslatedLanguages queries the "translated_languages" edge of the SupportedLocale entity. +func (_m *SupportedLocale) QueryTranslatedLanguages() *SpokenLanguageQuery { + return NewSupportedLocaleClient(_m.config).QueryTranslatedLanguages(_m) +} + +// Update returns a builder for updating this SupportedLocale. +// Note that you need to call SupportedLocale.Unwrap() before calling this method if this SupportedLocale +// was returned from a transaction, and the transaction was committed or rolled back. +func (_m *SupportedLocale) Update() *SupportedLocaleUpdateOne { + return NewSupportedLocaleClient(_m.config).UpdateOne(_m) +} + +// Unwrap unwraps the SupportedLocale 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 *SupportedLocale) Unwrap() *SupportedLocale { + _tx, ok := _m.config.driver.(*txDriver) + if !ok { + panic("ent: SupportedLocale is not a transactional entity") + } + _m.config.driver = _tx.drv + return _m +} + +// String implements the fmt.Stringer. +func (_m *SupportedLocale) String() string { + var builder strings.Builder + builder.WriteString("SupportedLocale(") + builder.WriteString(fmt.Sprintf("id=%v, ", _m.ID)) + builder.WriteString("active=") + builder.WriteString(fmt.Sprintf("%v", _m.Active)) + builder.WriteString(", ") + builder.WriteString("sort_order=") + builder.WriteString(fmt.Sprintf("%v", _m.SortOrder)) + builder.WriteByte(')') + return builder.String() +} + +// SupportedLocales is a parsable slice of SupportedLocale. +type SupportedLocales []*SupportedLocale diff --git a/backend/ent/supportedlocale/supportedlocale.go b/backend/ent/supportedlocale/supportedlocale.go new file mode 100644 index 0000000..1659cd9 --- /dev/null +++ b/backend/ent/supportedlocale/supportedlocale.go @@ -0,0 +1,130 @@ +// Code generated by ent, DO NOT EDIT. + +package supportedlocale + +import ( + "entgo.io/ent/dialect/sql" + "entgo.io/ent/dialect/sql/sqlgraph" +) + +const ( + // Label holds the string label denoting the supportedlocale type in the database. + Label = "supported_locale" + // FieldID holds the string denoting the id field in the database. + FieldID = "id" + // FieldActive holds the string denoting the active field in the database. + FieldActive = "active" + // FieldSortOrder holds the string denoting the sort_order field in the database. + FieldSortOrder = "sort_order" + // EdgeTranslatedCategories holds the string denoting the translated_categories edge name in mutations. + EdgeTranslatedCategories = "translated_categories" + // EdgeTranslatedLanguages holds the string denoting the translated_languages edge name in mutations. + EdgeTranslatedLanguages = "translated_languages" + // Table holds the table name of the supportedlocale in the database. + Table = "supported_locales" + // TranslatedCategoriesTable is the table that holds the translated_categories relation/edge. The primary key declared below. + TranslatedCategoriesTable = "service_category_translations" + // TranslatedCategoriesInverseTable is the table name for the ServiceCategory entity. + // It exists in this package in order to avoid circular dependency with the "servicecategory" package. + TranslatedCategoriesInverseTable = "service_categories" + // TranslatedLanguagesTable is the table that holds the translated_languages relation/edge. The primary key declared below. + TranslatedLanguagesTable = "spoken_language_translations" + // TranslatedLanguagesInverseTable is the table name for the SpokenLanguage entity. + // It exists in this package in order to avoid circular dependency with the "spokenlanguage" package. + TranslatedLanguagesInverseTable = "spoken_languages" +) + +// Columns holds all SQL columns for supportedlocale fields. +var Columns = []string{ + FieldID, + FieldActive, + FieldSortOrder, +} + +var ( + // TranslatedCategoriesPrimaryKey and TranslatedCategoriesColumn2 are the table columns denoting the + // primary key for the translated_categories relation (M2M). + TranslatedCategoriesPrimaryKey = []string{"category_id", "locale"} + // TranslatedLanguagesPrimaryKey and TranslatedLanguagesColumn2 are the table columns denoting the + // primary key for the translated_languages relation (M2M). + TranslatedLanguagesPrimaryKey = []string{"language_code", "locale"} +) + +// 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 ( + // DefaultActive holds the default value on creation for the "active" field. + DefaultActive bool + // SortOrderValidator is a validator for the "sort_order" field. It is called by the builders before save. + SortOrderValidator func(int) error + // IDValidator is a validator for the "id" field. It is called by the builders before save. + IDValidator func(string) error +) + +// OrderOption defines the ordering options for the SupportedLocale 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() +} + +// ByActive orders the results by the active field. +func ByActive(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldActive, opts...).ToFunc() +} + +// BySortOrder orders the results by the sort_order field. +func BySortOrder(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldSortOrder, opts...).ToFunc() +} + +// ByTranslatedCategoriesCount orders the results by translated_categories count. +func ByTranslatedCategoriesCount(opts ...sql.OrderTermOption) OrderOption { + return func(s *sql.Selector) { + sqlgraph.OrderByNeighborsCount(s, newTranslatedCategoriesStep(), opts...) + } +} + +// ByTranslatedCategories orders the results by translated_categories terms. +func ByTranslatedCategories(term sql.OrderTerm, terms ...sql.OrderTerm) OrderOption { + return func(s *sql.Selector) { + sqlgraph.OrderByNeighborTerms(s, newTranslatedCategoriesStep(), append([]sql.OrderTerm{term}, terms...)...) + } +} + +// ByTranslatedLanguagesCount orders the results by translated_languages count. +func ByTranslatedLanguagesCount(opts ...sql.OrderTermOption) OrderOption { + return func(s *sql.Selector) { + sqlgraph.OrderByNeighborsCount(s, newTranslatedLanguagesStep(), opts...) + } +} + +// ByTranslatedLanguages orders the results by translated_languages terms. +func ByTranslatedLanguages(term sql.OrderTerm, terms ...sql.OrderTerm) OrderOption { + return func(s *sql.Selector) { + sqlgraph.OrderByNeighborTerms(s, newTranslatedLanguagesStep(), append([]sql.OrderTerm{term}, terms...)...) + } +} +func newTranslatedCategoriesStep() *sqlgraph.Step { + return sqlgraph.NewStep( + sqlgraph.From(Table, FieldID), + sqlgraph.To(TranslatedCategoriesInverseTable, FieldID), + sqlgraph.Edge(sqlgraph.M2M, true, TranslatedCategoriesTable, TranslatedCategoriesPrimaryKey...), + ) +} +func newTranslatedLanguagesStep() *sqlgraph.Step { + return sqlgraph.NewStep( + sqlgraph.From(Table, FieldID), + sqlgraph.To(TranslatedLanguagesInverseTable, FieldID), + sqlgraph.Edge(sqlgraph.M2M, true, TranslatedLanguagesTable, TranslatedLanguagesPrimaryKey...), + ) +} diff --git a/backend/ent/supportedlocale/where.go b/backend/ent/supportedlocale/where.go new file mode 100644 index 0000000..40b3ffa --- /dev/null +++ b/backend/ent/supportedlocale/where.go @@ -0,0 +1,185 @@ +// Code generated by ent, DO NOT EDIT. + +package supportedlocale + +import ( + "entgo.io/ent/dialect/sql" + "entgo.io/ent/dialect/sql/sqlgraph" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/predicate" +) + +// ID filters vertices based on their ID field. +func ID(id string) predicate.SupportedLocale { + return predicate.SupportedLocale(sql.FieldEQ(FieldID, id)) +} + +// IDEQ applies the EQ predicate on the ID field. +func IDEQ(id string) predicate.SupportedLocale { + return predicate.SupportedLocale(sql.FieldEQ(FieldID, id)) +} + +// IDNEQ applies the NEQ predicate on the ID field. +func IDNEQ(id string) predicate.SupportedLocale { + return predicate.SupportedLocale(sql.FieldNEQ(FieldID, id)) +} + +// IDIn applies the In predicate on the ID field. +func IDIn(ids ...string) predicate.SupportedLocale { + return predicate.SupportedLocale(sql.FieldIn(FieldID, ids...)) +} + +// IDNotIn applies the NotIn predicate on the ID field. +func IDNotIn(ids ...string) predicate.SupportedLocale { + return predicate.SupportedLocale(sql.FieldNotIn(FieldID, ids...)) +} + +// IDGT applies the GT predicate on the ID field. +func IDGT(id string) predicate.SupportedLocale { + return predicate.SupportedLocale(sql.FieldGT(FieldID, id)) +} + +// IDGTE applies the GTE predicate on the ID field. +func IDGTE(id string) predicate.SupportedLocale { + return predicate.SupportedLocale(sql.FieldGTE(FieldID, id)) +} + +// IDLT applies the LT predicate on the ID field. +func IDLT(id string) predicate.SupportedLocale { + return predicate.SupportedLocale(sql.FieldLT(FieldID, id)) +} + +// IDLTE applies the LTE predicate on the ID field. +func IDLTE(id string) predicate.SupportedLocale { + return predicate.SupportedLocale(sql.FieldLTE(FieldID, id)) +} + +// IDEqualFold applies the EqualFold predicate on the ID field. +func IDEqualFold(id string) predicate.SupportedLocale { + return predicate.SupportedLocale(sql.FieldEqualFold(FieldID, id)) +} + +// IDContainsFold applies the ContainsFold predicate on the ID field. +func IDContainsFold(id string) predicate.SupportedLocale { + return predicate.SupportedLocale(sql.FieldContainsFold(FieldID, id)) +} + +// Active applies equality check predicate on the "active" field. It's identical to ActiveEQ. +func Active(v bool) predicate.SupportedLocale { + return predicate.SupportedLocale(sql.FieldEQ(FieldActive, v)) +} + +// SortOrder applies equality check predicate on the "sort_order" field. It's identical to SortOrderEQ. +func SortOrder(v int) predicate.SupportedLocale { + return predicate.SupportedLocale(sql.FieldEQ(FieldSortOrder, v)) +} + +// ActiveEQ applies the EQ predicate on the "active" field. +func ActiveEQ(v bool) predicate.SupportedLocale { + return predicate.SupportedLocale(sql.FieldEQ(FieldActive, v)) +} + +// ActiveNEQ applies the NEQ predicate on the "active" field. +func ActiveNEQ(v bool) predicate.SupportedLocale { + return predicate.SupportedLocale(sql.FieldNEQ(FieldActive, v)) +} + +// SortOrderEQ applies the EQ predicate on the "sort_order" field. +func SortOrderEQ(v int) predicate.SupportedLocale { + return predicate.SupportedLocale(sql.FieldEQ(FieldSortOrder, v)) +} + +// SortOrderNEQ applies the NEQ predicate on the "sort_order" field. +func SortOrderNEQ(v int) predicate.SupportedLocale { + return predicate.SupportedLocale(sql.FieldNEQ(FieldSortOrder, v)) +} + +// SortOrderIn applies the In predicate on the "sort_order" field. +func SortOrderIn(vs ...int) predicate.SupportedLocale { + return predicate.SupportedLocale(sql.FieldIn(FieldSortOrder, vs...)) +} + +// SortOrderNotIn applies the NotIn predicate on the "sort_order" field. +func SortOrderNotIn(vs ...int) predicate.SupportedLocale { + return predicate.SupportedLocale(sql.FieldNotIn(FieldSortOrder, vs...)) +} + +// SortOrderGT applies the GT predicate on the "sort_order" field. +func SortOrderGT(v int) predicate.SupportedLocale { + return predicate.SupportedLocale(sql.FieldGT(FieldSortOrder, v)) +} + +// SortOrderGTE applies the GTE predicate on the "sort_order" field. +func SortOrderGTE(v int) predicate.SupportedLocale { + return predicate.SupportedLocale(sql.FieldGTE(FieldSortOrder, v)) +} + +// SortOrderLT applies the LT predicate on the "sort_order" field. +func SortOrderLT(v int) predicate.SupportedLocale { + return predicate.SupportedLocale(sql.FieldLT(FieldSortOrder, v)) +} + +// SortOrderLTE applies the LTE predicate on the "sort_order" field. +func SortOrderLTE(v int) predicate.SupportedLocale { + return predicate.SupportedLocale(sql.FieldLTE(FieldSortOrder, v)) +} + +// HasTranslatedCategories applies the HasEdge predicate on the "translated_categories" edge. +func HasTranslatedCategories() predicate.SupportedLocale { + return predicate.SupportedLocale(func(s *sql.Selector) { + step := sqlgraph.NewStep( + sqlgraph.From(Table, FieldID), + sqlgraph.Edge(sqlgraph.M2M, true, TranslatedCategoriesTable, TranslatedCategoriesPrimaryKey...), + ) + sqlgraph.HasNeighbors(s, step) + }) +} + +// HasTranslatedCategoriesWith applies the HasEdge predicate on the "translated_categories" edge with a given conditions (other predicates). +func HasTranslatedCategoriesWith(preds ...predicate.ServiceCategory) predicate.SupportedLocale { + return predicate.SupportedLocale(func(s *sql.Selector) { + step := newTranslatedCategoriesStep() + sqlgraph.HasNeighborsWith(s, step, func(s *sql.Selector) { + for _, p := range preds { + p(s) + } + }) + }) +} + +// HasTranslatedLanguages applies the HasEdge predicate on the "translated_languages" edge. +func HasTranslatedLanguages() predicate.SupportedLocale { + return predicate.SupportedLocale(func(s *sql.Selector) { + step := sqlgraph.NewStep( + sqlgraph.From(Table, FieldID), + sqlgraph.Edge(sqlgraph.M2M, true, TranslatedLanguagesTable, TranslatedLanguagesPrimaryKey...), + ) + sqlgraph.HasNeighbors(s, step) + }) +} + +// HasTranslatedLanguagesWith applies the HasEdge predicate on the "translated_languages" edge with a given conditions (other predicates). +func HasTranslatedLanguagesWith(preds ...predicate.SpokenLanguage) predicate.SupportedLocale { + return predicate.SupportedLocale(func(s *sql.Selector) { + step := newTranslatedLanguagesStep() + 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.SupportedLocale) predicate.SupportedLocale { + return predicate.SupportedLocale(sql.AndPredicates(predicates...)) +} + +// Or groups predicates with the OR operator between them. +func Or(predicates ...predicate.SupportedLocale) predicate.SupportedLocale { + return predicate.SupportedLocale(sql.OrPredicates(predicates...)) +} + +// Not applies the not operator on the given predicate. +func Not(p predicate.SupportedLocale) predicate.SupportedLocale { + return predicate.SupportedLocale(sql.NotPredicates(p)) +} diff --git a/backend/ent/supportedlocale_create.go b/backend/ent/supportedlocale_create.go new file mode 100644 index 0000000..bde0708 --- /dev/null +++ b/backend/ent/supportedlocale_create.go @@ -0,0 +1,300 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "context" + "errors" + "fmt" + + "entgo.io/ent/dialect/sql/sqlgraph" + "entgo.io/ent/schema/field" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/servicecategory" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/spokenlanguage" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/supportedlocale" + "github.com/google/uuid" +) + +// SupportedLocaleCreate is the builder for creating a SupportedLocale entity. +type SupportedLocaleCreate struct { + config + mutation *SupportedLocaleMutation + hooks []Hook +} + +// SetActive sets the "active" field. +func (_c *SupportedLocaleCreate) SetActive(v bool) *SupportedLocaleCreate { + _c.mutation.SetActive(v) + return _c +} + +// SetNillableActive sets the "active" field if the given value is not nil. +func (_c *SupportedLocaleCreate) SetNillableActive(v *bool) *SupportedLocaleCreate { + if v != nil { + _c.SetActive(*v) + } + return _c +} + +// SetSortOrder sets the "sort_order" field. +func (_c *SupportedLocaleCreate) SetSortOrder(v int) *SupportedLocaleCreate { + _c.mutation.SetSortOrder(v) + return _c +} + +// SetID sets the "id" field. +func (_c *SupportedLocaleCreate) SetID(v string) *SupportedLocaleCreate { + _c.mutation.SetID(v) + return _c +} + +// AddTranslatedCategoryIDs adds the "translated_categories" edge to the ServiceCategory entity by IDs. +func (_c *SupportedLocaleCreate) AddTranslatedCategoryIDs(ids ...uuid.UUID) *SupportedLocaleCreate { + _c.mutation.AddTranslatedCategoryIDs(ids...) + return _c +} + +// AddTranslatedCategories adds the "translated_categories" edges to the ServiceCategory entity. +func (_c *SupportedLocaleCreate) AddTranslatedCategories(v ...*ServiceCategory) *SupportedLocaleCreate { + ids := make([]uuid.UUID, len(v)) + for i := range v { + ids[i] = v[i].ID + } + return _c.AddTranslatedCategoryIDs(ids...) +} + +// AddTranslatedLanguageIDs adds the "translated_languages" edge to the SpokenLanguage entity by IDs. +func (_c *SupportedLocaleCreate) AddTranslatedLanguageIDs(ids ...string) *SupportedLocaleCreate { + _c.mutation.AddTranslatedLanguageIDs(ids...) + return _c +} + +// AddTranslatedLanguages adds the "translated_languages" edges to the SpokenLanguage entity. +func (_c *SupportedLocaleCreate) AddTranslatedLanguages(v ...*SpokenLanguage) *SupportedLocaleCreate { + ids := make([]string, len(v)) + for i := range v { + ids[i] = v[i].ID + } + return _c.AddTranslatedLanguageIDs(ids...) +} + +// Mutation returns the SupportedLocaleMutation object of the builder. +func (_c *SupportedLocaleCreate) Mutation() *SupportedLocaleMutation { + return _c.mutation +} + +// Save creates the SupportedLocale in the database. +func (_c *SupportedLocaleCreate) Save(ctx context.Context) (*SupportedLocale, error) { + _c.defaults() + return withHooks(ctx, _c.sqlSave, _c.mutation, _c.hooks) +} + +// SaveX calls Save and panics if Save returns an error. +func (_c *SupportedLocaleCreate) SaveX(ctx context.Context) *SupportedLocale { + v, err := _c.Save(ctx) + if err != nil { + panic(err) + } + return v +} + +// Exec executes the query. +func (_c *SupportedLocaleCreate) Exec(ctx context.Context) error { + _, err := _c.Save(ctx) + return err +} + +// ExecX is like Exec, but panics if an error occurs. +func (_c *SupportedLocaleCreate) 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 *SupportedLocaleCreate) defaults() { + if _, ok := _c.mutation.Active(); !ok { + v := supportedlocale.DefaultActive + _c.mutation.SetActive(v) + } +} + +// check runs all checks and user-defined validators on the builder. +func (_c *SupportedLocaleCreate) check() error { + if _, ok := _c.mutation.Active(); !ok { + return &ValidationError{Name: "active", err: errors.New(`ent: missing required field "SupportedLocale.active"`)} + } + if _, ok := _c.mutation.SortOrder(); !ok { + return &ValidationError{Name: "sort_order", err: errors.New(`ent: missing required field "SupportedLocale.sort_order"`)} + } + if v, ok := _c.mutation.SortOrder(); ok { + if err := supportedlocale.SortOrderValidator(v); err != nil { + return &ValidationError{Name: "sort_order", err: fmt.Errorf(`ent: validator failed for field "SupportedLocale.sort_order": %w`, err)} + } + } + if v, ok := _c.mutation.ID(); ok { + if err := supportedlocale.IDValidator(v); err != nil { + return &ValidationError{Name: "id", err: fmt.Errorf(`ent: validator failed for field "SupportedLocale.id": %w`, err)} + } + } + return nil +} + +func (_c *SupportedLocaleCreate) sqlSave(ctx context.Context) (*SupportedLocale, 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.(string); ok { + _node.ID = id + } else { + return nil, fmt.Errorf("unexpected SupportedLocale.ID type: %T", _spec.ID.Value) + } + } + _c.mutation.id = &_node.ID + _c.mutation.done = true + return _node, nil +} + +func (_c *SupportedLocaleCreate) createSpec() (*SupportedLocale, *sqlgraph.CreateSpec) { + var ( + _node = &SupportedLocale{config: _c.config} + _spec = sqlgraph.NewCreateSpec(supportedlocale.Table, sqlgraph.NewFieldSpec(supportedlocale.FieldID, field.TypeString)) + ) + if id, ok := _c.mutation.ID(); ok { + _node.ID = id + _spec.ID.Value = id + } + if value, ok := _c.mutation.Active(); ok { + _spec.SetField(supportedlocale.FieldActive, field.TypeBool, value) + _node.Active = value + } + if value, ok := _c.mutation.SortOrder(); ok { + _spec.SetField(supportedlocale.FieldSortOrder, field.TypeInt, value) + _node.SortOrder = value + } + if nodes := _c.mutation.TranslatedCategoriesIDs(); len(nodes) > 0 { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.M2M, + Inverse: true, + Table: supportedlocale.TranslatedCategoriesTable, + Columns: supportedlocale.TranslatedCategoriesPrimaryKey, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(servicecategory.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.TranslatedLanguagesIDs(); len(nodes) > 0 { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.M2M, + Inverse: true, + Table: supportedlocale.TranslatedLanguagesTable, + Columns: supportedlocale.TranslatedLanguagesPrimaryKey, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(spokenlanguage.FieldID, field.TypeString), + }, + } + for _, k := range nodes { + edge.Target.Nodes = append(edge.Target.Nodes, k) + } + _spec.Edges = append(_spec.Edges, edge) + } + return _node, _spec +} + +// SupportedLocaleCreateBulk is the builder for creating many SupportedLocale entities in bulk. +type SupportedLocaleCreateBulk struct { + config + err error + builders []*SupportedLocaleCreate +} + +// Save creates the SupportedLocale entities in the database. +func (_c *SupportedLocaleCreateBulk) Save(ctx context.Context) ([]*SupportedLocale, error) { + if _c.err != nil { + return nil, _c.err + } + specs := make([]*sqlgraph.CreateSpec, len(_c.builders)) + nodes := make([]*SupportedLocale, 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.(*SupportedLocaleMutation) + 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 *SupportedLocaleCreateBulk) SaveX(ctx context.Context) []*SupportedLocale { + v, err := _c.Save(ctx) + if err != nil { + panic(err) + } + return v +} + +// Exec executes the query. +func (_c *SupportedLocaleCreateBulk) Exec(ctx context.Context) error { + _, err := _c.Save(ctx) + return err +} + +// ExecX is like Exec, but panics if an error occurs. +func (_c *SupportedLocaleCreateBulk) ExecX(ctx context.Context) { + if err := _c.Exec(ctx); err != nil { + panic(err) + } +} diff --git a/backend/ent/supportedlocale_delete.go b/backend/ent/supportedlocale_delete.go new file mode 100644 index 0000000..1803f91 --- /dev/null +++ b/backend/ent/supportedlocale_delete.go @@ -0,0 +1,88 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "context" + + "entgo.io/ent/dialect/sql" + "entgo.io/ent/dialect/sql/sqlgraph" + "entgo.io/ent/schema/field" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/predicate" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/supportedlocale" +) + +// SupportedLocaleDelete is the builder for deleting a SupportedLocale entity. +type SupportedLocaleDelete struct { + config + hooks []Hook + mutation *SupportedLocaleMutation +} + +// Where appends a list predicates to the SupportedLocaleDelete builder. +func (_d *SupportedLocaleDelete) Where(ps ...predicate.SupportedLocale) *SupportedLocaleDelete { + _d.mutation.Where(ps...) + return _d +} + +// Exec executes the deletion query and returns how many vertices were deleted. +func (_d *SupportedLocaleDelete) 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 *SupportedLocaleDelete) ExecX(ctx context.Context) int { + n, err := _d.Exec(ctx) + if err != nil { + panic(err) + } + return n +} + +func (_d *SupportedLocaleDelete) sqlExec(ctx context.Context) (int, error) { + _spec := sqlgraph.NewDeleteSpec(supportedlocale.Table, sqlgraph.NewFieldSpec(supportedlocale.FieldID, field.TypeString)) + 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 +} + +// SupportedLocaleDeleteOne is the builder for deleting a single SupportedLocale entity. +type SupportedLocaleDeleteOne struct { + _d *SupportedLocaleDelete +} + +// Where appends a list predicates to the SupportedLocaleDelete builder. +func (_d *SupportedLocaleDeleteOne) Where(ps ...predicate.SupportedLocale) *SupportedLocaleDeleteOne { + _d._d.mutation.Where(ps...) + return _d +} + +// Exec executes the deletion query. +func (_d *SupportedLocaleDeleteOne) Exec(ctx context.Context) error { + n, err := _d._d.Exec(ctx) + switch { + case err != nil: + return err + case n == 0: + return &NotFoundError{supportedlocale.Label} + default: + return nil + } +} + +// ExecX is like Exec, but panics if an error occurs. +func (_d *SupportedLocaleDeleteOne) ExecX(ctx context.Context) { + if err := _d.Exec(ctx); err != nil { + panic(err) + } +} diff --git a/backend/ent/supportedlocale_query.go b/backend/ent/supportedlocale_query.go new file mode 100644 index 0000000..55aaa1e --- /dev/null +++ b/backend/ent/supportedlocale_query.go @@ -0,0 +1,747 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "context" + "database/sql/driver" + "fmt" + "math" + + "entgo.io/ent" + "entgo.io/ent/dialect/sql" + "entgo.io/ent/dialect/sql/sqlgraph" + "entgo.io/ent/schema/field" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/predicate" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/servicecategory" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/spokenlanguage" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/supportedlocale" + "github.com/google/uuid" +) + +// SupportedLocaleQuery is the builder for querying SupportedLocale entities. +type SupportedLocaleQuery struct { + config + ctx *QueryContext + order []supportedlocale.OrderOption + inters []Interceptor + predicates []predicate.SupportedLocale + withTranslatedCategories *ServiceCategoryQuery + withTranslatedLanguages *SpokenLanguageQuery + // intermediate query (i.e. traversal path). + sql *sql.Selector + path func(context.Context) (*sql.Selector, error) +} + +// Where adds a new predicate for the SupportedLocaleQuery builder. +func (_q *SupportedLocaleQuery) Where(ps ...predicate.SupportedLocale) *SupportedLocaleQuery { + _q.predicates = append(_q.predicates, ps...) + return _q +} + +// Limit the number of records to be returned by this query. +func (_q *SupportedLocaleQuery) Limit(limit int) *SupportedLocaleQuery { + _q.ctx.Limit = &limit + return _q +} + +// Offset to start from. +func (_q *SupportedLocaleQuery) Offset(offset int) *SupportedLocaleQuery { + _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 *SupportedLocaleQuery) Unique(unique bool) *SupportedLocaleQuery { + _q.ctx.Unique = &unique + return _q +} + +// Order specifies how the records should be ordered. +func (_q *SupportedLocaleQuery) Order(o ...supportedlocale.OrderOption) *SupportedLocaleQuery { + _q.order = append(_q.order, o...) + return _q +} + +// QueryTranslatedCategories chains the current query on the "translated_categories" edge. +func (_q *SupportedLocaleQuery) QueryTranslatedCategories() *ServiceCategoryQuery { + query := (&ServiceCategoryClient{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(supportedlocale.Table, supportedlocale.FieldID, selector), + sqlgraph.To(servicecategory.Table, servicecategory.FieldID), + sqlgraph.Edge(sqlgraph.M2M, true, supportedlocale.TranslatedCategoriesTable, supportedlocale.TranslatedCategoriesPrimaryKey...), + ) + fromU = sqlgraph.SetNeighbors(_q.driver.Dialect(), step) + return fromU, nil + } + return query +} + +// QueryTranslatedLanguages chains the current query on the "translated_languages" edge. +func (_q *SupportedLocaleQuery) QueryTranslatedLanguages() *SpokenLanguageQuery { + query := (&SpokenLanguageClient{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(supportedlocale.Table, supportedlocale.FieldID, selector), + sqlgraph.To(spokenlanguage.Table, spokenlanguage.FieldID), + sqlgraph.Edge(sqlgraph.M2M, true, supportedlocale.TranslatedLanguagesTable, supportedlocale.TranslatedLanguagesPrimaryKey...), + ) + fromU = sqlgraph.SetNeighbors(_q.driver.Dialect(), step) + return fromU, nil + } + return query +} + +// First returns the first SupportedLocale entity from the query. +// Returns a *NotFoundError when no SupportedLocale was found. +func (_q *SupportedLocaleQuery) First(ctx context.Context) (*SupportedLocale, 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{supportedlocale.Label} + } + return nodes[0], nil +} + +// FirstX is like First, but panics if an error occurs. +func (_q *SupportedLocaleQuery) FirstX(ctx context.Context) *SupportedLocale { + node, err := _q.First(ctx) + if err != nil && !IsNotFound(err) { + panic(err) + } + return node +} + +// FirstID returns the first SupportedLocale ID from the query. +// Returns a *NotFoundError when no SupportedLocale ID was found. +func (_q *SupportedLocaleQuery) FirstID(ctx context.Context) (id string, err error) { + var ids []string + if ids, err = _q.Limit(1).IDs(setContextOp(ctx, _q.ctx, ent.OpQueryFirstID)); err != nil { + return + } + if len(ids) == 0 { + err = &NotFoundError{supportedlocale.Label} + return + } + return ids[0], nil +} + +// FirstIDX is like FirstID, but panics if an error occurs. +func (_q *SupportedLocaleQuery) FirstIDX(ctx context.Context) string { + id, err := _q.FirstID(ctx) + if err != nil && !IsNotFound(err) { + panic(err) + } + return id +} + +// Only returns a single SupportedLocale entity found by the query, ensuring it only returns one. +// Returns a *NotSingularError when more than one SupportedLocale entity is found. +// Returns a *NotFoundError when no SupportedLocale entities are found. +func (_q *SupportedLocaleQuery) Only(ctx context.Context) (*SupportedLocale, 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{supportedlocale.Label} + default: + return nil, &NotSingularError{supportedlocale.Label} + } +} + +// OnlyX is like Only, but panics if an error occurs. +func (_q *SupportedLocaleQuery) OnlyX(ctx context.Context) *SupportedLocale { + node, err := _q.Only(ctx) + if err != nil { + panic(err) + } + return node +} + +// OnlyID is like Only, but returns the only SupportedLocale ID in the query. +// Returns a *NotSingularError when more than one SupportedLocale ID is found. +// Returns a *NotFoundError when no entities are found. +func (_q *SupportedLocaleQuery) OnlyID(ctx context.Context) (id string, err error) { + var ids []string + 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{supportedlocale.Label} + default: + err = &NotSingularError{supportedlocale.Label} + } + return +} + +// OnlyIDX is like OnlyID, but panics if an error occurs. +func (_q *SupportedLocaleQuery) OnlyIDX(ctx context.Context) string { + id, err := _q.OnlyID(ctx) + if err != nil { + panic(err) + } + return id +} + +// All executes the query and returns a list of SupportedLocales. +func (_q *SupportedLocaleQuery) All(ctx context.Context) ([]*SupportedLocale, error) { + ctx = setContextOp(ctx, _q.ctx, ent.OpQueryAll) + if err := _q.prepareQuery(ctx); err != nil { + return nil, err + } + qr := querierAll[[]*SupportedLocale, *SupportedLocaleQuery]() + return withInterceptors[[]*SupportedLocale](ctx, _q, qr, _q.inters) +} + +// AllX is like All, but panics if an error occurs. +func (_q *SupportedLocaleQuery) AllX(ctx context.Context) []*SupportedLocale { + nodes, err := _q.All(ctx) + if err != nil { + panic(err) + } + return nodes +} + +// IDs executes the query and returns a list of SupportedLocale IDs. +func (_q *SupportedLocaleQuery) IDs(ctx context.Context) (ids []string, err error) { + if _q.ctx.Unique == nil && _q.path != nil { + _q.Unique(true) + } + ctx = setContextOp(ctx, _q.ctx, ent.OpQueryIDs) + if err = _q.Select(supportedlocale.FieldID).Scan(ctx, &ids); err != nil { + return nil, err + } + return ids, nil +} + +// IDsX is like IDs, but panics if an error occurs. +func (_q *SupportedLocaleQuery) IDsX(ctx context.Context) []string { + ids, err := _q.IDs(ctx) + if err != nil { + panic(err) + } + return ids +} + +// Count returns the count of the given query. +func (_q *SupportedLocaleQuery) 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[*SupportedLocaleQuery](), _q.inters) +} + +// CountX is like Count, but panics if an error occurs. +func (_q *SupportedLocaleQuery) 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 *SupportedLocaleQuery) 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 *SupportedLocaleQuery) ExistX(ctx context.Context) bool { + exist, err := _q.Exist(ctx) + if err != nil { + panic(err) + } + return exist +} + +// Clone returns a duplicate of the SupportedLocaleQuery 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 *SupportedLocaleQuery) Clone() *SupportedLocaleQuery { + if _q == nil { + return nil + } + return &SupportedLocaleQuery{ + config: _q.config, + ctx: _q.ctx.Clone(), + order: append([]supportedlocale.OrderOption{}, _q.order...), + inters: append([]Interceptor{}, _q.inters...), + predicates: append([]predicate.SupportedLocale{}, _q.predicates...), + withTranslatedCategories: _q.withTranslatedCategories.Clone(), + withTranslatedLanguages: _q.withTranslatedLanguages.Clone(), + // clone intermediate query. + sql: _q.sql.Clone(), + path: _q.path, + } +} + +// WithTranslatedCategories tells the query-builder to eager-load the nodes that are connected to +// the "translated_categories" edge. The optional arguments are used to configure the query builder of the edge. +func (_q *SupportedLocaleQuery) WithTranslatedCategories(opts ...func(*ServiceCategoryQuery)) *SupportedLocaleQuery { + query := (&ServiceCategoryClient{config: _q.config}).Query() + for _, opt := range opts { + opt(query) + } + _q.withTranslatedCategories = query + return _q +} + +// WithTranslatedLanguages tells the query-builder to eager-load the nodes that are connected to +// the "translated_languages" edge. The optional arguments are used to configure the query builder of the edge. +func (_q *SupportedLocaleQuery) WithTranslatedLanguages(opts ...func(*SpokenLanguageQuery)) *SupportedLocaleQuery { + query := (&SpokenLanguageClient{config: _q.config}).Query() + for _, opt := range opts { + opt(query) + } + _q.withTranslatedLanguages = 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 { +// Active bool `json:"active,omitempty"` +// Count int `json:"count,omitempty"` +// } +// +// client.SupportedLocale.Query(). +// GroupBy(supportedlocale.FieldActive). +// Aggregate(ent.Count()). +// Scan(ctx, &v) +func (_q *SupportedLocaleQuery) GroupBy(field string, fields ...string) *SupportedLocaleGroupBy { + _q.ctx.Fields = append([]string{field}, fields...) + grbuild := &SupportedLocaleGroupBy{build: _q} + grbuild.flds = &_q.ctx.Fields + grbuild.label = supportedlocale.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 { +// Active bool `json:"active,omitempty"` +// } +// +// client.SupportedLocale.Query(). +// Select(supportedlocale.FieldActive). +// Scan(ctx, &v) +func (_q *SupportedLocaleQuery) Select(fields ...string) *SupportedLocaleSelect { + _q.ctx.Fields = append(_q.ctx.Fields, fields...) + sbuild := &SupportedLocaleSelect{SupportedLocaleQuery: _q} + sbuild.label = supportedlocale.Label + sbuild.flds, sbuild.scan = &_q.ctx.Fields, sbuild.Scan + return sbuild +} + +// Aggregate returns a SupportedLocaleSelect configured with the given aggregations. +func (_q *SupportedLocaleQuery) Aggregate(fns ...AggregateFunc) *SupportedLocaleSelect { + return _q.Select().Aggregate(fns...) +} + +func (_q *SupportedLocaleQuery) 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 !supportedlocale.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 *SupportedLocaleQuery) sqlAll(ctx context.Context, hooks ...queryHook) ([]*SupportedLocale, error) { + var ( + nodes = []*SupportedLocale{} + _spec = _q.querySpec() + loadedTypes = [2]bool{ + _q.withTranslatedCategories != nil, + _q.withTranslatedLanguages != nil, + } + ) + _spec.ScanValues = func(columns []string) ([]any, error) { + return (*SupportedLocale).scanValues(nil, columns) + } + _spec.Assign = func(columns []string, values []any) error { + node := &SupportedLocale{config: _q.config} + nodes = append(nodes, node) + node.Edges.loadedTypes = loadedTypes + return node.assignValues(columns, values) + } + 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.withTranslatedCategories; query != nil { + if err := _q.loadTranslatedCategories(ctx, query, nodes, + func(n *SupportedLocale) { n.Edges.TranslatedCategories = []*ServiceCategory{} }, + func(n *SupportedLocale, e *ServiceCategory) { + n.Edges.TranslatedCategories = append(n.Edges.TranslatedCategories, e) + }); err != nil { + return nil, err + } + } + if query := _q.withTranslatedLanguages; query != nil { + if err := _q.loadTranslatedLanguages(ctx, query, nodes, + func(n *SupportedLocale) { n.Edges.TranslatedLanguages = []*SpokenLanguage{} }, + func(n *SupportedLocale, e *SpokenLanguage) { + n.Edges.TranslatedLanguages = append(n.Edges.TranslatedLanguages, e) + }); err != nil { + return nil, err + } + } + return nodes, nil +} + +func (_q *SupportedLocaleQuery) loadTranslatedCategories(ctx context.Context, query *ServiceCategoryQuery, nodes []*SupportedLocale, init func(*SupportedLocale), assign func(*SupportedLocale, *ServiceCategory)) error { + edgeIDs := make([]driver.Value, len(nodes)) + byID := make(map[string]*SupportedLocale) + nids := make(map[uuid.UUID]map[*SupportedLocale]struct{}) + for i, node := range nodes { + edgeIDs[i] = node.ID + byID[node.ID] = node + if init != nil { + init(node) + } + } + query.Where(func(s *sql.Selector) { + joinT := sql.Table(supportedlocale.TranslatedCategoriesTable) + s.Join(joinT).On(s.C(servicecategory.FieldID), joinT.C(supportedlocale.TranslatedCategoriesPrimaryKey[0])) + s.Where(sql.InValues(joinT.C(supportedlocale.TranslatedCategoriesPrimaryKey[1]), edgeIDs...)) + columns := s.SelectedColumns() + s.Select(joinT.C(supportedlocale.TranslatedCategoriesPrimaryKey[1])) + s.AppendSelect(columns...) + s.SetDistinct(false) + }) + if err := query.prepareQuery(ctx); err != nil { + return err + } + qr := QuerierFunc(func(ctx context.Context, q Query) (Value, error) { + return query.sqlAll(ctx, func(_ context.Context, spec *sqlgraph.QuerySpec) { + assign := spec.Assign + values := spec.ScanValues + spec.ScanValues = func(columns []string) ([]any, error) { + values, err := values(columns[1:]) + if err != nil { + return nil, err + } + return append([]any{new(sql.NullString)}, values...), nil + } + spec.Assign = func(columns []string, values []any) error { + outValue := values[0].(*sql.NullString).String + inValue := *values[1].(*uuid.UUID) + if nids[inValue] == nil { + nids[inValue] = map[*SupportedLocale]struct{}{byID[outValue]: {}} + return assign(columns[1:], values[1:]) + } + nids[inValue][byID[outValue]] = struct{}{} + return nil + } + }) + }) + neighbors, err := withInterceptors[[]*ServiceCategory](ctx, query, qr, query.inters) + if err != nil { + return err + } + for _, n := range neighbors { + nodes, ok := nids[n.ID] + if !ok { + return fmt.Errorf(`unexpected "translated_categories" node returned %v`, n.ID) + } + for kn := range nodes { + assign(kn, n) + } + } + return nil +} +func (_q *SupportedLocaleQuery) loadTranslatedLanguages(ctx context.Context, query *SpokenLanguageQuery, nodes []*SupportedLocale, init func(*SupportedLocale), assign func(*SupportedLocale, *SpokenLanguage)) error { + edgeIDs := make([]driver.Value, len(nodes)) + byID := make(map[string]*SupportedLocale) + nids := make(map[string]map[*SupportedLocale]struct{}) + for i, node := range nodes { + edgeIDs[i] = node.ID + byID[node.ID] = node + if init != nil { + init(node) + } + } + query.Where(func(s *sql.Selector) { + joinT := sql.Table(supportedlocale.TranslatedLanguagesTable) + s.Join(joinT).On(s.C(spokenlanguage.FieldID), joinT.C(supportedlocale.TranslatedLanguagesPrimaryKey[0])) + s.Where(sql.InValues(joinT.C(supportedlocale.TranslatedLanguagesPrimaryKey[1]), edgeIDs...)) + columns := s.SelectedColumns() + s.Select(joinT.C(supportedlocale.TranslatedLanguagesPrimaryKey[1])) + s.AppendSelect(columns...) + s.SetDistinct(false) + }) + if err := query.prepareQuery(ctx); err != nil { + return err + } + qr := QuerierFunc(func(ctx context.Context, q Query) (Value, error) { + return query.sqlAll(ctx, func(_ context.Context, spec *sqlgraph.QuerySpec) { + assign := spec.Assign + values := spec.ScanValues + spec.ScanValues = func(columns []string) ([]any, error) { + values, err := values(columns[1:]) + if err != nil { + return nil, err + } + return append([]any{new(sql.NullString)}, values...), nil + } + spec.Assign = func(columns []string, values []any) error { + outValue := values[0].(*sql.NullString).String + inValue := values[1].(*sql.NullString).String + if nids[inValue] == nil { + nids[inValue] = map[*SupportedLocale]struct{}{byID[outValue]: {}} + return assign(columns[1:], values[1:]) + } + nids[inValue][byID[outValue]] = struct{}{} + return nil + } + }) + }) + neighbors, err := withInterceptors[[]*SpokenLanguage](ctx, query, qr, query.inters) + if err != nil { + return err + } + for _, n := range neighbors { + nodes, ok := nids[n.ID] + if !ok { + return fmt.Errorf(`unexpected "translated_languages" node returned %v`, n.ID) + } + for kn := range nodes { + assign(kn, n) + } + } + return nil +} + +func (_q *SupportedLocaleQuery) sqlCount(ctx context.Context) (int, error) { + _spec := _q.querySpec() + _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 *SupportedLocaleQuery) querySpec() *sqlgraph.QuerySpec { + _spec := sqlgraph.NewQuerySpec(supportedlocale.Table, supportedlocale.Columns, sqlgraph.NewFieldSpec(supportedlocale.FieldID, field.TypeString)) + _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, supportedlocale.FieldID) + for i := range fields { + if fields[i] != supportedlocale.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 *SupportedLocaleQuery) sqlQuery(ctx context.Context) *sql.Selector { + builder := sql.Dialect(_q.driver.Dialect()) + t1 := builder.Table(supportedlocale.Table) + columns := _q.ctx.Fields + if len(columns) == 0 { + columns = supportedlocale.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 _, 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 +} + +// SupportedLocaleGroupBy is the group-by builder for SupportedLocale entities. +type SupportedLocaleGroupBy struct { + selector + build *SupportedLocaleQuery +} + +// Aggregate adds the given aggregation functions to the group-by query. +func (_g *SupportedLocaleGroupBy) Aggregate(fns ...AggregateFunc) *SupportedLocaleGroupBy { + _g.fns = append(_g.fns, fns...) + return _g +} + +// Scan applies the selector query and scans the result into the given value. +func (_g *SupportedLocaleGroupBy) 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[*SupportedLocaleQuery, *SupportedLocaleGroupBy](ctx, _g.build, _g, _g.build.inters, v) +} + +func (_g *SupportedLocaleGroupBy) sqlScan(ctx context.Context, root *SupportedLocaleQuery, 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) +} + +// SupportedLocaleSelect is the builder for selecting fields of SupportedLocale entities. +type SupportedLocaleSelect struct { + *SupportedLocaleQuery + selector +} + +// Aggregate adds the given aggregation functions to the selector query. +func (_s *SupportedLocaleSelect) Aggregate(fns ...AggregateFunc) *SupportedLocaleSelect { + _s.fns = append(_s.fns, fns...) + return _s +} + +// Scan applies the selector query and scans the result into the given value. +func (_s *SupportedLocaleSelect) 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[*SupportedLocaleQuery, *SupportedLocaleSelect](ctx, _s.SupportedLocaleQuery, _s, _s.inters, v) +} + +func (_s *SupportedLocaleSelect) sqlScan(ctx context.Context, root *SupportedLocaleQuery, 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/backend/ent/supportedlocale_update.go b/backend/ent/supportedlocale_update.go new file mode 100644 index 0000000..5834dff --- /dev/null +++ b/backend/ent/supportedlocale_update.go @@ -0,0 +1,616 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "context" + "errors" + "fmt" + + "entgo.io/ent/dialect/sql" + "entgo.io/ent/dialect/sql/sqlgraph" + "entgo.io/ent/schema/field" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/predicate" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/servicecategory" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/spokenlanguage" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/supportedlocale" + "github.com/google/uuid" +) + +// SupportedLocaleUpdate is the builder for updating SupportedLocale entities. +type SupportedLocaleUpdate struct { + config + hooks []Hook + mutation *SupportedLocaleMutation +} + +// Where appends a list predicates to the SupportedLocaleUpdate builder. +func (_u *SupportedLocaleUpdate) Where(ps ...predicate.SupportedLocale) *SupportedLocaleUpdate { + _u.mutation.Where(ps...) + return _u +} + +// SetActive sets the "active" field. +func (_u *SupportedLocaleUpdate) SetActive(v bool) *SupportedLocaleUpdate { + _u.mutation.SetActive(v) + return _u +} + +// SetNillableActive sets the "active" field if the given value is not nil. +func (_u *SupportedLocaleUpdate) SetNillableActive(v *bool) *SupportedLocaleUpdate { + if v != nil { + _u.SetActive(*v) + } + return _u +} + +// SetSortOrder sets the "sort_order" field. +func (_u *SupportedLocaleUpdate) SetSortOrder(v int) *SupportedLocaleUpdate { + _u.mutation.ResetSortOrder() + _u.mutation.SetSortOrder(v) + return _u +} + +// SetNillableSortOrder sets the "sort_order" field if the given value is not nil. +func (_u *SupportedLocaleUpdate) SetNillableSortOrder(v *int) *SupportedLocaleUpdate { + if v != nil { + _u.SetSortOrder(*v) + } + return _u +} + +// AddSortOrder adds value to the "sort_order" field. +func (_u *SupportedLocaleUpdate) AddSortOrder(v int) *SupportedLocaleUpdate { + _u.mutation.AddSortOrder(v) + return _u +} + +// AddTranslatedCategoryIDs adds the "translated_categories" edge to the ServiceCategory entity by IDs. +func (_u *SupportedLocaleUpdate) AddTranslatedCategoryIDs(ids ...uuid.UUID) *SupportedLocaleUpdate { + _u.mutation.AddTranslatedCategoryIDs(ids...) + return _u +} + +// AddTranslatedCategories adds the "translated_categories" edges to the ServiceCategory entity. +func (_u *SupportedLocaleUpdate) AddTranslatedCategories(v ...*ServiceCategory) *SupportedLocaleUpdate { + ids := make([]uuid.UUID, len(v)) + for i := range v { + ids[i] = v[i].ID + } + return _u.AddTranslatedCategoryIDs(ids...) +} + +// AddTranslatedLanguageIDs adds the "translated_languages" edge to the SpokenLanguage entity by IDs. +func (_u *SupportedLocaleUpdate) AddTranslatedLanguageIDs(ids ...string) *SupportedLocaleUpdate { + _u.mutation.AddTranslatedLanguageIDs(ids...) + return _u +} + +// AddTranslatedLanguages adds the "translated_languages" edges to the SpokenLanguage entity. +func (_u *SupportedLocaleUpdate) AddTranslatedLanguages(v ...*SpokenLanguage) *SupportedLocaleUpdate { + ids := make([]string, len(v)) + for i := range v { + ids[i] = v[i].ID + } + return _u.AddTranslatedLanguageIDs(ids...) +} + +// Mutation returns the SupportedLocaleMutation object of the builder. +func (_u *SupportedLocaleUpdate) Mutation() *SupportedLocaleMutation { + return _u.mutation +} + +// ClearTranslatedCategories clears all "translated_categories" edges to the ServiceCategory entity. +func (_u *SupportedLocaleUpdate) ClearTranslatedCategories() *SupportedLocaleUpdate { + _u.mutation.ClearTranslatedCategories() + return _u +} + +// RemoveTranslatedCategoryIDs removes the "translated_categories" edge to ServiceCategory entities by IDs. +func (_u *SupportedLocaleUpdate) RemoveTranslatedCategoryIDs(ids ...uuid.UUID) *SupportedLocaleUpdate { + _u.mutation.RemoveTranslatedCategoryIDs(ids...) + return _u +} + +// RemoveTranslatedCategories removes "translated_categories" edges to ServiceCategory entities. +func (_u *SupportedLocaleUpdate) RemoveTranslatedCategories(v ...*ServiceCategory) *SupportedLocaleUpdate { + ids := make([]uuid.UUID, len(v)) + for i := range v { + ids[i] = v[i].ID + } + return _u.RemoveTranslatedCategoryIDs(ids...) +} + +// ClearTranslatedLanguages clears all "translated_languages" edges to the SpokenLanguage entity. +func (_u *SupportedLocaleUpdate) ClearTranslatedLanguages() *SupportedLocaleUpdate { + _u.mutation.ClearTranslatedLanguages() + return _u +} + +// RemoveTranslatedLanguageIDs removes the "translated_languages" edge to SpokenLanguage entities by IDs. +func (_u *SupportedLocaleUpdate) RemoveTranslatedLanguageIDs(ids ...string) *SupportedLocaleUpdate { + _u.mutation.RemoveTranslatedLanguageIDs(ids...) + return _u +} + +// RemoveTranslatedLanguages removes "translated_languages" edges to SpokenLanguage entities. +func (_u *SupportedLocaleUpdate) RemoveTranslatedLanguages(v ...*SpokenLanguage) *SupportedLocaleUpdate { + ids := make([]string, len(v)) + for i := range v { + ids[i] = v[i].ID + } + return _u.RemoveTranslatedLanguageIDs(ids...) +} + +// Save executes the query and returns the number of nodes affected by the update operation. +func (_u *SupportedLocaleUpdate) 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 *SupportedLocaleUpdate) SaveX(ctx context.Context) int { + affected, err := _u.Save(ctx) + if err != nil { + panic(err) + } + return affected +} + +// Exec executes the query. +func (_u *SupportedLocaleUpdate) Exec(ctx context.Context) error { + _, err := _u.Save(ctx) + return err +} + +// ExecX is like Exec, but panics if an error occurs. +func (_u *SupportedLocaleUpdate) 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 *SupportedLocaleUpdate) check() error { + if v, ok := _u.mutation.SortOrder(); ok { + if err := supportedlocale.SortOrderValidator(v); err != nil { + return &ValidationError{Name: "sort_order", err: fmt.Errorf(`ent: validator failed for field "SupportedLocale.sort_order": %w`, err)} + } + } + return nil +} + +func (_u *SupportedLocaleUpdate) sqlSave(ctx context.Context) (_node int, err error) { + if err := _u.check(); err != nil { + return _node, err + } + _spec := sqlgraph.NewUpdateSpec(supportedlocale.Table, supportedlocale.Columns, sqlgraph.NewFieldSpec(supportedlocale.FieldID, field.TypeString)) + 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.Active(); ok { + _spec.SetField(supportedlocale.FieldActive, field.TypeBool, value) + } + if value, ok := _u.mutation.SortOrder(); ok { + _spec.SetField(supportedlocale.FieldSortOrder, field.TypeInt, value) + } + if value, ok := _u.mutation.AddedSortOrder(); ok { + _spec.AddField(supportedlocale.FieldSortOrder, field.TypeInt, value) + } + if _u.mutation.TranslatedCategoriesCleared() { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.M2M, + Inverse: true, + Table: supportedlocale.TranslatedCategoriesTable, + Columns: supportedlocale.TranslatedCategoriesPrimaryKey, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(servicecategory.FieldID, field.TypeUUID), + }, + } + _spec.Edges.Clear = append(_spec.Edges.Clear, edge) + } + if nodes := _u.mutation.RemovedTranslatedCategoriesIDs(); len(nodes) > 0 && !_u.mutation.TranslatedCategoriesCleared() { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.M2M, + Inverse: true, + Table: supportedlocale.TranslatedCategoriesTable, + Columns: supportedlocale.TranslatedCategoriesPrimaryKey, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(servicecategory.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.TranslatedCategoriesIDs(); len(nodes) > 0 { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.M2M, + Inverse: true, + Table: supportedlocale.TranslatedCategoriesTable, + Columns: supportedlocale.TranslatedCategoriesPrimaryKey, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(servicecategory.FieldID, field.TypeUUID), + }, + } + for _, k := range nodes { + edge.Target.Nodes = append(edge.Target.Nodes, k) + } + _spec.Edges.Add = append(_spec.Edges.Add, edge) + } + if _u.mutation.TranslatedLanguagesCleared() { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.M2M, + Inverse: true, + Table: supportedlocale.TranslatedLanguagesTable, + Columns: supportedlocale.TranslatedLanguagesPrimaryKey, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(spokenlanguage.FieldID, field.TypeString), + }, + } + _spec.Edges.Clear = append(_spec.Edges.Clear, edge) + } + if nodes := _u.mutation.RemovedTranslatedLanguagesIDs(); len(nodes) > 0 && !_u.mutation.TranslatedLanguagesCleared() { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.M2M, + Inverse: true, + Table: supportedlocale.TranslatedLanguagesTable, + Columns: supportedlocale.TranslatedLanguagesPrimaryKey, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(spokenlanguage.FieldID, field.TypeString), + }, + } + for _, k := range nodes { + edge.Target.Nodes = append(edge.Target.Nodes, k) + } + _spec.Edges.Clear = append(_spec.Edges.Clear, edge) + } + if nodes := _u.mutation.TranslatedLanguagesIDs(); len(nodes) > 0 { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.M2M, + Inverse: true, + Table: supportedlocale.TranslatedLanguagesTable, + Columns: supportedlocale.TranslatedLanguagesPrimaryKey, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(spokenlanguage.FieldID, field.TypeString), + }, + } + 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{supportedlocale.Label} + } else if sqlgraph.IsConstraintError(err) { + err = &ConstraintError{msg: err.Error(), wrap: err} + } + return 0, err + } + _u.mutation.done = true + return _node, nil +} + +// SupportedLocaleUpdateOne is the builder for updating a single SupportedLocale entity. +type SupportedLocaleUpdateOne struct { + config + fields []string + hooks []Hook + mutation *SupportedLocaleMutation +} + +// SetActive sets the "active" field. +func (_u *SupportedLocaleUpdateOne) SetActive(v bool) *SupportedLocaleUpdateOne { + _u.mutation.SetActive(v) + return _u +} + +// SetNillableActive sets the "active" field if the given value is not nil. +func (_u *SupportedLocaleUpdateOne) SetNillableActive(v *bool) *SupportedLocaleUpdateOne { + if v != nil { + _u.SetActive(*v) + } + return _u +} + +// SetSortOrder sets the "sort_order" field. +func (_u *SupportedLocaleUpdateOne) SetSortOrder(v int) *SupportedLocaleUpdateOne { + _u.mutation.ResetSortOrder() + _u.mutation.SetSortOrder(v) + return _u +} + +// SetNillableSortOrder sets the "sort_order" field if the given value is not nil. +func (_u *SupportedLocaleUpdateOne) SetNillableSortOrder(v *int) *SupportedLocaleUpdateOne { + if v != nil { + _u.SetSortOrder(*v) + } + return _u +} + +// AddSortOrder adds value to the "sort_order" field. +func (_u *SupportedLocaleUpdateOne) AddSortOrder(v int) *SupportedLocaleUpdateOne { + _u.mutation.AddSortOrder(v) + return _u +} + +// AddTranslatedCategoryIDs adds the "translated_categories" edge to the ServiceCategory entity by IDs. +func (_u *SupportedLocaleUpdateOne) AddTranslatedCategoryIDs(ids ...uuid.UUID) *SupportedLocaleUpdateOne { + _u.mutation.AddTranslatedCategoryIDs(ids...) + return _u +} + +// AddTranslatedCategories adds the "translated_categories" edges to the ServiceCategory entity. +func (_u *SupportedLocaleUpdateOne) AddTranslatedCategories(v ...*ServiceCategory) *SupportedLocaleUpdateOne { + ids := make([]uuid.UUID, len(v)) + for i := range v { + ids[i] = v[i].ID + } + return _u.AddTranslatedCategoryIDs(ids...) +} + +// AddTranslatedLanguageIDs adds the "translated_languages" edge to the SpokenLanguage entity by IDs. +func (_u *SupportedLocaleUpdateOne) AddTranslatedLanguageIDs(ids ...string) *SupportedLocaleUpdateOne { + _u.mutation.AddTranslatedLanguageIDs(ids...) + return _u +} + +// AddTranslatedLanguages adds the "translated_languages" edges to the SpokenLanguage entity. +func (_u *SupportedLocaleUpdateOne) AddTranslatedLanguages(v ...*SpokenLanguage) *SupportedLocaleUpdateOne { + ids := make([]string, len(v)) + for i := range v { + ids[i] = v[i].ID + } + return _u.AddTranslatedLanguageIDs(ids...) +} + +// Mutation returns the SupportedLocaleMutation object of the builder. +func (_u *SupportedLocaleUpdateOne) Mutation() *SupportedLocaleMutation { + return _u.mutation +} + +// ClearTranslatedCategories clears all "translated_categories" edges to the ServiceCategory entity. +func (_u *SupportedLocaleUpdateOne) ClearTranslatedCategories() *SupportedLocaleUpdateOne { + _u.mutation.ClearTranslatedCategories() + return _u +} + +// RemoveTranslatedCategoryIDs removes the "translated_categories" edge to ServiceCategory entities by IDs. +func (_u *SupportedLocaleUpdateOne) RemoveTranslatedCategoryIDs(ids ...uuid.UUID) *SupportedLocaleUpdateOne { + _u.mutation.RemoveTranslatedCategoryIDs(ids...) + return _u +} + +// RemoveTranslatedCategories removes "translated_categories" edges to ServiceCategory entities. +func (_u *SupportedLocaleUpdateOne) RemoveTranslatedCategories(v ...*ServiceCategory) *SupportedLocaleUpdateOne { + ids := make([]uuid.UUID, len(v)) + for i := range v { + ids[i] = v[i].ID + } + return _u.RemoveTranslatedCategoryIDs(ids...) +} + +// ClearTranslatedLanguages clears all "translated_languages" edges to the SpokenLanguage entity. +func (_u *SupportedLocaleUpdateOne) ClearTranslatedLanguages() *SupportedLocaleUpdateOne { + _u.mutation.ClearTranslatedLanguages() + return _u +} + +// RemoveTranslatedLanguageIDs removes the "translated_languages" edge to SpokenLanguage entities by IDs. +func (_u *SupportedLocaleUpdateOne) RemoveTranslatedLanguageIDs(ids ...string) *SupportedLocaleUpdateOne { + _u.mutation.RemoveTranslatedLanguageIDs(ids...) + return _u +} + +// RemoveTranslatedLanguages removes "translated_languages" edges to SpokenLanguage entities. +func (_u *SupportedLocaleUpdateOne) RemoveTranslatedLanguages(v ...*SpokenLanguage) *SupportedLocaleUpdateOne { + ids := make([]string, len(v)) + for i := range v { + ids[i] = v[i].ID + } + return _u.RemoveTranslatedLanguageIDs(ids...) +} + +// Where appends a list predicates to the SupportedLocaleUpdate builder. +func (_u *SupportedLocaleUpdateOne) Where(ps ...predicate.SupportedLocale) *SupportedLocaleUpdateOne { + _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 *SupportedLocaleUpdateOne) Select(field string, fields ...string) *SupportedLocaleUpdateOne { + _u.fields = append([]string{field}, fields...) + return _u +} + +// Save executes the query and returns the updated SupportedLocale entity. +func (_u *SupportedLocaleUpdateOne) Save(ctx context.Context) (*SupportedLocale, error) { + return withHooks(ctx, _u.sqlSave, _u.mutation, _u.hooks) +} + +// SaveX is like Save, but panics if an error occurs. +func (_u *SupportedLocaleUpdateOne) SaveX(ctx context.Context) *SupportedLocale { + node, err := _u.Save(ctx) + if err != nil { + panic(err) + } + return node +} + +// Exec executes the query on the entity. +func (_u *SupportedLocaleUpdateOne) Exec(ctx context.Context) error { + _, err := _u.Save(ctx) + return err +} + +// ExecX is like Exec, but panics if an error occurs. +func (_u *SupportedLocaleUpdateOne) 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 *SupportedLocaleUpdateOne) check() error { + if v, ok := _u.mutation.SortOrder(); ok { + if err := supportedlocale.SortOrderValidator(v); err != nil { + return &ValidationError{Name: "sort_order", err: fmt.Errorf(`ent: validator failed for field "SupportedLocale.sort_order": %w`, err)} + } + } + return nil +} + +func (_u *SupportedLocaleUpdateOne) sqlSave(ctx context.Context) (_node *SupportedLocale, err error) { + if err := _u.check(); err != nil { + return _node, err + } + _spec := sqlgraph.NewUpdateSpec(supportedlocale.Table, supportedlocale.Columns, sqlgraph.NewFieldSpec(supportedlocale.FieldID, field.TypeString)) + id, ok := _u.mutation.ID() + if !ok { + return nil, &ValidationError{Name: "id", err: errors.New(`ent: missing "SupportedLocale.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, supportedlocale.FieldID) + for _, f := range fields { + if !supportedlocale.ValidColumn(f) { + return nil, &ValidationError{Name: f, err: fmt.Errorf("ent: invalid field %q for query", f)} + } + if f != supportedlocale.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.Active(); ok { + _spec.SetField(supportedlocale.FieldActive, field.TypeBool, value) + } + if value, ok := _u.mutation.SortOrder(); ok { + _spec.SetField(supportedlocale.FieldSortOrder, field.TypeInt, value) + } + if value, ok := _u.mutation.AddedSortOrder(); ok { + _spec.AddField(supportedlocale.FieldSortOrder, field.TypeInt, value) + } + if _u.mutation.TranslatedCategoriesCleared() { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.M2M, + Inverse: true, + Table: supportedlocale.TranslatedCategoriesTable, + Columns: supportedlocale.TranslatedCategoriesPrimaryKey, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(servicecategory.FieldID, field.TypeUUID), + }, + } + _spec.Edges.Clear = append(_spec.Edges.Clear, edge) + } + if nodes := _u.mutation.RemovedTranslatedCategoriesIDs(); len(nodes) > 0 && !_u.mutation.TranslatedCategoriesCleared() { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.M2M, + Inverse: true, + Table: supportedlocale.TranslatedCategoriesTable, + Columns: supportedlocale.TranslatedCategoriesPrimaryKey, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(servicecategory.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.TranslatedCategoriesIDs(); len(nodes) > 0 { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.M2M, + Inverse: true, + Table: supportedlocale.TranslatedCategoriesTable, + Columns: supportedlocale.TranslatedCategoriesPrimaryKey, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(servicecategory.FieldID, field.TypeUUID), + }, + } + for _, k := range nodes { + edge.Target.Nodes = append(edge.Target.Nodes, k) + } + _spec.Edges.Add = append(_spec.Edges.Add, edge) + } + if _u.mutation.TranslatedLanguagesCleared() { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.M2M, + Inverse: true, + Table: supportedlocale.TranslatedLanguagesTable, + Columns: supportedlocale.TranslatedLanguagesPrimaryKey, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(spokenlanguage.FieldID, field.TypeString), + }, + } + _spec.Edges.Clear = append(_spec.Edges.Clear, edge) + } + if nodes := _u.mutation.RemovedTranslatedLanguagesIDs(); len(nodes) > 0 && !_u.mutation.TranslatedLanguagesCleared() { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.M2M, + Inverse: true, + Table: supportedlocale.TranslatedLanguagesTable, + Columns: supportedlocale.TranslatedLanguagesPrimaryKey, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(spokenlanguage.FieldID, field.TypeString), + }, + } + for _, k := range nodes { + edge.Target.Nodes = append(edge.Target.Nodes, k) + } + _spec.Edges.Clear = append(_spec.Edges.Clear, edge) + } + if nodes := _u.mutation.TranslatedLanguagesIDs(); len(nodes) > 0 { + edge := &sqlgraph.EdgeSpec{ + Rel: sqlgraph.M2M, + Inverse: true, + Table: supportedlocale.TranslatedLanguagesTable, + Columns: supportedlocale.TranslatedLanguagesPrimaryKey, + Bidi: false, + Target: &sqlgraph.EdgeTarget{ + IDSpec: sqlgraph.NewFieldSpec(spokenlanguage.FieldID, field.TypeString), + }, + } + for _, k := range nodes { + edge.Target.Nodes = append(edge.Target.Nodes, k) + } + _spec.Edges.Add = append(_spec.Edges.Add, edge) + } + _node = &SupportedLocale{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{supportedlocale.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/backend/ent/tx.go b/backend/ent/tx.go new file mode 100644 index 0000000..48c5445 --- /dev/null +++ b/backend/ent/tx.go @@ -0,0 +1,264 @@ +// 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 + // AdministrativeArea is the client for interacting with the AdministrativeArea builders. + AdministrativeArea *AdministrativeAreaClient + // ContactRevealDailyLimit is the client for interacting with the ContactRevealDailyLimit builders. + ContactRevealDailyLimit *ContactRevealDailyLimitClient + // ContactRevealEvent is the client for interacting with the ContactRevealEvent builders. + ContactRevealEvent *ContactRevealEventClient + // InternalUser is the client for interacting with the InternalUser builders. + InternalUser *InternalUserClient + // Listing is the client for interacting with the Listing builders. + Listing *ListingClient + // ListingEvent is the client for interacting with the ListingEvent builders. + ListingEvent *ListingEventClient + // ListingMedia is the client for interacting with the ListingMedia builders. + ListingMedia *ListingMediaClient + // Locality is the client for interacting with the Locality builders. + Locality *LocalityClient + // PlatformRole is the client for interacting with the PlatformRole builders. + PlatformRole *PlatformRoleClient + // ProviderContactChannel is the client for interacting with the ProviderContactChannel builders. + ProviderContactChannel *ProviderContactChannelClient + // ProviderProfile is the client for interacting with the ProviderProfile builders. + ProviderProfile *ProviderProfileClient + // ProviderServiceLocality is the client for interacting with the ProviderServiceLocality builders. + ProviderServiceLocality *ProviderServiceLocalityClient + // ProviderSpokenLanguage is the client for interacting with the ProviderSpokenLanguage builders. + ProviderSpokenLanguage *ProviderSpokenLanguageClient + // ServiceCategory is the client for interacting with the ServiceCategory builders. + ServiceCategory *ServiceCategoryClient + // ServiceCategoryTranslation is the client for interacting with the ServiceCategoryTranslation builders. + ServiceCategoryTranslation *ServiceCategoryTranslationClient + // SpokenLanguage is the client for interacting with the SpokenLanguage builders. + SpokenLanguage *SpokenLanguageClient + // SpokenLanguageTranslation is the client for interacting with the SpokenLanguageTranslation builders. + SpokenLanguageTranslation *SpokenLanguageTranslationClient + // SupportedLocale is the client for interacting with the SupportedLocale builders. + SupportedLocale *SupportedLocaleClient + // UserAccount is the client for interacting with the UserAccount builders. + UserAccount *UserAccountClient + + // 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.AdministrativeArea = NewAdministrativeAreaClient(tx.config) + tx.ContactRevealDailyLimit = NewContactRevealDailyLimitClient(tx.config) + tx.ContactRevealEvent = NewContactRevealEventClient(tx.config) + tx.InternalUser = NewInternalUserClient(tx.config) + tx.Listing = NewListingClient(tx.config) + tx.ListingEvent = NewListingEventClient(tx.config) + tx.ListingMedia = NewListingMediaClient(tx.config) + tx.Locality = NewLocalityClient(tx.config) + tx.PlatformRole = NewPlatformRoleClient(tx.config) + tx.ProviderContactChannel = NewProviderContactChannelClient(tx.config) + tx.ProviderProfile = NewProviderProfileClient(tx.config) + tx.ProviderServiceLocality = NewProviderServiceLocalityClient(tx.config) + tx.ProviderSpokenLanguage = NewProviderSpokenLanguageClient(tx.config) + tx.ServiceCategory = NewServiceCategoryClient(tx.config) + tx.ServiceCategoryTranslation = NewServiceCategoryTranslationClient(tx.config) + tx.SpokenLanguage = NewSpokenLanguageClient(tx.config) + tx.SpokenLanguageTranslation = NewSpokenLanguageTranslationClient(tx.config) + tx.SupportedLocale = NewSupportedLocaleClient(tx.config) + tx.UserAccount = NewUserAccountClient(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: AdministrativeArea.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/backend/ent/useraccount.go b/backend/ent/useraccount.go new file mode 100644 index 0000000..d219893 --- /dev/null +++ b/backend/ent/useraccount.go @@ -0,0 +1,140 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "fmt" + "strings" + "time" + + "entgo.io/ent" + "entgo.io/ent/dialect/sql" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/useraccount" + "github.com/google/uuid" +) + +// UserAccount is the model entity for the UserAccount schema. +type UserAccount struct { + config `json:"-"` + // ID of the ent. + ID uuid.UUID `json:"id,omitempty"` + // ProviderEnabled holds the value of the "provider_enabled" field. + ProviderEnabled bool `json:"provider_enabled,omitempty"` + // OnboardingCompletedAt holds the value of the "onboarding_completed_at" field. + OnboardingCompletedAt time.Time `json:"onboarding_completed_at,omitempty"` + // CreatedAt holds the value of the "created_at" field. + CreatedAt time.Time `json:"created_at,omitempty"` + // UpdatedAt holds the value of the "updated_at" field. + UpdatedAt time.Time `json:"updated_at,omitempty"` + selectValues sql.SelectValues +} + +// scanValues returns the types for scanning values from sql.Rows. +func (*UserAccount) scanValues(columns []string) ([]any, error) { + values := make([]any, len(columns)) + for i := range columns { + switch columns[i] { + case useraccount.FieldProviderEnabled: + values[i] = new(sql.NullBool) + case useraccount.FieldOnboardingCompletedAt, useraccount.FieldCreatedAt, useraccount.FieldUpdatedAt: + values[i] = new(sql.NullTime) + case useraccount.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 UserAccount fields. +func (_m *UserAccount) 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 useraccount.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 useraccount.FieldProviderEnabled: + if value, ok := values[i].(*sql.NullBool); !ok { + return fmt.Errorf("unexpected type %T for field provider_enabled", values[i]) + } else if value.Valid { + _m.ProviderEnabled = value.Bool + } + case useraccount.FieldOnboardingCompletedAt: + if value, ok := values[i].(*sql.NullTime); !ok { + return fmt.Errorf("unexpected type %T for field onboarding_completed_at", values[i]) + } else if value.Valid { + _m.OnboardingCompletedAt = value.Time + } + case useraccount.FieldCreatedAt: + if value, ok := values[i].(*sql.NullTime); !ok { + return fmt.Errorf("unexpected type %T for field created_at", values[i]) + } else if value.Valid { + _m.CreatedAt = value.Time + } + case useraccount.FieldUpdatedAt: + if value, ok := values[i].(*sql.NullTime); !ok { + return fmt.Errorf("unexpected type %T for field updated_at", values[i]) + } else if value.Valid { + _m.UpdatedAt = value.Time + } + default: + _m.selectValues.Set(columns[i], values[i]) + } + } + return nil +} + +// Value returns the ent.Value that was dynamically selected and assigned to the UserAccount. +// This includes values selected through modifiers, order, etc. +func (_m *UserAccount) Value(name string) (ent.Value, error) { + return _m.selectValues.Get(name) +} + +// Update returns a builder for updating this UserAccount. +// Note that you need to call UserAccount.Unwrap() before calling this method if this UserAccount +// was returned from a transaction, and the transaction was committed or rolled back. +func (_m *UserAccount) Update() *UserAccountUpdateOne { + return NewUserAccountClient(_m.config).UpdateOne(_m) +} + +// Unwrap unwraps the UserAccount 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 *UserAccount) Unwrap() *UserAccount { + _tx, ok := _m.config.driver.(*txDriver) + if !ok { + panic("ent: UserAccount is not a transactional entity") + } + _m.config.driver = _tx.drv + return _m +} + +// String implements the fmt.Stringer. +func (_m *UserAccount) String() string { + var builder strings.Builder + builder.WriteString("UserAccount(") + builder.WriteString(fmt.Sprintf("id=%v, ", _m.ID)) + builder.WriteString("provider_enabled=") + builder.WriteString(fmt.Sprintf("%v", _m.ProviderEnabled)) + builder.WriteString(", ") + builder.WriteString("onboarding_completed_at=") + builder.WriteString(_m.OnboardingCompletedAt.Format(time.ANSIC)) + builder.WriteString(", ") + builder.WriteString("created_at=") + builder.WriteString(_m.CreatedAt.Format(time.ANSIC)) + builder.WriteString(", ") + builder.WriteString("updated_at=") + builder.WriteString(_m.UpdatedAt.Format(time.ANSIC)) + builder.WriteByte(')') + return builder.String() +} + +// UserAccounts is a parsable slice of UserAccount. +type UserAccounts []*UserAccount diff --git a/backend/ent/useraccount/useraccount.go b/backend/ent/useraccount/useraccount.go new file mode 100644 index 0000000..06da900 --- /dev/null +++ b/backend/ent/useraccount/useraccount.go @@ -0,0 +1,86 @@ +// Code generated by ent, DO NOT EDIT. + +package useraccount + +import ( + "time" + + "entgo.io/ent/dialect/sql" +) + +const ( + // Label holds the string label denoting the useraccount type in the database. + Label = "user_account" + // FieldID holds the string denoting the id field in the database. + FieldID = "internal_user_id" + // FieldProviderEnabled holds the string denoting the provider_enabled field in the database. + FieldProviderEnabled = "provider_enabled" + // FieldOnboardingCompletedAt holds the string denoting the onboarding_completed_at field in the database. + FieldOnboardingCompletedAt = "onboarding_completed_at" + // FieldCreatedAt holds the string denoting the created_at field in the database. + FieldCreatedAt = "created_at" + // FieldUpdatedAt holds the string denoting the updated_at field in the database. + FieldUpdatedAt = "updated_at" + // Table holds the table name of the useraccount in the database. + Table = "user_accounts" +) + +// Columns holds all SQL columns for useraccount fields. +var Columns = []string{ + FieldID, + FieldProviderEnabled, + FieldOnboardingCompletedAt, + FieldCreatedAt, + FieldUpdatedAt, +} + +// 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 ( + // DefaultProviderEnabled holds the default value on creation for the "provider_enabled" field. + DefaultProviderEnabled bool + // DefaultOnboardingCompletedAt holds the default value on creation for the "onboarding_completed_at" field. + DefaultOnboardingCompletedAt func() time.Time + // DefaultCreatedAt holds the default value on creation for the "created_at" field. + DefaultCreatedAt func() time.Time + // DefaultUpdatedAt holds the default value on creation for the "updated_at" field. + DefaultUpdatedAt func() time.Time + // UpdateDefaultUpdatedAt holds the default value on update for the "updated_at" field. + UpdateDefaultUpdatedAt func() time.Time +) + +// OrderOption defines the ordering options for the UserAccount 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() +} + +// ByProviderEnabled orders the results by the provider_enabled field. +func ByProviderEnabled(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldProviderEnabled, opts...).ToFunc() +} + +// ByOnboardingCompletedAt orders the results by the onboarding_completed_at field. +func ByOnboardingCompletedAt(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldOnboardingCompletedAt, opts...).ToFunc() +} + +// ByCreatedAt orders the results by the created_at field. +func ByCreatedAt(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldCreatedAt, opts...).ToFunc() +} + +// ByUpdatedAt orders the results by the updated_at field. +func ByUpdatedAt(opts ...sql.OrderTermOption) OrderOption { + return sql.OrderByField(FieldUpdatedAt, opts...).ToFunc() +} diff --git a/backend/ent/useraccount/where.go b/backend/ent/useraccount/where.go new file mode 100644 index 0000000..7f48636 --- /dev/null +++ b/backend/ent/useraccount/where.go @@ -0,0 +1,221 @@ +// Code generated by ent, DO NOT EDIT. + +package useraccount + +import ( + "time" + + "entgo.io/ent/dialect/sql" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/predicate" + "github.com/google/uuid" +) + +// ID filters vertices based on their ID field. +func ID(id uuid.UUID) predicate.UserAccount { + return predicate.UserAccount(sql.FieldEQ(FieldID, id)) +} + +// IDEQ applies the EQ predicate on the ID field. +func IDEQ(id uuid.UUID) predicate.UserAccount { + return predicate.UserAccount(sql.FieldEQ(FieldID, id)) +} + +// IDNEQ applies the NEQ predicate on the ID field. +func IDNEQ(id uuid.UUID) predicate.UserAccount { + return predicate.UserAccount(sql.FieldNEQ(FieldID, id)) +} + +// IDIn applies the In predicate on the ID field. +func IDIn(ids ...uuid.UUID) predicate.UserAccount { + return predicate.UserAccount(sql.FieldIn(FieldID, ids...)) +} + +// IDNotIn applies the NotIn predicate on the ID field. +func IDNotIn(ids ...uuid.UUID) predicate.UserAccount { + return predicate.UserAccount(sql.FieldNotIn(FieldID, ids...)) +} + +// IDGT applies the GT predicate on the ID field. +func IDGT(id uuid.UUID) predicate.UserAccount { + return predicate.UserAccount(sql.FieldGT(FieldID, id)) +} + +// IDGTE applies the GTE predicate on the ID field. +func IDGTE(id uuid.UUID) predicate.UserAccount { + return predicate.UserAccount(sql.FieldGTE(FieldID, id)) +} + +// IDLT applies the LT predicate on the ID field. +func IDLT(id uuid.UUID) predicate.UserAccount { + return predicate.UserAccount(sql.FieldLT(FieldID, id)) +} + +// IDLTE applies the LTE predicate on the ID field. +func IDLTE(id uuid.UUID) predicate.UserAccount { + return predicate.UserAccount(sql.FieldLTE(FieldID, id)) +} + +// ProviderEnabled applies equality check predicate on the "provider_enabled" field. It's identical to ProviderEnabledEQ. +func ProviderEnabled(v bool) predicate.UserAccount { + return predicate.UserAccount(sql.FieldEQ(FieldProviderEnabled, v)) +} + +// OnboardingCompletedAt applies equality check predicate on the "onboarding_completed_at" field. It's identical to OnboardingCompletedAtEQ. +func OnboardingCompletedAt(v time.Time) predicate.UserAccount { + return predicate.UserAccount(sql.FieldEQ(FieldOnboardingCompletedAt, v)) +} + +// CreatedAt applies equality check predicate on the "created_at" field. It's identical to CreatedAtEQ. +func CreatedAt(v time.Time) predicate.UserAccount { + return predicate.UserAccount(sql.FieldEQ(FieldCreatedAt, v)) +} + +// UpdatedAt applies equality check predicate on the "updated_at" field. It's identical to UpdatedAtEQ. +func UpdatedAt(v time.Time) predicate.UserAccount { + return predicate.UserAccount(sql.FieldEQ(FieldUpdatedAt, v)) +} + +// ProviderEnabledEQ applies the EQ predicate on the "provider_enabled" field. +func ProviderEnabledEQ(v bool) predicate.UserAccount { + return predicate.UserAccount(sql.FieldEQ(FieldProviderEnabled, v)) +} + +// ProviderEnabledNEQ applies the NEQ predicate on the "provider_enabled" field. +func ProviderEnabledNEQ(v bool) predicate.UserAccount { + return predicate.UserAccount(sql.FieldNEQ(FieldProviderEnabled, v)) +} + +// OnboardingCompletedAtEQ applies the EQ predicate on the "onboarding_completed_at" field. +func OnboardingCompletedAtEQ(v time.Time) predicate.UserAccount { + return predicate.UserAccount(sql.FieldEQ(FieldOnboardingCompletedAt, v)) +} + +// OnboardingCompletedAtNEQ applies the NEQ predicate on the "onboarding_completed_at" field. +func OnboardingCompletedAtNEQ(v time.Time) predicate.UserAccount { + return predicate.UserAccount(sql.FieldNEQ(FieldOnboardingCompletedAt, v)) +} + +// OnboardingCompletedAtIn applies the In predicate on the "onboarding_completed_at" field. +func OnboardingCompletedAtIn(vs ...time.Time) predicate.UserAccount { + return predicate.UserAccount(sql.FieldIn(FieldOnboardingCompletedAt, vs...)) +} + +// OnboardingCompletedAtNotIn applies the NotIn predicate on the "onboarding_completed_at" field. +func OnboardingCompletedAtNotIn(vs ...time.Time) predicate.UserAccount { + return predicate.UserAccount(sql.FieldNotIn(FieldOnboardingCompletedAt, vs...)) +} + +// OnboardingCompletedAtGT applies the GT predicate on the "onboarding_completed_at" field. +func OnboardingCompletedAtGT(v time.Time) predicate.UserAccount { + return predicate.UserAccount(sql.FieldGT(FieldOnboardingCompletedAt, v)) +} + +// OnboardingCompletedAtGTE applies the GTE predicate on the "onboarding_completed_at" field. +func OnboardingCompletedAtGTE(v time.Time) predicate.UserAccount { + return predicate.UserAccount(sql.FieldGTE(FieldOnboardingCompletedAt, v)) +} + +// OnboardingCompletedAtLT applies the LT predicate on the "onboarding_completed_at" field. +func OnboardingCompletedAtLT(v time.Time) predicate.UserAccount { + return predicate.UserAccount(sql.FieldLT(FieldOnboardingCompletedAt, v)) +} + +// OnboardingCompletedAtLTE applies the LTE predicate on the "onboarding_completed_at" field. +func OnboardingCompletedAtLTE(v time.Time) predicate.UserAccount { + return predicate.UserAccount(sql.FieldLTE(FieldOnboardingCompletedAt, v)) +} + +// CreatedAtEQ applies the EQ predicate on the "created_at" field. +func CreatedAtEQ(v time.Time) predicate.UserAccount { + return predicate.UserAccount(sql.FieldEQ(FieldCreatedAt, v)) +} + +// CreatedAtNEQ applies the NEQ predicate on the "created_at" field. +func CreatedAtNEQ(v time.Time) predicate.UserAccount { + return predicate.UserAccount(sql.FieldNEQ(FieldCreatedAt, v)) +} + +// CreatedAtIn applies the In predicate on the "created_at" field. +func CreatedAtIn(vs ...time.Time) predicate.UserAccount { + return predicate.UserAccount(sql.FieldIn(FieldCreatedAt, vs...)) +} + +// CreatedAtNotIn applies the NotIn predicate on the "created_at" field. +func CreatedAtNotIn(vs ...time.Time) predicate.UserAccount { + return predicate.UserAccount(sql.FieldNotIn(FieldCreatedAt, vs...)) +} + +// CreatedAtGT applies the GT predicate on the "created_at" field. +func CreatedAtGT(v time.Time) predicate.UserAccount { + return predicate.UserAccount(sql.FieldGT(FieldCreatedAt, v)) +} + +// CreatedAtGTE applies the GTE predicate on the "created_at" field. +func CreatedAtGTE(v time.Time) predicate.UserAccount { + return predicate.UserAccount(sql.FieldGTE(FieldCreatedAt, v)) +} + +// CreatedAtLT applies the LT predicate on the "created_at" field. +func CreatedAtLT(v time.Time) predicate.UserAccount { + return predicate.UserAccount(sql.FieldLT(FieldCreatedAt, v)) +} + +// CreatedAtLTE applies the LTE predicate on the "created_at" field. +func CreatedAtLTE(v time.Time) predicate.UserAccount { + return predicate.UserAccount(sql.FieldLTE(FieldCreatedAt, v)) +} + +// UpdatedAtEQ applies the EQ predicate on the "updated_at" field. +func UpdatedAtEQ(v time.Time) predicate.UserAccount { + return predicate.UserAccount(sql.FieldEQ(FieldUpdatedAt, v)) +} + +// UpdatedAtNEQ applies the NEQ predicate on the "updated_at" field. +func UpdatedAtNEQ(v time.Time) predicate.UserAccount { + return predicate.UserAccount(sql.FieldNEQ(FieldUpdatedAt, v)) +} + +// UpdatedAtIn applies the In predicate on the "updated_at" field. +func UpdatedAtIn(vs ...time.Time) predicate.UserAccount { + return predicate.UserAccount(sql.FieldIn(FieldUpdatedAt, vs...)) +} + +// UpdatedAtNotIn applies the NotIn predicate on the "updated_at" field. +func UpdatedAtNotIn(vs ...time.Time) predicate.UserAccount { + return predicate.UserAccount(sql.FieldNotIn(FieldUpdatedAt, vs...)) +} + +// UpdatedAtGT applies the GT predicate on the "updated_at" field. +func UpdatedAtGT(v time.Time) predicate.UserAccount { + return predicate.UserAccount(sql.FieldGT(FieldUpdatedAt, v)) +} + +// UpdatedAtGTE applies the GTE predicate on the "updated_at" field. +func UpdatedAtGTE(v time.Time) predicate.UserAccount { + return predicate.UserAccount(sql.FieldGTE(FieldUpdatedAt, v)) +} + +// UpdatedAtLT applies the LT predicate on the "updated_at" field. +func UpdatedAtLT(v time.Time) predicate.UserAccount { + return predicate.UserAccount(sql.FieldLT(FieldUpdatedAt, v)) +} + +// UpdatedAtLTE applies the LTE predicate on the "updated_at" field. +func UpdatedAtLTE(v time.Time) predicate.UserAccount { + return predicate.UserAccount(sql.FieldLTE(FieldUpdatedAt, v)) +} + +// And groups predicates with the AND operator between them. +func And(predicates ...predicate.UserAccount) predicate.UserAccount { + return predicate.UserAccount(sql.AndPredicates(predicates...)) +} + +// Or groups predicates with the OR operator between them. +func Or(predicates ...predicate.UserAccount) predicate.UserAccount { + return predicate.UserAccount(sql.OrPredicates(predicates...)) +} + +// Not applies the not operator on the given predicate. +func Not(p predicate.UserAccount) predicate.UserAccount { + return predicate.UserAccount(sql.NotPredicates(p)) +} diff --git a/backend/ent/useraccount_create.go b/backend/ent/useraccount_create.go new file mode 100644 index 0000000..5715b0f --- /dev/null +++ b/backend/ent/useraccount_create.go @@ -0,0 +1,289 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "context" + "errors" + "fmt" + "time" + + "entgo.io/ent/dialect/sql/sqlgraph" + "entgo.io/ent/schema/field" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/useraccount" + "github.com/google/uuid" +) + +// UserAccountCreate is the builder for creating a UserAccount entity. +type UserAccountCreate struct { + config + mutation *UserAccountMutation + hooks []Hook +} + +// SetProviderEnabled sets the "provider_enabled" field. +func (_c *UserAccountCreate) SetProviderEnabled(v bool) *UserAccountCreate { + _c.mutation.SetProviderEnabled(v) + return _c +} + +// SetNillableProviderEnabled sets the "provider_enabled" field if the given value is not nil. +func (_c *UserAccountCreate) SetNillableProviderEnabled(v *bool) *UserAccountCreate { + if v != nil { + _c.SetProviderEnabled(*v) + } + return _c +} + +// SetOnboardingCompletedAt sets the "onboarding_completed_at" field. +func (_c *UserAccountCreate) SetOnboardingCompletedAt(v time.Time) *UserAccountCreate { + _c.mutation.SetOnboardingCompletedAt(v) + return _c +} + +// SetNillableOnboardingCompletedAt sets the "onboarding_completed_at" field if the given value is not nil. +func (_c *UserAccountCreate) SetNillableOnboardingCompletedAt(v *time.Time) *UserAccountCreate { + if v != nil { + _c.SetOnboardingCompletedAt(*v) + } + return _c +} + +// SetCreatedAt sets the "created_at" field. +func (_c *UserAccountCreate) SetCreatedAt(v time.Time) *UserAccountCreate { + _c.mutation.SetCreatedAt(v) + return _c +} + +// SetNillableCreatedAt sets the "created_at" field if the given value is not nil. +func (_c *UserAccountCreate) SetNillableCreatedAt(v *time.Time) *UserAccountCreate { + if v != nil { + _c.SetCreatedAt(*v) + } + return _c +} + +// SetUpdatedAt sets the "updated_at" field. +func (_c *UserAccountCreate) SetUpdatedAt(v time.Time) *UserAccountCreate { + _c.mutation.SetUpdatedAt(v) + return _c +} + +// SetNillableUpdatedAt sets the "updated_at" field if the given value is not nil. +func (_c *UserAccountCreate) SetNillableUpdatedAt(v *time.Time) *UserAccountCreate { + if v != nil { + _c.SetUpdatedAt(*v) + } + return _c +} + +// SetID sets the "id" field. +func (_c *UserAccountCreate) SetID(v uuid.UUID) *UserAccountCreate { + _c.mutation.SetID(v) + return _c +} + +// Mutation returns the UserAccountMutation object of the builder. +func (_c *UserAccountCreate) Mutation() *UserAccountMutation { + return _c.mutation +} + +// Save creates the UserAccount in the database. +func (_c *UserAccountCreate) Save(ctx context.Context) (*UserAccount, error) { + _c.defaults() + return withHooks(ctx, _c.sqlSave, _c.mutation, _c.hooks) +} + +// SaveX calls Save and panics if Save returns an error. +func (_c *UserAccountCreate) SaveX(ctx context.Context) *UserAccount { + v, err := _c.Save(ctx) + if err != nil { + panic(err) + } + return v +} + +// Exec executes the query. +func (_c *UserAccountCreate) Exec(ctx context.Context) error { + _, err := _c.Save(ctx) + return err +} + +// ExecX is like Exec, but panics if an error occurs. +func (_c *UserAccountCreate) 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 *UserAccountCreate) defaults() { + if _, ok := _c.mutation.ProviderEnabled(); !ok { + v := useraccount.DefaultProviderEnabled + _c.mutation.SetProviderEnabled(v) + } + if _, ok := _c.mutation.OnboardingCompletedAt(); !ok { + v := useraccount.DefaultOnboardingCompletedAt() + _c.mutation.SetOnboardingCompletedAt(v) + } + if _, ok := _c.mutation.CreatedAt(); !ok { + v := useraccount.DefaultCreatedAt() + _c.mutation.SetCreatedAt(v) + } + if _, ok := _c.mutation.UpdatedAt(); !ok { + v := useraccount.DefaultUpdatedAt() + _c.mutation.SetUpdatedAt(v) + } +} + +// check runs all checks and user-defined validators on the builder. +func (_c *UserAccountCreate) check() error { + if _, ok := _c.mutation.ProviderEnabled(); !ok { + return &ValidationError{Name: "provider_enabled", err: errors.New(`ent: missing required field "UserAccount.provider_enabled"`)} + } + if _, ok := _c.mutation.OnboardingCompletedAt(); !ok { + return &ValidationError{Name: "onboarding_completed_at", err: errors.New(`ent: missing required field "UserAccount.onboarding_completed_at"`)} + } + if _, ok := _c.mutation.CreatedAt(); !ok { + return &ValidationError{Name: "created_at", err: errors.New(`ent: missing required field "UserAccount.created_at"`)} + } + if _, ok := _c.mutation.UpdatedAt(); !ok { + return &ValidationError{Name: "updated_at", err: errors.New(`ent: missing required field "UserAccount.updated_at"`)} + } + return nil +} + +func (_c *UserAccountCreate) sqlSave(ctx context.Context) (*UserAccount, 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 *UserAccountCreate) createSpec() (*UserAccount, *sqlgraph.CreateSpec) { + var ( + _node = &UserAccount{config: _c.config} + _spec = sqlgraph.NewCreateSpec(useraccount.Table, sqlgraph.NewFieldSpec(useraccount.FieldID, field.TypeUUID)) + ) + if id, ok := _c.mutation.ID(); ok { + _node.ID = id + _spec.ID.Value = &id + } + if value, ok := _c.mutation.ProviderEnabled(); ok { + _spec.SetField(useraccount.FieldProviderEnabled, field.TypeBool, value) + _node.ProviderEnabled = value + } + if value, ok := _c.mutation.OnboardingCompletedAt(); ok { + _spec.SetField(useraccount.FieldOnboardingCompletedAt, field.TypeTime, value) + _node.OnboardingCompletedAt = value + } + if value, ok := _c.mutation.CreatedAt(); ok { + _spec.SetField(useraccount.FieldCreatedAt, field.TypeTime, value) + _node.CreatedAt = value + } + if value, ok := _c.mutation.UpdatedAt(); ok { + _spec.SetField(useraccount.FieldUpdatedAt, field.TypeTime, value) + _node.UpdatedAt = value + } + return _node, _spec +} + +// UserAccountCreateBulk is the builder for creating many UserAccount entities in bulk. +type UserAccountCreateBulk struct { + config + err error + builders []*UserAccountCreate +} + +// Save creates the UserAccount entities in the database. +func (_c *UserAccountCreateBulk) Save(ctx context.Context) ([]*UserAccount, error) { + if _c.err != nil { + return nil, _c.err + } + specs := make([]*sqlgraph.CreateSpec, len(_c.builders)) + nodes := make([]*UserAccount, 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.(*UserAccountMutation) + 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 *UserAccountCreateBulk) SaveX(ctx context.Context) []*UserAccount { + v, err := _c.Save(ctx) + if err != nil { + panic(err) + } + return v +} + +// Exec executes the query. +func (_c *UserAccountCreateBulk) Exec(ctx context.Context) error { + _, err := _c.Save(ctx) + return err +} + +// ExecX is like Exec, but panics if an error occurs. +func (_c *UserAccountCreateBulk) ExecX(ctx context.Context) { + if err := _c.Exec(ctx); err != nil { + panic(err) + } +} diff --git a/backend/ent/useraccount_delete.go b/backend/ent/useraccount_delete.go new file mode 100644 index 0000000..0f62b47 --- /dev/null +++ b/backend/ent/useraccount_delete.go @@ -0,0 +1,88 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "context" + + "entgo.io/ent/dialect/sql" + "entgo.io/ent/dialect/sql/sqlgraph" + "entgo.io/ent/schema/field" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/predicate" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/useraccount" +) + +// UserAccountDelete is the builder for deleting a UserAccount entity. +type UserAccountDelete struct { + config + hooks []Hook + mutation *UserAccountMutation +} + +// Where appends a list predicates to the UserAccountDelete builder. +func (_d *UserAccountDelete) Where(ps ...predicate.UserAccount) *UserAccountDelete { + _d.mutation.Where(ps...) + return _d +} + +// Exec executes the deletion query and returns how many vertices were deleted. +func (_d *UserAccountDelete) 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 *UserAccountDelete) ExecX(ctx context.Context) int { + n, err := _d.Exec(ctx) + if err != nil { + panic(err) + } + return n +} + +func (_d *UserAccountDelete) sqlExec(ctx context.Context) (int, error) { + _spec := sqlgraph.NewDeleteSpec(useraccount.Table, sqlgraph.NewFieldSpec(useraccount.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 +} + +// UserAccountDeleteOne is the builder for deleting a single UserAccount entity. +type UserAccountDeleteOne struct { + _d *UserAccountDelete +} + +// Where appends a list predicates to the UserAccountDelete builder. +func (_d *UserAccountDeleteOne) Where(ps ...predicate.UserAccount) *UserAccountDeleteOne { + _d._d.mutation.Where(ps...) + return _d +} + +// Exec executes the deletion query. +func (_d *UserAccountDeleteOne) Exec(ctx context.Context) error { + n, err := _d._d.Exec(ctx) + switch { + case err != nil: + return err + case n == 0: + return &NotFoundError{useraccount.Label} + default: + return nil + } +} + +// ExecX is like Exec, but panics if an error occurs. +func (_d *UserAccountDeleteOne) ExecX(ctx context.Context) { + if err := _d.Exec(ctx); err != nil { + panic(err) + } +} diff --git a/backend/ent/useraccount_query.go b/backend/ent/useraccount_query.go new file mode 100644 index 0000000..2858282 --- /dev/null +++ b/backend/ent/useraccount_query.go @@ -0,0 +1,528 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "context" + "fmt" + "math" + + "entgo.io/ent" + "entgo.io/ent/dialect/sql" + "entgo.io/ent/dialect/sql/sqlgraph" + "entgo.io/ent/schema/field" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/predicate" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/useraccount" + "github.com/google/uuid" +) + +// UserAccountQuery is the builder for querying UserAccount entities. +type UserAccountQuery struct { + config + ctx *QueryContext + order []useraccount.OrderOption + inters []Interceptor + predicates []predicate.UserAccount + // intermediate query (i.e. traversal path). + sql *sql.Selector + path func(context.Context) (*sql.Selector, error) +} + +// Where adds a new predicate for the UserAccountQuery builder. +func (_q *UserAccountQuery) Where(ps ...predicate.UserAccount) *UserAccountQuery { + _q.predicates = append(_q.predicates, ps...) + return _q +} + +// Limit the number of records to be returned by this query. +func (_q *UserAccountQuery) Limit(limit int) *UserAccountQuery { + _q.ctx.Limit = &limit + return _q +} + +// Offset to start from. +func (_q *UserAccountQuery) Offset(offset int) *UserAccountQuery { + _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 *UserAccountQuery) Unique(unique bool) *UserAccountQuery { + _q.ctx.Unique = &unique + return _q +} + +// Order specifies how the records should be ordered. +func (_q *UserAccountQuery) Order(o ...useraccount.OrderOption) *UserAccountQuery { + _q.order = append(_q.order, o...) + return _q +} + +// First returns the first UserAccount entity from the query. +// Returns a *NotFoundError when no UserAccount was found. +func (_q *UserAccountQuery) First(ctx context.Context) (*UserAccount, 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{useraccount.Label} + } + return nodes[0], nil +} + +// FirstX is like First, but panics if an error occurs. +func (_q *UserAccountQuery) FirstX(ctx context.Context) *UserAccount { + node, err := _q.First(ctx) + if err != nil && !IsNotFound(err) { + panic(err) + } + return node +} + +// FirstID returns the first UserAccount ID from the query. +// Returns a *NotFoundError when no UserAccount ID was found. +func (_q *UserAccountQuery) 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{useraccount.Label} + return + } + return ids[0], nil +} + +// FirstIDX is like FirstID, but panics if an error occurs. +func (_q *UserAccountQuery) FirstIDX(ctx context.Context) uuid.UUID { + id, err := _q.FirstID(ctx) + if err != nil && !IsNotFound(err) { + panic(err) + } + return id +} + +// Only returns a single UserAccount entity found by the query, ensuring it only returns one. +// Returns a *NotSingularError when more than one UserAccount entity is found. +// Returns a *NotFoundError when no UserAccount entities are found. +func (_q *UserAccountQuery) Only(ctx context.Context) (*UserAccount, 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{useraccount.Label} + default: + return nil, &NotSingularError{useraccount.Label} + } +} + +// OnlyX is like Only, but panics if an error occurs. +func (_q *UserAccountQuery) OnlyX(ctx context.Context) *UserAccount { + node, err := _q.Only(ctx) + if err != nil { + panic(err) + } + return node +} + +// OnlyID is like Only, but returns the only UserAccount ID in the query. +// Returns a *NotSingularError when more than one UserAccount ID is found. +// Returns a *NotFoundError when no entities are found. +func (_q *UserAccountQuery) 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{useraccount.Label} + default: + err = &NotSingularError{useraccount.Label} + } + return +} + +// OnlyIDX is like OnlyID, but panics if an error occurs. +func (_q *UserAccountQuery) 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 UserAccounts. +func (_q *UserAccountQuery) All(ctx context.Context) ([]*UserAccount, error) { + ctx = setContextOp(ctx, _q.ctx, ent.OpQueryAll) + if err := _q.prepareQuery(ctx); err != nil { + return nil, err + } + qr := querierAll[[]*UserAccount, *UserAccountQuery]() + return withInterceptors[[]*UserAccount](ctx, _q, qr, _q.inters) +} + +// AllX is like All, but panics if an error occurs. +func (_q *UserAccountQuery) AllX(ctx context.Context) []*UserAccount { + nodes, err := _q.All(ctx) + if err != nil { + panic(err) + } + return nodes +} + +// IDs executes the query and returns a list of UserAccount IDs. +func (_q *UserAccountQuery) 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(useraccount.FieldID).Scan(ctx, &ids); err != nil { + return nil, err + } + return ids, nil +} + +// IDsX is like IDs, but panics if an error occurs. +func (_q *UserAccountQuery) 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 *UserAccountQuery) 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[*UserAccountQuery](), _q.inters) +} + +// CountX is like Count, but panics if an error occurs. +func (_q *UserAccountQuery) 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 *UserAccountQuery) 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 *UserAccountQuery) ExistX(ctx context.Context) bool { + exist, err := _q.Exist(ctx) + if err != nil { + panic(err) + } + return exist +} + +// Clone returns a duplicate of the UserAccountQuery 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 *UserAccountQuery) Clone() *UserAccountQuery { + if _q == nil { + return nil + } + return &UserAccountQuery{ + config: _q.config, + ctx: _q.ctx.Clone(), + order: append([]useraccount.OrderOption{}, _q.order...), + inters: append([]Interceptor{}, _q.inters...), + predicates: append([]predicate.UserAccount{}, _q.predicates...), + // clone intermediate query. + sql: _q.sql.Clone(), + path: _q.path, + } +} + +// GroupBy is used to group vertices by one or more fields/columns. +// It is often used with aggregate functions, like: count, max, mean, min, sum. +// +// Example: +// +// var v []struct { +// ProviderEnabled bool `json:"provider_enabled,omitempty"` +// Count int `json:"count,omitempty"` +// } +// +// client.UserAccount.Query(). +// GroupBy(useraccount.FieldProviderEnabled). +// Aggregate(ent.Count()). +// Scan(ctx, &v) +func (_q *UserAccountQuery) GroupBy(field string, fields ...string) *UserAccountGroupBy { + _q.ctx.Fields = append([]string{field}, fields...) + grbuild := &UserAccountGroupBy{build: _q} + grbuild.flds = &_q.ctx.Fields + grbuild.label = useraccount.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 { +// ProviderEnabled bool `json:"provider_enabled,omitempty"` +// } +// +// client.UserAccount.Query(). +// Select(useraccount.FieldProviderEnabled). +// Scan(ctx, &v) +func (_q *UserAccountQuery) Select(fields ...string) *UserAccountSelect { + _q.ctx.Fields = append(_q.ctx.Fields, fields...) + sbuild := &UserAccountSelect{UserAccountQuery: _q} + sbuild.label = useraccount.Label + sbuild.flds, sbuild.scan = &_q.ctx.Fields, sbuild.Scan + return sbuild +} + +// Aggregate returns a UserAccountSelect configured with the given aggregations. +func (_q *UserAccountQuery) Aggregate(fns ...AggregateFunc) *UserAccountSelect { + return _q.Select().Aggregate(fns...) +} + +func (_q *UserAccountQuery) 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 !useraccount.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 *UserAccountQuery) sqlAll(ctx context.Context, hooks ...queryHook) ([]*UserAccount, error) { + var ( + nodes = []*UserAccount{} + _spec = _q.querySpec() + ) + _spec.ScanValues = func(columns []string) ([]any, error) { + return (*UserAccount).scanValues(nil, columns) + } + _spec.Assign = func(columns []string, values []any) error { + node := &UserAccount{config: _q.config} + nodes = append(nodes, node) + return node.assignValues(columns, values) + } + for i := range hooks { + hooks[i](ctx, _spec) + } + if err := sqlgraph.QueryNodes(ctx, _q.driver, _spec); err != nil { + return nil, err + } + if len(nodes) == 0 { + return nodes, nil + } + return nodes, nil +} + +func (_q *UserAccountQuery) sqlCount(ctx context.Context) (int, error) { + _spec := _q.querySpec() + _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 *UserAccountQuery) querySpec() *sqlgraph.QuerySpec { + _spec := sqlgraph.NewQuerySpec(useraccount.Table, useraccount.Columns, sqlgraph.NewFieldSpec(useraccount.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, useraccount.FieldID) + for i := range fields { + if fields[i] != useraccount.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 *UserAccountQuery) sqlQuery(ctx context.Context) *sql.Selector { + builder := sql.Dialect(_q.driver.Dialect()) + t1 := builder.Table(useraccount.Table) + columns := _q.ctx.Fields + if len(columns) == 0 { + columns = useraccount.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 _, 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 +} + +// UserAccountGroupBy is the group-by builder for UserAccount entities. +type UserAccountGroupBy struct { + selector + build *UserAccountQuery +} + +// Aggregate adds the given aggregation functions to the group-by query. +func (_g *UserAccountGroupBy) Aggregate(fns ...AggregateFunc) *UserAccountGroupBy { + _g.fns = append(_g.fns, fns...) + return _g +} + +// Scan applies the selector query and scans the result into the given value. +func (_g *UserAccountGroupBy) 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[*UserAccountQuery, *UserAccountGroupBy](ctx, _g.build, _g, _g.build.inters, v) +} + +func (_g *UserAccountGroupBy) sqlScan(ctx context.Context, root *UserAccountQuery, 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) +} + +// UserAccountSelect is the builder for selecting fields of UserAccount entities. +type UserAccountSelect struct { + *UserAccountQuery + selector +} + +// Aggregate adds the given aggregation functions to the selector query. +func (_s *UserAccountSelect) Aggregate(fns ...AggregateFunc) *UserAccountSelect { + _s.fns = append(_s.fns, fns...) + return _s +} + +// Scan applies the selector query and scans the result into the given value. +func (_s *UserAccountSelect) 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[*UserAccountQuery, *UserAccountSelect](ctx, _s.UserAccountQuery, _s, _s.inters, v) +} + +func (_s *UserAccountSelect) sqlScan(ctx context.Context, root *UserAccountQuery, 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/backend/ent/useraccount_update.go b/backend/ent/useraccount_update.go new file mode 100644 index 0000000..9bba537 --- /dev/null +++ b/backend/ent/useraccount_update.go @@ -0,0 +1,246 @@ +// Code generated by ent, DO NOT EDIT. + +package ent + +import ( + "context" + "errors" + "fmt" + "time" + + "entgo.io/ent/dialect/sql" + "entgo.io/ent/dialect/sql/sqlgraph" + "entgo.io/ent/schema/field" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/predicate" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/useraccount" +) + +// UserAccountUpdate is the builder for updating UserAccount entities. +type UserAccountUpdate struct { + config + hooks []Hook + mutation *UserAccountMutation +} + +// Where appends a list predicates to the UserAccountUpdate builder. +func (_u *UserAccountUpdate) Where(ps ...predicate.UserAccount) *UserAccountUpdate { + _u.mutation.Where(ps...) + return _u +} + +// SetProviderEnabled sets the "provider_enabled" field. +func (_u *UserAccountUpdate) SetProviderEnabled(v bool) *UserAccountUpdate { + _u.mutation.SetProviderEnabled(v) + return _u +} + +// SetNillableProviderEnabled sets the "provider_enabled" field if the given value is not nil. +func (_u *UserAccountUpdate) SetNillableProviderEnabled(v *bool) *UserAccountUpdate { + if v != nil { + _u.SetProviderEnabled(*v) + } + return _u +} + +// SetUpdatedAt sets the "updated_at" field. +func (_u *UserAccountUpdate) SetUpdatedAt(v time.Time) *UserAccountUpdate { + _u.mutation.SetUpdatedAt(v) + return _u +} + +// Mutation returns the UserAccountMutation object of the builder. +func (_u *UserAccountUpdate) Mutation() *UserAccountMutation { + return _u.mutation +} + +// Save executes the query and returns the number of nodes affected by the update operation. +func (_u *UserAccountUpdate) Save(ctx context.Context) (int, error) { + _u.defaults() + return withHooks(ctx, _u.sqlSave, _u.mutation, _u.hooks) +} + +// SaveX is like Save, but panics if an error occurs. +func (_u *UserAccountUpdate) SaveX(ctx context.Context) int { + affected, err := _u.Save(ctx) + if err != nil { + panic(err) + } + return affected +} + +// Exec executes the query. +func (_u *UserAccountUpdate) Exec(ctx context.Context) error { + _, err := _u.Save(ctx) + return err +} + +// ExecX is like Exec, but panics if an error occurs. +func (_u *UserAccountUpdate) ExecX(ctx context.Context) { + if err := _u.Exec(ctx); err != nil { + panic(err) + } +} + +// defaults sets the default values of the builder before save. +func (_u *UserAccountUpdate) defaults() { + if _, ok := _u.mutation.UpdatedAt(); !ok { + v := useraccount.UpdateDefaultUpdatedAt() + _u.mutation.SetUpdatedAt(v) + } +} + +func (_u *UserAccountUpdate) sqlSave(ctx context.Context) (_node int, err error) { + _spec := sqlgraph.NewUpdateSpec(useraccount.Table, useraccount.Columns, sqlgraph.NewFieldSpec(useraccount.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.ProviderEnabled(); ok { + _spec.SetField(useraccount.FieldProviderEnabled, field.TypeBool, value) + } + if value, ok := _u.mutation.UpdatedAt(); ok { + _spec.SetField(useraccount.FieldUpdatedAt, field.TypeTime, value) + } + if _node, err = sqlgraph.UpdateNodes(ctx, _u.driver, _spec); err != nil { + if _, ok := err.(*sqlgraph.NotFoundError); ok { + err = &NotFoundError{useraccount.Label} + } else if sqlgraph.IsConstraintError(err) { + err = &ConstraintError{msg: err.Error(), wrap: err} + } + return 0, err + } + _u.mutation.done = true + return _node, nil +} + +// UserAccountUpdateOne is the builder for updating a single UserAccount entity. +type UserAccountUpdateOne struct { + config + fields []string + hooks []Hook + mutation *UserAccountMutation +} + +// SetProviderEnabled sets the "provider_enabled" field. +func (_u *UserAccountUpdateOne) SetProviderEnabled(v bool) *UserAccountUpdateOne { + _u.mutation.SetProviderEnabled(v) + return _u +} + +// SetNillableProviderEnabled sets the "provider_enabled" field if the given value is not nil. +func (_u *UserAccountUpdateOne) SetNillableProviderEnabled(v *bool) *UserAccountUpdateOne { + if v != nil { + _u.SetProviderEnabled(*v) + } + return _u +} + +// SetUpdatedAt sets the "updated_at" field. +func (_u *UserAccountUpdateOne) SetUpdatedAt(v time.Time) *UserAccountUpdateOne { + _u.mutation.SetUpdatedAt(v) + return _u +} + +// Mutation returns the UserAccountMutation object of the builder. +func (_u *UserAccountUpdateOne) Mutation() *UserAccountMutation { + return _u.mutation +} + +// Where appends a list predicates to the UserAccountUpdate builder. +func (_u *UserAccountUpdateOne) Where(ps ...predicate.UserAccount) *UserAccountUpdateOne { + _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 *UserAccountUpdateOne) Select(field string, fields ...string) *UserAccountUpdateOne { + _u.fields = append([]string{field}, fields...) + return _u +} + +// Save executes the query and returns the updated UserAccount entity. +func (_u *UserAccountUpdateOne) Save(ctx context.Context) (*UserAccount, error) { + _u.defaults() + return withHooks(ctx, _u.sqlSave, _u.mutation, _u.hooks) +} + +// SaveX is like Save, but panics if an error occurs. +func (_u *UserAccountUpdateOne) SaveX(ctx context.Context) *UserAccount { + node, err := _u.Save(ctx) + if err != nil { + panic(err) + } + return node +} + +// Exec executes the query on the entity. +func (_u *UserAccountUpdateOne) Exec(ctx context.Context) error { + _, err := _u.Save(ctx) + return err +} + +// ExecX is like Exec, but panics if an error occurs. +func (_u *UserAccountUpdateOne) ExecX(ctx context.Context) { + if err := _u.Exec(ctx); err != nil { + panic(err) + } +} + +// defaults sets the default values of the builder before save. +func (_u *UserAccountUpdateOne) defaults() { + if _, ok := _u.mutation.UpdatedAt(); !ok { + v := useraccount.UpdateDefaultUpdatedAt() + _u.mutation.SetUpdatedAt(v) + } +} + +func (_u *UserAccountUpdateOne) sqlSave(ctx context.Context) (_node *UserAccount, err error) { + _spec := sqlgraph.NewUpdateSpec(useraccount.Table, useraccount.Columns, sqlgraph.NewFieldSpec(useraccount.FieldID, field.TypeUUID)) + id, ok := _u.mutation.ID() + if !ok { + return nil, &ValidationError{Name: "id", err: errors.New(`ent: missing "UserAccount.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, useraccount.FieldID) + for _, f := range fields { + if !useraccount.ValidColumn(f) { + return nil, &ValidationError{Name: f, err: fmt.Errorf("ent: invalid field %q for query", f)} + } + if f != useraccount.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.ProviderEnabled(); ok { + _spec.SetField(useraccount.FieldProviderEnabled, field.TypeBool, value) + } + if value, ok := _u.mutation.UpdatedAt(); ok { + _spec.SetField(useraccount.FieldUpdatedAt, field.TypeTime, value) + } + _node = &UserAccount{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{useraccount.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/backend/go.mod b/backend/go.mod new file mode 100644 index 0000000..557a90c --- /dev/null +++ b/backend/go.mod @@ -0,0 +1,35 @@ +module github.com/SourceSenseiTheRealOne/juntly/backend + +go 1.26 + +toolchain go1.26.6 + +require ( + entgo.io/ent v0.14.6 + github.com/clerk/clerk-sdk-go/v2 v2.7.0 + github.com/google/uuid v1.6.0 + github.com/jackc/pgx/v5 v5.10.0 +) + +require ( + ariga.io/atlas v0.36.2-0.20250730182955-2c6300d0a3e1 // indirect + github.com/agext/levenshtein v1.2.3 // indirect + github.com/apparentlymart/go-textseg/v15 v15.0.0 // indirect + github.com/bmatcuk/doublestar v1.3.4 // indirect + github.com/go-jose/go-jose/v3 v3.0.5 // indirect + github.com/go-openapi/inflect v0.19.0 // indirect + github.com/google/go-cmp v0.6.0 // indirect + github.com/hashicorp/hcl/v2 v2.18.1 // 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/mitchellh/go-wordwrap v1.0.1 // indirect + github.com/rogpeppe/go-internal v1.16.0 // indirect + github.com/zclconf/go-cty v1.14.4 // indirect + github.com/zclconf/go-cty-yaml v1.1.0 // indirect + golang.org/x/crypto v0.52.0 // indirect + golang.org/x/mod v0.37.0 // indirect + golang.org/x/sync v0.21.0 // indirect + golang.org/x/text v0.39.0 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect +) diff --git a/backend/go.sum b/backend/go.sum new file mode 100644 index 0000000..2a431ea --- /dev/null +++ b/backend/go.sum @@ -0,0 +1,115 @@ +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/ent v0.14.6 h1:/f2696BpwuWAEEG6PVGWflg6+Inrpq4pRWuNlWz/Skk= +entgo.io/ent v0.14.6/go.mod h1:z46QBUdGC+BATwsedbDuREfSS0oSCV+csdEYlL4p73s= +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/apparentlymart/go-textseg/v15 v15.0.0 h1:uYvfpb3DyLSCGWnctWKGj857c6ew1u1fNQOlOtuGxQY= +github.com/apparentlymart/go-textseg/v15 v15.0.0/go.mod h1:K8XmNZdhEBkdlyDdvbmmsvpAG721bKi0joRfFdHIWJ4= +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/clerk/clerk-sdk-go/v2 v2.7.0 h1:Bc/hbqpXdPsaNpp9ppOzL3I0R5+8jVeZ5FvgB+bPv0o= +github.com/clerk/clerk-sdk-go/v2 v2.7.0/go.mod h1:ncFmsPwmD5WpGCNW5bJve862j/HQfpkzsshXYV/quJ8= +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/go-jose/go-jose/v3 v3.0.5 h1:BLLJWbC4nMZOfuPVxoZIxeYsn6Nl2r1fITaJ78UQlVQ= +github.com/go-jose/go-jose/v3 v3.0.5/go.mod h1:5b+7YgP7ZICgJDBdfjZaIt+H/9L9T/YQrVfLAMboGkQ= +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/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +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/hcl/v2 v2.18.1 h1:6nxnOJFku1EuSawSD81fuviYUV8DxFr3fp2dUi3ZYSo= +github.com/hashicorp/hcl/v2 v2.18.1/go.mod h1:ThLC89FV4p9MPW804KVbe/cEXoQ8NZEh+JtMeeGErHE= +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.10.0 h1:VhSvgU2jSli8o3AqIEOTJr7rZwAEUVo4E4XhR94Zfr0= +github.com/jackc/pgx/v5 v5.10.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-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/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +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/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/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= +github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= +github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= +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= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= +golang.org/x/crypto v0.52.0 h1:RMs7fP2rXdep0CftQlK8Uf+kibLm7qkCcradZWYz988= +golang.org/x/crypto v0.52.0/go.mod h1:1QgfPxDqh0T2M/elOJtp9RvuR95kVjir0e6/BvEmGbc= +golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= +golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/mod v0.37.0 h1:vF1DjpVEshcIqoEaauuHebaLk1O1forxjxBaVn884JQ= +golang.org/x/mod v0.37.0/go.mod h1:m8S8VeM9r4dzDwjrKO0a1sZP3YjeMamRRlD+fmR2Q/0= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= +golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.21.0 h1:HLII4xRRTtCRkxYp4HNFF0Js/Og6q2i++KXbg0gHCwM= +golang.org/x/sync v0.21.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= +golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= +golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= +golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/text v0.39.0 h1:UbZz4pLOvn600D6Oh6GGEI6VAmndrEBLv8/6BEXzyus= +golang.org/x/text v0.39.0/go.mod h1:3UwRclnC2g0TU9x8PZiyfOajCd1zaUNHF9cvqcQZ+ZM= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= +golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +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/backend/internal/accounts/ent_repository.go b/backend/internal/accounts/ent_repository.go new file mode 100644 index 0000000..c5c332f --- /dev/null +++ b/backend/internal/accounts/ent_repository.go @@ -0,0 +1,75 @@ +package accounts + +import ( + "context" + "errors" + "time" + + "github.com/SourceSenseiTheRealOne/juntly/backend/ent" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/useraccount" + "github.com/google/uuid" +) + +type entRepository struct { + client *ent.Client +} + +func NewEntRepository(client *ent.Client) Repository { + return entRepository{client: client} +} + +func (r entRepository) FindByInternalUserID(ctx context.Context, internalUserID uuid.UUID) (Record, bool, error) { + if r.client == nil { + return Record{}, false, errors.New("Ent client is nil") + } + + entity, err := r.client.UserAccount.Query().Where(useraccount.IDEQ(internalUserID)).Only(ctx) + if err != nil { + if ent.IsNotFound(err) { + return Record{}, false, nil + } + return Record{}, false, err + } + return recordFromEnt(entity), true, nil +} + +func (r entRepository) Create(ctx context.Context, internalUserID uuid.UUID) (Record, error) { + if r.client == nil { + return Record{}, errors.New("Ent client is nil") + } + + entity, err := r.client.UserAccount.Create().SetID(internalUserID).Save(ctx) + if err != nil { + if ent.IsConstraintError(err) { + return Record{}, ErrAccountConflict + } + return Record{}, err + } + return recordFromEnt(entity), nil +} + +func (r entRepository) SetProviderEnabled(ctx context.Context, internalUserID uuid.UUID, enabled bool) (Record, error) { + if r.client == nil { + return Record{}, errors.New("Ent client is nil") + } + + entity, err := r.client.UserAccount.UpdateOneID(internalUserID).SetProviderEnabled(enabled).Save(ctx) + if err != nil { + return Record{}, err + } + return recordFromEnt(entity), nil +} + +func recordFromEnt(entity *ent.UserAccount) Record { + return Record{ + InternalUserID: entity.ID, + ProviderEnabled: entity.ProviderEnabled, + OnboardingCompletedAt: normalizeDatabaseTime(entity.OnboardingCompletedAt), + CreatedAt: normalizeDatabaseTime(entity.CreatedAt), + UpdatedAt: normalizeDatabaseTime(entity.UpdatedAt), + } +} + +func normalizeDatabaseTime(value time.Time) time.Time { + return value.UTC().Truncate(time.Microsecond) +} diff --git a/backend/internal/accounts/ent_repository_test.go b/backend/internal/accounts/ent_repository_test.go new file mode 100644 index 0000000..a95771c --- /dev/null +++ b/backend/internal/accounts/ent_repository_test.go @@ -0,0 +1,165 @@ +package accounts + +import ( + "context" + "database/sql" + "os" + "sync" + "testing" + + "entgo.io/ent/dialect" + entsql "entgo.io/ent/dialect/sql" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/useraccount" + "github.com/SourceSenseiTheRealOne/juntly/backend/internal/users" + "github.com/google/uuid" + _ "github.com/jackc/pgx/v5/stdlib" +) + +func TestEntRepositoryCreatesFindsAndUpdatesAccount(t *testing.T) { + client := openAccountIntegrationClient(t) + ctx := context.Background() + identity := users.VerifiedIdentity{Subject: "test_account_" + uuid.NewString()} + internalUser, _, err := users.NewService(users.NewEntRepository(client)).Reconcile(ctx, identity) + if err != nil { + t.Fatalf("reconcile internal user: %v", err) + } + t.Cleanup(func() { + if err := client.InternalUser.DeleteOneID(internalUser.ID).Exec(ctx); err != nil { + t.Errorf("cleanup internal user: %v", err) + } + }) + + repository := NewEntRepository(client) + created, err := repository.Create(ctx, internalUser.ID) + if err != nil { + t.Fatalf("create account: %v", err) + } + if created.InternalUserID != internalUser.ID { + t.Fatalf("internal user ID = %s, want %s", created.InternalUserID, internalUser.ID) + } + if created.ProviderEnabled { + t.Fatal("provider capability must default to false") + } + + found, exists, err := repository.FindByInternalUserID(ctx, internalUser.ID) + if err != nil { + t.Fatalf("find account: %v", err) + } + if !exists || found != created { + t.Fatalf("found = %#v exists=%t, want %#v true", found, exists, created) + } + + enabled, err := repository.SetProviderEnabled(ctx, internalUser.ID, true) + if err != nil { + t.Fatalf("enable provider capability: %v", err) + } + if !enabled.ProviderEnabled { + t.Fatal("provider capability was not enabled") + } + if enabled.OnboardingCompletedAt != created.OnboardingCompletedAt { + t.Fatalf("onboarding timestamp changed: got %s want %s", enabled.OnboardingCompletedAt, created.OnboardingCompletedAt) + } + + disabled, err := repository.SetProviderEnabled(ctx, internalUser.ID, false) + if err != nil { + t.Fatalf("disable provider capability: %v", err) + } + if disabled.ProviderEnabled { + t.Fatal("provider capability was not disabled") + } + if disabled.OnboardingCompletedAt != created.OnboardingCompletedAt { + t.Fatalf("onboarding timestamp changed: got %s want %s", disabled.OnboardingCompletedAt, created.OnboardingCompletedAt) + } +} + +func TestConcurrentFirstAccountReadsProduceOneStableRow(t *testing.T) { + client := openAccountIntegrationClient(t) + ctx := context.Background() + identity := users.VerifiedIdentity{Subject: "test_account_race_" + uuid.NewString()} + identityService := users.NewService(users.NewEntRepository(client)) + accountService := NewService(identityService, NewEntRepository(client)) + + const attempts = 8 + start := make(chan struct{}) + results := make(chan Account, attempts) + errs := make(chan error, attempts) + var workers sync.WaitGroup + for range attempts { + workers.Add(1) + go func() { + defer workers.Done() + <-start + account, err := accountService.Get(ctx, identity) + if err != nil { + errs <- err + return + } + results <- account + }() + } + close(start) + workers.Wait() + close(results) + close(errs) + + for err := range errs { + t.Fatalf("get account: %v", err) + } + + var stable Account + for account := range results { + if stable.OnboardingCompletedAt.IsZero() { + stable = account + continue + } + if account != stable { + t.Fatalf("account = %#v, want stable %#v", account, stable) + } + } + if stable.OnboardingCompletedAt.IsZero() { + t.Fatal("no account result") + } + if !stable.CustomerEnabled || stable.ProviderEnabled { + t.Fatalf("capabilities = customer:%t provider:%t, want true:false", stable.CustomerEnabled, stable.ProviderEnabled) + } + + internalUser, _, err := identityService.Reconcile(ctx, identity) + if err != nil { + t.Fatalf("reload internal user: %v", err) + } + t.Cleanup(func() { + if err := client.InternalUser.DeleteOneID(internalUser.ID).Exec(ctx); err != nil { + t.Errorf("cleanup internal user: %v", err) + } + }) + + count, err := client.UserAccount.Query().Where(useraccount.IDEQ(internalUser.ID)).Count(ctx) + if err != nil { + t.Fatalf("count account rows: %v", err) + } + if count != 1 { + t.Fatalf("account row count = %d, want 1", count) + } +} + +func openAccountIntegrationClient(t *testing.T) *ent.Client { + t.Helper() + + databaseURL := os.Getenv("TEST_DATABASE_URL") + if databaseURL == "" { + t.Skip("TEST_DATABASE_URL is required for PostgreSQL integration tests") + } + + database, err := sql.Open("pgx", databaseURL) + if err != nil { + t.Fatalf("open pgx database: %v", err) + } + client := ent.NewClient(ent.Driver(entsql.OpenDB(dialect.Postgres, database))) + t.Cleanup(func() { + if err := client.Close(); err != nil { + t.Errorf("close Ent client: %v", err) + } + }) + return client +} diff --git a/backend/internal/accounts/model.go b/backend/internal/accounts/model.go new file mode 100644 index 0000000..0aad287 --- /dev/null +++ b/backend/internal/accounts/model.go @@ -0,0 +1,29 @@ +package accounts + +import ( + "time" + + "github.com/google/uuid" +) + +type Account struct { + CustomerEnabled bool + ProviderEnabled bool + OnboardingCompletedAt time.Time +} + +type Record struct { + InternalUserID uuid.UUID + ProviderEnabled bool + OnboardingCompletedAt time.Time + CreatedAt time.Time + UpdatedAt time.Time +} + +func accountFromRecord(record Record) Account { + return Account{ + CustomerEnabled: true, + ProviderEnabled: record.ProviderEnabled, + OnboardingCompletedAt: record.OnboardingCompletedAt, + } +} diff --git a/backend/internal/accounts/repository.go b/backend/internal/accounts/repository.go new file mode 100644 index 0000000..7553ae0 --- /dev/null +++ b/backend/internal/accounts/repository.go @@ -0,0 +1,20 @@ +package accounts + +import ( + "context" + "errors" + + "github.com/google/uuid" +) + +var ( + ErrAccountConflict = errors.New("user account conflict") + ErrUnavailable = errors.New("user account persistence unavailable") + ErrInvalidIdentity = errors.New("invalid verified identity") +) + +type Repository interface { + FindByInternalUserID(context.Context, uuid.UUID) (Record, bool, error) + Create(context.Context, uuid.UUID) (Record, error) + SetProviderEnabled(context.Context, uuid.UUID, bool) (Record, error) +} diff --git a/backend/internal/accounts/service.go b/backend/internal/accounts/service.go new file mode 100644 index 0000000..9bd089a --- /dev/null +++ b/backend/internal/accounts/service.go @@ -0,0 +1,83 @@ +package accounts + +import ( + "context" + "errors" + + "github.com/SourceSenseiTheRealOne/juntly/backend/internal/users" +) + +type IdentityReconciler interface { + Reconcile(context.Context, users.VerifiedIdentity) (users.InternalUser, bool, error) +} + +type Service interface { + Get(context.Context, users.VerifiedIdentity) (Account, error) + SetProviderEnabled(context.Context, users.VerifiedIdentity, bool) (Account, error) +} + +type service struct { + identities IdentityReconciler + repository Repository +} + +func NewService(identities IdentityReconciler, repository Repository) Service { + return service{identities: identities, repository: repository} +} + +func (s service) Get(ctx context.Context, identity users.VerifiedIdentity) (Account, error) { + record, err := s.reconcileAccount(ctx, identity) + if err != nil { + return Account{}, err + } + return accountFromRecord(record), nil +} + +func (s service) SetProviderEnabled(ctx context.Context, identity users.VerifiedIdentity, enabled bool) (Account, error) { + record, err := s.reconcileAccount(ctx, identity) + if err != nil { + return Account{}, err + } + + updated, err := s.repository.SetProviderEnabled(ctx, record.InternalUserID, enabled) + if err != nil { + return Account{}, ErrUnavailable + } + return accountFromRecord(updated), nil +} + +func (s service) reconcileAccount(ctx context.Context, identity users.VerifiedIdentity) (Record, error) { + if s.identities == nil || s.repository == nil { + return Record{}, ErrUnavailable + } + + internalUser, _, err := s.identities.Reconcile(ctx, identity) + if err != nil { + if errors.Is(err, users.ErrInvalidIdentity) { + return Record{}, ErrInvalidIdentity + } + return Record{}, ErrUnavailable + } + + existing, found, err := s.repository.FindByInternalUserID(ctx, internalUser.ID) + if err != nil { + return Record{}, ErrUnavailable + } + if found { + return existing, nil + } + + created, err := s.repository.Create(ctx, internalUser.ID) + if err == nil { + return created, nil + } + if !errors.Is(err, ErrAccountConflict) { + return Record{}, ErrUnavailable + } + + winner, found, err := s.repository.FindByInternalUserID(ctx, internalUser.ID) + if err != nil || !found { + return Record{}, ErrUnavailable + } + return winner, nil +} diff --git a/backend/internal/accounts/service_test.go b/backend/internal/accounts/service_test.go new file mode 100644 index 0000000..d9d80a4 --- /dev/null +++ b/backend/internal/accounts/service_test.go @@ -0,0 +1,234 @@ +package accounts + +import ( + "context" + "errors" + "testing" + "time" + + "github.com/SourceSenseiTheRealOne/juntly/backend/internal/users" + "github.com/google/uuid" +) + +func TestServiceRejectsInvalidVerifiedIdentityBeforeAccountRepositoryAccess(t *testing.T) { + t.Parallel() + + reconciler := &recordingReconciler{err: users.ErrInvalidIdentity} + repository := &recordingRepository{} + + _, err := NewService(reconciler, repository).Get(context.Background(), users.VerifiedIdentity{}) + + if !errors.Is(err, ErrInvalidIdentity) { + t.Fatalf("error = %v, want ErrInvalidIdentity", err) + } + if repository.findCalls != 0 || repository.createCalls != 0 || repository.updateCalls != 0 { + t.Fatalf("repository calls = find:%d create:%d update:%d, want none", repository.findCalls, repository.createCalls, repository.updateCalls) + } +} + +func TestServiceGetCreatesFirstAccountWithImplicitCustomerCapability(t *testing.T) { + t.Parallel() + + internalUser := testInternalUser() + record := testRecord(internalUser.ID, false) + repository := &recordingRepository{createResult: record} + + account, err := NewService(&recordingReconciler{user: internalUser}, repository).Get( + context.Background(), + users.VerifiedIdentity{Subject: "user_123"}, + ) + + if err != nil { + t.Fatalf("get account: %v", err) + } + assertAccountMatchesRecord(t, account, record) + if !account.CustomerEnabled { + t.Fatal("customer capability must always be enabled") + } + if repository.findCalls != 1 || repository.createCalls != 1 { + t.Fatalf("repository calls = find:%d create:%d, want 1:1", repository.findCalls, repository.createCalls) + } +} + +func TestServiceGetReturnsExistingAccountWithoutCreate(t *testing.T) { + t.Parallel() + + internalUser := testInternalUser() + record := testRecord(internalUser.ID, true) + repository := &recordingRepository{findResults: []accountFindResult{{record: record, found: true}}} + + account, err := NewService(&recordingReconciler{user: internalUser}, repository).Get( + context.Background(), + users.VerifiedIdentity{Subject: "user_123"}, + ) + + if err != nil { + t.Fatalf("get account: %v", err) + } + assertAccountMatchesRecord(t, account, record) + if repository.createCalls != 0 { + t.Fatalf("create calls = %d, want 0", repository.createCalls) + } +} + +func TestServiceGetReloadsConcurrentCreationWinner(t *testing.T) { + t.Parallel() + + internalUser := testInternalUser() + winner := testRecord(internalUser.ID, false) + repository := &recordingRepository{ + findResults: []accountFindResult{{}, {record: winner, found: true}}, + createErr: ErrAccountConflict, + } + + account, err := NewService(&recordingReconciler{user: internalUser}, repository).Get( + context.Background(), + users.VerifiedIdentity{Subject: "user_123"}, + ) + + if err != nil { + t.Fatalf("get account: %v", err) + } + assertAccountMatchesRecord(t, account, winner) + if repository.findCalls != 2 || repository.createCalls != 1 { + t.Fatalf("repository calls = find:%d create:%d, want 2:1", repository.findCalls, repository.createCalls) + } +} + +func TestServiceSetProviderEnabledChangesCapabilityBothDirections(t *testing.T) { + t.Parallel() + + internalUser := testInternalUser() + initial := testRecord(internalUser.ID, false) + enabled := initial + enabled.ProviderEnabled = true + disabled := enabled + disabled.ProviderEnabled = false + repository := &recordingRepository{ + findResults: []accountFindResult{{record: initial, found: true}, {record: enabled, found: true}}, + updateResults: []Record{enabled, disabled}, + } + service := NewService(&recordingReconciler{user: internalUser}, repository) + identity := users.VerifiedIdentity{Subject: "user_123"} + + account, err := service.SetProviderEnabled(context.Background(), identity, true) + if err != nil { + t.Fatalf("enable provider capability: %v", err) + } + assertAccountMatchesRecord(t, account, enabled) + + account, err = service.SetProviderEnabled(context.Background(), identity, false) + if err != nil { + t.Fatalf("disable provider capability: %v", err) + } + assertAccountMatchesRecord(t, account, disabled) + + if repository.updateCalls != 2 || repository.updateValues[0] != true || repository.updateValues[1] != false { + t.Fatalf("updates = calls:%d values:%v, want 2 [true false]", repository.updateCalls, repository.updateValues) + } +} + +func TestServiceMapsRepositoryFailureToUnavailable(t *testing.T) { + t.Parallel() + + internalUser := testInternalUser() + repository := &recordingRepository{findResults: []accountFindResult{{err: errors.New("database unavailable")}}} + + _, err := NewService(&recordingReconciler{user: internalUser}, repository).Get( + context.Background(), + users.VerifiedIdentity{Subject: "user_123"}, + ) + + if !errors.Is(err, ErrUnavailable) { + t.Fatalf("error = %v, want ErrUnavailable", err) + } + if repository.createCalls != 0 || repository.updateCalls != 0 { + t.Fatalf("mutating calls = create:%d update:%d, want none", repository.createCalls, repository.updateCalls) + } +} + +func testInternalUser() users.InternalUser { + return users.InternalUser{ + ID: uuid.MustParse("5809af0d-3cf5-45ac-b120-b45e23a675a4"), + CreatedAt: time.Date(2026, 8, 23, 12, 0, 0, 0, time.UTC), + } +} + +func testRecord(internalUserID uuid.UUID, providerEnabled bool) Record { + return Record{ + InternalUserID: internalUserID, + ProviderEnabled: providerEnabled, + OnboardingCompletedAt: time.Date(2026, 8, 23, 12, 5, 0, 0, time.UTC), + CreatedAt: time.Date(2026, 8, 23, 12, 5, 0, 0, time.UTC), + UpdatedAt: time.Date(2026, 8, 23, 12, 5, 0, 0, time.UTC), + } +} + +func assertAccountMatchesRecord(t *testing.T, account Account, record Record) { + t.Helper() + if account != (Account{ + CustomerEnabled: true, + ProviderEnabled: record.ProviderEnabled, + OnboardingCompletedAt: record.OnboardingCompletedAt, + }) { + t.Fatalf("account = %#v, want record-derived account for %#v", account, record) + } +} + +type recordingReconciler struct { + user users.InternalUser + err error + calls int +} + +func (r *recordingReconciler) Reconcile(_ context.Context, _ users.VerifiedIdentity) (users.InternalUser, bool, error) { + r.calls++ + return r.user, false, r.err +} + +type accountFindResult struct { + record Record + found bool + err error +} + +type recordingRepository struct { + findResults []accountFindResult + createResult Record + createErr error + updateResults []Record + updateErr error + findCalls int + createCalls int + updateCalls int + updateValues []bool +} + +func (r *recordingRepository) FindByInternalUserID(_ context.Context, _ uuid.UUID) (Record, bool, error) { + r.findCalls++ + if len(r.findResults) == 0 { + return Record{}, false, nil + } + result := r.findResults[0] + r.findResults = r.findResults[1:] + return result.record, result.found, result.err +} + +func (r *recordingRepository) Create(_ context.Context, _ uuid.UUID) (Record, error) { + r.createCalls++ + return r.createResult, r.createErr +} + +func (r *recordingRepository) SetProviderEnabled(_ context.Context, _ uuid.UUID, enabled bool) (Record, error) { + r.updateCalls++ + r.updateValues = append(r.updateValues, enabled) + if r.updateErr != nil { + return Record{}, r.updateErr + } + if len(r.updateResults) == 0 { + return Record{}, nil + } + result := r.updateResults[0] + r.updateResults = r.updateResults[1:] + return result, nil +} diff --git a/backend/internal/administration/model.go b/backend/internal/administration/model.go new file mode 100644 index 0000000..a272c5b --- /dev/null +++ b/backend/internal/administration/model.go @@ -0,0 +1,46 @@ +package administration + +import ( + "errors" + "github.com/google/uuid" + "time" +) + +var ( + ErrInvalid = errors.New("administration invalid request") + ErrUnauthorized = errors.New("administration unauthorized") + ErrForbidden = errors.New("administration forbidden") + ErrNotFound = errors.New("administration not found") + ErrUnavailable = errors.New("administration unavailable") +) + +type Metrics struct { + Users int `json:"users"` + Providers int `json:"providers"` + ActiveListings int `json:"activeListings"` + CompletedBookings int `json:"completedBookings"` + PublishedReviews int `json:"publishedReviews"` + OpenReports int `json:"openReports"` +} +type ReportItem struct { + ID uuid.UUID `json:"id"` + ConversationID uuid.UUID `json:"conversationId"` + Reason string `json:"reason"` + CreatedAt time.Time `json:"createdAt"` +} +type ReviewItem struct { + ID uuid.UUID `json:"id"` + Rating int `json:"rating"` + Body string `json:"body"` + State string `json:"state"` + CreatedAt time.Time `json:"createdAt"` +} +type Queue struct { + Reports []ReportItem `json:"reports"` + Reviews []ReviewItem `json:"reviews"` +} +type ModerationAction struct { + Kind string `json:"kind"` + TargetID uuid.UUID `json:"targetId"` + Reason string `json:"reason"` +} diff --git a/backend/internal/administration/service.go b/backend/internal/administration/service.go new file mode 100644 index 0000000..e762c11 --- /dev/null +++ b/backend/internal/administration/service.go @@ -0,0 +1,94 @@ +package administration + +import ( + "context" + "errors" + "github.com/SourceSenseiTheRealOne/juntly/backend/internal/users" + "github.com/google/uuid" + "strings" + "unicode/utf8" +) + +type IdentityReconciler interface { + Reconcile(context.Context, users.VerifiedIdentity) (users.InternalUser, bool, error) +} +type Store interface { + Metrics(context.Context, uuid.UUID) (Metrics, error) + Queue(context.Context, uuid.UUID) (Queue, error) + Moderate(context.Context, uuid.UUID, ModerationAction) error +} +type Service interface { + Metrics(context.Context, users.VerifiedIdentity) (Metrics, error) + Queue(context.Context, users.VerifiedIdentity) (Queue, error) + Moderate(context.Context, users.VerifiedIdentity, ModerationAction) error +} +type service struct { + identities IdentityReconciler + store Store +} + +func NewService(i IdentityReconciler, s Store) Service { return service{identities: i, store: s} } +func (s service) Metrics(ctx context.Context, identity users.VerifiedIdentity) (Metrics, error) { + actor, err := s.actor(ctx, identity) + if err != nil { + return Metrics{}, err + } + v, err := s.store.Metrics(ctx, actor) + return v, normalize(err) +} +func (s service) Queue(ctx context.Context, identity users.VerifiedIdentity) (Queue, error) { + actor, err := s.actor(ctx, identity) + if err != nil { + return Queue{}, err + } + v, err := s.store.Queue(ctx, actor) + return v, normalize(err) +} +func (s service) Moderate(ctx context.Context, identity users.VerifiedIdentity, input ModerationAction) error { + input.Kind = strings.TrimSpace(input.Kind) + input.Reason = strings.TrimSpace(input.Reason) + if input.TargetID == uuid.Nil || !(strings3{"hide_review", "publish_review", "resolve_report"}).contains(input.Kind) || utf8.RuneCountInString(input.Reason) < 5 || utf8.RuneCountInString(input.Reason) > 500 { + return ErrInvalid + } + actor, err := s.actor(ctx, identity) + if err != nil { + return err + } + return normalize(s.store.Moderate(ctx, actor, input)) +} +func (s service) actor(ctx context.Context, identity users.VerifiedIdentity) (uuid.UUID, error) { + if s.identities == nil || s.store == nil { + return uuid.Nil, ErrUnavailable + } + u, _, err := s.identities.Reconcile(ctx, identity) + if err != nil { + if errors.Is(err, users.ErrInvalidIdentity) { + return uuid.Nil, ErrUnauthorized + } + return uuid.Nil, ErrUnavailable + } + if u.ID == uuid.Nil { + return uuid.Nil, ErrUnauthorized + } + return u.ID, nil +} +func normalize(err error) error { + if err == nil { + return nil + } + if errors.Is(err, ErrInvalid) || errors.Is(err, ErrUnauthorized) || errors.Is(err, ErrForbidden) || errors.Is(err, ErrNotFound) { + return err + } + return ErrUnavailable +} + +type strings3 [3]string + +func (v strings3) contains(candidate string) bool { + for _, item := range v { + if item == candidate { + return true + } + } + return false +} diff --git a/backend/internal/administration/service_test.go b/backend/internal/administration/service_test.go new file mode 100644 index 0000000..2f26577 --- /dev/null +++ b/backend/internal/administration/service_test.go @@ -0,0 +1,60 @@ +package administration + +import ( + "context" + "errors" + "testing" + + "github.com/SourceSenseiTheRealOne/juntly/backend/internal/users" + "github.com/google/uuid" +) + +func TestServiceReadsBoundedMetricsForAdministrator(t *testing.T) { + t.Parallel() + actor := uuid.New() + store := &recordingStore{metrics: Metrics{Users: 12, ActiveListings: 5}} + service := NewService(staticIdentity{user: users.InternalUser{ID: actor}}, store) + value, err := service.Metrics(context.Background(), users.VerifiedIdentity{Subject: "admin"}) + if err != nil || value.Users != 12 || store.actor != actor { + t.Fatalf("Metrics() = %#v/%v/%s", value, err, store.actor) + } +} +func TestServiceRejectsInvalidModerationActionBeforeStore(t *testing.T) { + t.Parallel() + store := &recordingStore{} + service := NewService(staticIdentity{user: users.InternalUser{ID: uuid.New()}}, store) + err := service.Moderate(context.Background(), users.VerifiedIdentity{Subject: "admin"}, ModerationAction{}) + if !errors.Is(err, ErrInvalid) { + t.Fatalf("error = %v", err) + } + if store.actor != uuid.Nil { + t.Fatal("store called") + } +} + +type staticIdentity struct { + user users.InternalUser + err error +} + +func (s staticIdentity) Reconcile(context.Context, users.VerifiedIdentity) (users.InternalUser, bool, error) { + return s.user, false, s.err +} + +type recordingStore struct { + metrics Metrics + actor uuid.UUID +} + +func (s *recordingStore) Metrics(_ context.Context, actor uuid.UUID) (Metrics, error) { + s.actor = actor + return s.metrics, nil +} +func (s *recordingStore) Queue(_ context.Context, actor uuid.UUID) (Queue, error) { + s.actor = actor + return Queue{}, nil +} +func (s *recordingStore) Moderate(_ context.Context, actor uuid.UUID, _ ModerationAction) error { + s.actor = actor + return nil +} diff --git a/backend/internal/administration/sql_store.go b/backend/internal/administration/sql_store.go new file mode 100644 index 0000000..6b18148 --- /dev/null +++ b/backend/internal/administration/sql_store.go @@ -0,0 +1,123 @@ +package administration + +import ( + "context" + "database/sql" + "errors" + + "github.com/google/uuid" +) + +type sqlStore struct{ database *sql.DB } + +func NewSQLStore(db *sql.DB) Store { return sqlStore{database: db} } +func (s sqlStore) Metrics(ctx context.Context, actor uuid.UUID) (Metrics, error) { + if ok, err := s.admin(ctx, s.database, actor); err != nil { + return Metrics{}, err + } else if !ok { + return Metrics{}, ErrForbidden + } + var v Metrics + err := s.database.QueryRowContext(ctx, `select (select count(*) from public.internal_users),(select count(*) from public.provider_profiles),(select count(*) from public.listings where state='active'),(select count(*) from public.bookings where state='completed'),(select count(*) from public.reviews where state='published'),(select count(*) from public.conversation_reports where state='open')`).Scan(&v.Users, &v.Providers, &v.ActiveListings, &v.CompletedBookings, &v.PublishedReviews, &v.OpenReports) + return v, err +} +func (s sqlStore) Queue(ctx context.Context, actor uuid.UUID) (Queue, error) { + if ok, err := s.admin(ctx, s.database, actor); err != nil { + return Queue{}, err + } else if !ok { + return Queue{}, ErrForbidden + } + result := Queue{Reports: []ReportItem{}, Reviews: []ReviewItem{}} + reports, err := s.database.QueryContext(ctx, `select id,conversation_id,reason,created_at from public.conversation_reports where state='open' order by created_at,id limit 100`) + if err != nil { + return Queue{}, err + } + defer reports.Close() + for reports.Next() { + var v ReportItem + if err := reports.Scan(&v.ID, &v.ConversationID, &v.Reason, &v.CreatedAt); err != nil { + return Queue{}, err + } + result.Reports = append(result.Reports, v) + } + if err = reports.Err(); err != nil { + return Queue{}, err + } + reviews, err := s.database.QueryContext(ctx, `select id,rating,body,state,created_at from public.reviews order by created_at desc,id limit 100`) + if err != nil { + return Queue{}, err + } + defer reviews.Close() + for reviews.Next() { + var v ReviewItem + if err := reviews.Scan(&v.ID, &v.Rating, &v.Body, &v.State, &v.CreatedAt); err != nil { + return Queue{}, err + } + result.Reviews = append(result.Reviews, v) + } + return result, reviews.Err() +} +func (s sqlStore) Moderate(ctx context.Context, actor uuid.UUID, input ModerationAction) error { + tx, err := s.database.BeginTx(ctx, nil) + if err != nil { + return err + } + defer tx.Rollback() + if ok, err := s.admin(ctx, tx, actor); err != nil { + return err + } else if !ok { + return ErrForbidden + } + targetType := "review" + switch input.Kind { + case "resolve_report": + targetType = "conversation_report" + result, err := tx.ExecContext(ctx, `update public.conversation_reports set state='resolved',resolved_at=timezone('utc',now()),resolved_by_internal_user_id=$1 where id=$2 and state='open'`, actor, input.TargetID) + if err != nil { + return err + } + if n, _ := result.RowsAffected(); n != 1 { + return ErrNotFound + } + case "hide_review", "publish_review": + var provider uuid.UUID + var rating int + var current string + if err := tx.QueryRowContext(ctx, `select provider_internal_user_id,rating,state from public.reviews where id=$1 for update`, input.TargetID).Scan(&provider, &rating, ¤t); errors.Is(err, sql.ErrNoRows) { + return ErrNotFound + } else if err != nil { + return err + } + target := "hidden" + delta := -1 + if input.Kind == "publish_review" { + target = "published" + delta = 1 + } + if current == target { + return ErrInvalid + } + if _, err := tx.ExecContext(ctx, `update public.reviews set state=$1,updated_at=timezone('utc',now()) where id=$2`, target, input.TargetID); err != nil { + return err + } + if _, err := tx.ExecContext(ctx, `update public.provider_rating_aggregates set rating_sum=greatest(0,rating_sum+$1),review_count=greatest(0,review_count+$2),updated_at=timezone('utc',now()) where provider_internal_user_id=$3`, delta*rating, delta, provider); err != nil { + return err + } + default: + return ErrInvalid + } + if _, err := tx.ExecContext(ctx, `insert into public.administration_audit_records(actor_internal_user_id,action,target_type,target_id,reason) values($1,$2,$3,$4,$5)`, actor, input.Kind, targetType, input.TargetID, input.Reason); err != nil { + return err + } + return tx.Commit() +} + +type queryer interface { + QueryRowContext(context.Context, string, ...any) *sql.Row +} + +func (s sqlStore) admin(ctx context.Context, q queryer, actor uuid.UUID) (bool, error) { + var ok bool + err := q.QueryRowContext(ctx, `select exists(select 1 from public.platform_roles where internal_user_id=$1 and role='administrator')`, actor).Scan(&ok) + return ok, err +} diff --git a/backend/internal/authn/clerk_verifier.go b/backend/internal/authn/clerk_verifier.go new file mode 100644 index 0000000..d07d078 --- /dev/null +++ b/backend/internal/authn/clerk_verifier.go @@ -0,0 +1,95 @@ +package authn + +import ( + "context" + "errors" + "strings" + "time" + + "github.com/SourceSenseiTheRealOne/juntly/backend/internal/users" + clerk "github.com/clerk/clerk-sdk-go/v2" + clerkhttp "github.com/clerk/clerk-sdk-go/v2/http" + "github.com/clerk/clerk-sdk-go/v2/jwks" + clerkjwt "github.com/clerk/clerk-sdk-go/v2/jwt" +) + +var ( + ErrInvalidClerkVerifierConfig = errors.New("invalid Clerk verifier configuration") + ErrUnverifiedClerkToken = errors.New("unverified Clerk token") +) + +type ClerkVerifierConfig struct { + SecretKey string + JWTKey string + AuthorizedParties []string + ClockSkew time.Duration +} + +type clerkVerifier struct { + jwk *clerk.JSONWebKey + jwksClient *jwks.Client + authorizedParties map[string]struct{} + clockSkew time.Duration +} + +func NewClerkVerifier(config ClerkVerifierConfig) (Verifier, error) { + parties := authorizedPartySet(config.AuthorizedParties) + if len(parties) == 0 { + return nil, ErrInvalidClerkVerifierConfig + } + if config.ClockSkew < 0 || config.ClockSkew > 30*time.Second { + return nil, ErrInvalidClerkVerifierConfig + } + + verifier := &clerkVerifier{ + authorizedParties: parties, + clockSkew: config.ClockSkew, + } + if jwtKey := strings.TrimSpace(config.JWTKey); jwtKey != "" { + params := &clerkhttp.AuthorizationParams{} + if err := clerkhttp.JSONWebKey(jwtKey)(params); err != nil { + return nil, ErrInvalidClerkVerifierConfig + } + verifier.jwk = params.JWK + return verifier, nil + } + + secretKey := strings.TrimSpace(config.SecretKey) + if secretKey == "" { + return nil, ErrInvalidClerkVerifierConfig + } + verifier.jwksClient = jwks.NewClient(&clerk.ClientConfig{ + BackendConfig: clerk.BackendConfig{Key: &secretKey}, + }) + return verifier, nil +} + +func (v *clerkVerifier) Verify(ctx context.Context, token string) (users.VerifiedIdentity, error) { + claims, err := clerkjwt.Verify(ctx, &clerkjwt.VerifyParams{ + Token: token, + JWK: v.jwk, + JWKSClient: v.jwksClient, + AuthorizedPartyHandler: v.authorizedParty, + Leeway: v.clockSkew, + }) + if err != nil || claims == nil || claims.SessionID == "" || strings.TrimSpace(claims.Subject) == "" { + return users.VerifiedIdentity{}, ErrUnverifiedClerkToken + } + + return users.VerifiedIdentity{Subject: claims.Subject}, nil +} + +func (v *clerkVerifier) authorizedParty(value string) bool { + _, ok := v.authorizedParties[value] + return ok +} + +func authorizedPartySet(values []string) map[string]struct{} { + parties := make(map[string]struct{}, len(values)) + for _, value := range values { + if value = strings.TrimSpace(value); value != "" { + parties[value] = struct{}{} + } + } + return parties +} diff --git a/backend/internal/authn/clerk_verifier_test.go b/backend/internal/authn/clerk_verifier_test.go new file mode 100644 index 0000000..c3098bf --- /dev/null +++ b/backend/internal/authn/clerk_verifier_test.go @@ -0,0 +1,104 @@ +package authn + +import ( + "context" + "crypto/rand" + "crypto/rsa" + "crypto/x509" + "encoding/pem" + "errors" + "testing" + "time" +) + +func TestNewClerkVerifierRejectsMissingVerificationMaterial(t *testing.T) { + t.Parallel() + + _, err := NewClerkVerifier(ClerkVerifierConfig{ + AuthorizedParties: []string{"http://localhost:4200"}, + }) + + if !errors.Is(err, ErrInvalidClerkVerifierConfig) { + t.Fatalf("error = %v, want ErrInvalidClerkVerifierConfig", err) + } +} + +func TestNewClerkVerifierRejectsMissingAuthorizedParties(t *testing.T) { + t.Parallel() + + _, err := NewClerkVerifier(ClerkVerifierConfig{JWTKey: "synthetic-public-key"}) + + if !errors.Is(err, ErrInvalidClerkVerifierConfig) { + t.Fatalf("error = %v, want ErrInvalidClerkVerifierConfig", err) + } +} + +func TestClerkVerifierRejectsMalformedTokenWithoutIdentity(t *testing.T) { + t.Parallel() + + verifier, err := NewClerkVerifier(ClerkVerifierConfig{ + JWTKey: testPublicKey(t), + AuthorizedParties: []string{"http://localhost:4200"}, + }) + if err != nil { + t.Fatalf("new Clerk verifier: %v", err) + } + + identity, err := verifier.Verify(context.Background(), "not-a-jwt") + + if err == nil { + t.Fatal("Verify error = nil, want rejection") + } + if identity.Subject != "" { + t.Fatalf("identity subject = %q, want empty", identity.Subject) + } +} + +func testPublicKey(t *testing.T) string { + t.Helper() + + privateKey, err := rsa.GenerateKey(rand.Reader, 2048) + if err != nil { + t.Fatalf("generate RSA key: %v", err) + } + der, err := x509.MarshalPKIXPublicKey(&privateKey.PublicKey) + if err != nil { + t.Fatalf("marshal public key: %v", err) + } + return string(pem.EncodeToMemory(&pem.Block{Type: "PUBLIC KEY", Bytes: der})) +} + +func TestNewClerkVerifierAcceptsBoundedClockSkew(t *testing.T) { + t.Parallel() + + verifier, err := NewClerkVerifier(ClerkVerifierConfig{ + JWTKey: testPublicKey(t), + AuthorizedParties: []string{"http://localhost:4200"}, + ClockSkew: 30 * time.Second, + }) + if err != nil { + t.Fatalf("new Clerk verifier: %v", err) + } + concrete, ok := verifier.(*clerkVerifier) + if !ok { + t.Fatalf("verifier type = %T, want *clerkVerifier", verifier) + } + if concrete.clockSkew != 30*time.Second { + t.Fatalf("clock skew = %s, want 30s", concrete.clockSkew) + } +} + +func TestNewClerkVerifierRejectsUnsafeClockSkew(t *testing.T) { + t.Parallel() + + for _, clockSkew := range []time.Duration{-time.Second, 30*time.Second + time.Nanosecond} { + _, err := NewClerkVerifier(ClerkVerifierConfig{ + JWTKey: testPublicKey(t), + AuthorizedParties: []string{"http://localhost:4200"}, + ClockSkew: clockSkew, + }) + if !errors.Is(err, ErrInvalidClerkVerifierConfig) { + t.Fatalf("clock skew %s error = %v, want ErrInvalidClerkVerifierConfig", clockSkew, err) + } + } +} diff --git a/backend/internal/authn/middleware.go b/backend/internal/authn/middleware.go new file mode 100644 index 0000000..89280d0 --- /dev/null +++ b/backend/internal/authn/middleware.go @@ -0,0 +1,99 @@ +package authn + +import ( + "context" + "crypto/rand" + "encoding/hex" + "encoding/json" + "net/http" + "strings" + + "github.com/SourceSenseiTheRealOne/juntly/backend/internal/users" +) + +type verifiedIdentityContextKey struct{} + +type unauthorizedResponse struct { + Error unauthorizedError `json:"error"` +} + +type unauthorizedError struct { + Code string `json:"code"` + Message string `json:"message"` + RequestID string `json:"requestId"` +} + +func RequireVerifiedIdentity(verifier Verifier, next http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, request *http.Request) { + token, ok := bearerToken(request.Header.Get("Authorization")) + if !ok || verifier == nil { + writeUnauthorized(w, request.Header.Get("X-Request-ID")) + return + } + + identity, err := verifier.Verify(request.Context(), token) + if err != nil || strings.TrimSpace(identity.Subject) == "" { + writeUnauthorized(w, request.Header.Get("X-Request-ID")) + return + } + + next.ServeHTTP(w, request.WithContext(context.WithValue(request.Context(), verifiedIdentityContextKey{}, identity))) + }) +} + +func IdentityFromContext(ctx context.Context) (users.VerifiedIdentity, bool) { + identity, ok := ctx.Value(verifiedIdentityContextKey{}).(users.VerifiedIdentity) + return identity, ok +} + +func bearerToken(header string) (string, bool) { + parts := strings.Fields(header) + if len(parts) != 2 || parts[0] != "Bearer" || parts[1] == "" { + return "", false + } + return parts[1], true +} + +func writeUnauthorized(w http.ResponseWriter, requestedID string) { + requestID := requestIDFromHeader(requestedID) + w.Header().Set("X-Request-ID", requestID) + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(http.StatusUnauthorized) + _ = json.NewEncoder(w).Encode(unauthorizedResponse{Error: unauthorizedError{ + Code: "UNAUTHORIZED", + Message: "Unauthorized", + RequestID: requestID, + }}) +} + +func requestIDFromHeader(value string) string { + if validRequestID(value) { + return value + } + + var randomBytes [16]byte + if _, err := rand.Read(randomBytes[:]); err != nil { + return "req_unavailable" + } + + return "req_" + hex.EncodeToString(randomBytes[:]) +} + +func validRequestID(value string) bool { + if len(value) < 8 || len(value) > 128 { + return false + } + + for _, char := range value { + switch { + case char >= 'A' && char <= 'Z': + case char >= 'a' && char <= 'z': + case char >= '0' && char <= '9': + case char == '.', char == '_', char == ':', char == '-': + default: + return false + } + } + + return true +} diff --git a/backend/internal/authn/middleware_test.go b/backend/internal/authn/middleware_test.go new file mode 100644 index 0000000..da2be7b --- /dev/null +++ b/backend/internal/authn/middleware_test.go @@ -0,0 +1,186 @@ +package authn + +import ( + "context" + "encoding/json" + "errors" + "net/http" + "net/http/httptest" + "testing" + + "github.com/SourceSenseiTheRealOne/juntly/backend/internal/users" +) + +func TestRequireVerifiedIdentityRejectsMissingBearerBeforeVerifier(t *testing.T) { + t.Parallel() + + verifier := &recordingVerifier{} + nextCalled := false + handler := RequireVerifiedIdentity(verifier, http.HandlerFunc(func(http.ResponseWriter, *http.Request) { + nextCalled = true + })) + response := httptest.NewRecorder() + request := httptest.NewRequest(http.MethodPost, "/api/v1/auth/reconcile", nil) + request.Header.Set("X-Request-ID", "req_missing_bearer") + + handler.ServeHTTP(response, request) + + if response.Code != http.StatusUnauthorized { + t.Fatalf("status = %d, want %d", response.Code, http.StatusUnauthorized) + } + if verifier.calls != 0 { + t.Fatalf("verifier calls = %d, want 0", verifier.calls) + } + if nextCalled { + t.Fatal("downstream handler was called") + } + assertUnauthorizedResponse(t, response, "req_missing_bearer") +} + +func TestRequireVerifiedIdentityRejectsMalformedAuthorizationBeforeVerifier(t *testing.T) { + t.Parallel() + + verifier := &recordingVerifier{} + nextCalled := false + handler := RequireVerifiedIdentity(verifier, http.HandlerFunc(func(http.ResponseWriter, *http.Request) { + nextCalled = true + })) + request := httptest.NewRequest(http.MethodPost, "/api/v1/auth/reconcile", nil) + request.Header.Set("Authorization", "Basic not-a-bearer-token") + response := httptest.NewRecorder() + + handler.ServeHTTP(response, request) + + if response.Code != http.StatusUnauthorized { + t.Fatalf("status = %d, want %d", response.Code, http.StatusUnauthorized) + } + if verifier.calls != 0 { + t.Fatalf("verifier calls = %d, want 0", verifier.calls) + } + if nextCalled { + t.Fatal("downstream handler was called") + } +} + +func TestRequireVerifiedIdentityRejectsVerifierFailure(t *testing.T) { + t.Parallel() + + verifier := &recordingVerifier{err: errors.New("verification failed")} + nextCalled := false + handler := RequireVerifiedIdentity(verifier, http.HandlerFunc(func(http.ResponseWriter, *http.Request) { + nextCalled = true + })) + request := httptest.NewRequest(http.MethodPost, "/api/v1/auth/reconcile", nil) + request.Header.Set("Authorization", "Bearer synthetic-token") + response := httptest.NewRecorder() + + handler.ServeHTTP(response, request) + + if response.Code != http.StatusUnauthorized { + t.Fatalf("status = %d, want %d", response.Code, http.StatusUnauthorized) + } + if verifier.calls != 1 { + t.Fatalf("verifier calls = %d, want 1", verifier.calls) + } + if nextCalled { + t.Fatal("downstream handler was called") + } +} + +func TestRequireVerifiedIdentityRejectsEmptyVerifiedSubject(t *testing.T) { + t.Parallel() + + verifier := &recordingVerifier{identity: users.VerifiedIdentity{}} + nextCalled := false + handler := RequireVerifiedIdentity(verifier, http.HandlerFunc(func(http.ResponseWriter, *http.Request) { + nextCalled = true + })) + request := httptest.NewRequest(http.MethodPost, "/api/v1/auth/reconcile", nil) + request.Header.Set("Authorization", "Bearer synthetic-token") + response := httptest.NewRecorder() + + handler.ServeHTTP(response, request) + + if response.Code != http.StatusUnauthorized { + t.Fatalf("status = %d, want %d", response.Code, http.StatusUnauthorized) + } + if verifier.calls != 1 { + t.Fatalf("verifier calls = %d, want 1", verifier.calls) + } + if nextCalled { + t.Fatal("downstream handler was called") + } +} + +func TestRequireVerifiedIdentityPropagatesVerifiedSubjectToDownstreamHandler(t *testing.T) { + t.Parallel() + + identity := users.VerifiedIdentity{Subject: "user_synthetic"} + verifier := &recordingVerifier{identity: identity} + handler := RequireVerifiedIdentity(verifier, http.HandlerFunc(func(w http.ResponseWriter, request *http.Request) { + actual, ok := IdentityFromContext(request.Context()) + if !ok { + t.Fatal("verified identity missing from context") + } + if actual != identity { + t.Fatalf("identity = %#v, want %#v", actual, identity) + } + w.WriteHeader(http.StatusNoContent) + })) + request := httptest.NewRequest(http.MethodPost, "/api/v1/auth/reconcile", nil) + request.Header.Set("Authorization", "Bearer synthetic-token") + response := httptest.NewRecorder() + + handler.ServeHTTP(response, request) + + if response.Code != http.StatusNoContent { + t.Fatalf("status = %d, want %d", response.Code, http.StatusNoContent) + } + if verifier.token != "synthetic-token" { + t.Fatalf("verifier token = %q, want synthetic-token", verifier.token) + } +} + +func assertUnauthorizedResponse(t *testing.T, response *httptest.ResponseRecorder, requestID string) { + t.Helper() + + if got := response.Header().Get("X-Request-ID"); got != requestID { + t.Fatalf("X-Request-ID = %q, want %q", got, requestID) + } + if got := response.Header().Get("Content-Type"); got != "application/json" { + t.Fatalf("Content-Type = %q, want application/json", got) + } + + var body map[string]any + if err := json.NewDecoder(response.Body).Decode(&body); err != nil { + t.Fatalf("decode response: %v", err) + } + encoded, err := json.Marshal(body) + if err != nil { + t.Fatalf("marshal response: %v", err) + } + want, err := json.Marshal(map[string]any{"error": map[string]any{ + "code": "UNAUTHORIZED", + "message": "Unauthorized", + "requestId": requestID, + }}) + if err != nil { + t.Fatalf("marshal expected response: %v", err) + } + if string(encoded) != string(want) { + t.Fatalf("error body = %s, want %s", encoded, want) + } +} + +type recordingVerifier struct { + identity users.VerifiedIdentity + err error + calls int + token string +} + +func (v *recordingVerifier) Verify(_ context.Context, token string) (users.VerifiedIdentity, error) { + v.calls++ + v.token = token + return v.identity, v.err +} diff --git a/backend/internal/authn/verifier.go b/backend/internal/authn/verifier.go new file mode 100644 index 0000000..938ecd4 --- /dev/null +++ b/backend/internal/authn/verifier.go @@ -0,0 +1,11 @@ +package authn + +import ( + "context" + + "github.com/SourceSenseiTheRealOne/juntly/backend/internal/users" +) + +type Verifier interface { + Verify(context.Context, string) (users.VerifiedIdentity, error) +} diff --git a/backend/internal/bookings/model.go b/backend/internal/bookings/model.go new file mode 100644 index 0000000..e164e19 --- /dev/null +++ b/backend/internal/bookings/model.go @@ -0,0 +1,70 @@ +package bookings + +import ( + "errors" + "time" + + "github.com/google/uuid" +) + +var ( + ErrInvalid = errors.New("booking invalid request") + ErrUnauthorized = errors.New("booking unauthorized") + ErrForbidden = errors.New("booking forbidden") + ErrNotFound = errors.New("booking not found") + ErrConflict = errors.New("booking conflict") + ErrUnavailable = errors.New("booking unavailable") +) + +type State string + +const ( + StateDraft State = "draft" + StatePendingProviderConfirmation State = "pending_provider_confirmation" + StateConfirmed State = "confirmed" + StateScheduled State = "scheduled" + StateInProgress State = "in_progress" + StateCompleted State = "completed" + StateCancelled State = "cancelled" + StateDisputed State = "disputed" + StateRefunded State = "refunded" +) + +type SourceType string + +const ( + SourceProposal SourceType = "proposal" + SourceListing SourceType = "listing" + SourceDirect SourceType = "direct" +) + +type CreateBooking struct { + SourceType SourceType + SourceID *uuid.UUID + ProviderID *uuid.UUID + IdempotencyKey string + ScheduledAt time.Time + PrivateLocation string + AgreedPriceMinor *int +} +type Transition struct { + ExpectedState State + TargetState State + Revision int + Reason *string +} +type Booking struct { + ID uuid.UUID + CustomerID uuid.UUID + ProviderID uuid.UUID + SourceType SourceType + SourceID *uuid.UUID + State State + Revision int + ScheduledAt time.Time + PrivateLocation string + AgreedPriceMinor int + Currency string + CreatedAt time.Time + UpdatedAt time.Time +} diff --git a/backend/internal/bookings/service.go b/backend/internal/bookings/service.go new file mode 100644 index 0000000..e79597a --- /dev/null +++ b/backend/internal/bookings/service.go @@ -0,0 +1,139 @@ +package bookings + +import ( + "context" + "errors" + "regexp" + "strings" + "unicode/utf8" + + "github.com/SourceSenseiTheRealOne/juntly/backend/internal/users" + "github.com/google/uuid" +) + +var idempotencyPattern = regexp.MustCompile(`^[A-Za-z0-9._:-]{8,128}$`) + +type IdentityReconciler interface { + Reconcile(context.Context, users.VerifiedIdentity) (users.InternalUser, bool, error) +} +type Store interface { + Create(context.Context, uuid.UUID, CreateBooking) (Booking, error) + List(context.Context, uuid.UUID) ([]Booking, error) + Get(context.Context, uuid.UUID, uuid.UUID) (Booking, error) + Transition(context.Context, uuid.UUID, uuid.UUID, Transition) (Booking, error) +} +type Service interface { + Create(context.Context, users.VerifiedIdentity, CreateBooking) (Booking, error) + List(context.Context, users.VerifiedIdentity) ([]Booking, error) + Get(context.Context, users.VerifiedIdentity, uuid.UUID) (Booking, error) + Transition(context.Context, users.VerifiedIdentity, uuid.UUID, Transition) (Booking, error) +} +type service struct { + identities IdentityReconciler + store Store +} + +func NewService(identities IdentityReconciler, store Store) Service { + return service{identities: identities, store: store} +} +func (s service) Create(ctx context.Context, identity users.VerifiedIdentity, input CreateBooking) (Booking, error) { + input.IdempotencyKey = strings.TrimSpace(input.IdempotencyKey) + input.PrivateLocation = strings.TrimSpace(input.PrivateLocation) + if !validCreate(input) { + return Booking{}, ErrInvalid + } + actor, err := s.actor(ctx, identity) + if err != nil { + return Booking{}, err + } + v, err := s.store.Create(ctx, actor, input) + return v, normalize(err) +} +func (s service) List(ctx context.Context, identity users.VerifiedIdentity) ([]Booking, error) { + actor, err := s.actor(ctx, identity) + if err != nil { + return nil, err + } + v, err := s.store.List(ctx, actor) + return v, normalize(err) +} +func (s service) Get(ctx context.Context, identity users.VerifiedIdentity, id uuid.UUID) (Booking, error) { + if id == uuid.Nil { + return Booking{}, ErrInvalid + } + actor, err := s.actor(ctx, identity) + if err != nil { + return Booking{}, err + } + v, err := s.store.Get(ctx, actor, id) + return v, normalize(err) +} +func (s service) Transition(ctx context.Context, identity users.VerifiedIdentity, id uuid.UUID, input Transition) (Booking, error) { + if id == uuid.Nil || input.Revision < 1 || !validTransition(input.ExpectedState, input.TargetState) || input.Reason != nil && (utf8.RuneCountInString(strings.TrimSpace(*input.Reason)) < 3 || utf8.RuneCountInString(*input.Reason) > 500) { + return Booking{}, ErrInvalid + } + actor, err := s.actor(ctx, identity) + if err != nil { + return Booking{}, err + } + v, err := s.store.Transition(ctx, actor, id, input) + return v, normalize(err) +} +func validCreate(v CreateBooking) bool { + if !idempotencyPattern.MatchString(v.IdempotencyKey) || v.ScheduledAt.IsZero() || utf8.RuneCountInString(v.PrivateLocation) < 5 || utf8.RuneCountInString(v.PrivateLocation) > 500 { + return false + } + switch v.SourceType { + case SourceProposal: + return v.SourceID != nil && *v.SourceID != uuid.Nil && v.ProviderID == nil + case SourceListing: + return v.SourceID != nil && *v.SourceID != uuid.Nil && v.ProviderID == nil + case SourceDirect: + return v.SourceID == nil && v.ProviderID != nil && *v.ProviderID != uuid.Nil && v.AgreedPriceMinor != nil && *v.AgreedPriceMinor > 0 + default: + return false + } +} +func validTransition(from, to State) bool { + switch from { + case StatePendingProviderConfirmation: + return to == StateConfirmed || to == StateCancelled + case StateConfirmed: + return to == StateScheduled || to == StateCancelled || to == StateDisputed + case StateScheduled: + return to == StateInProgress || to == StateCancelled || to == StateDisputed + case StateInProgress: + return to == StateCompleted || to == StateDisputed + case StateCompleted: + return to == StateDisputed + case StateDisputed: + return to == StateRefunded + default: + return false + } +} +func (s service) actor(ctx context.Context, identity users.VerifiedIdentity) (uuid.UUID, error) { + if s.identities == nil || s.store == nil { + return uuid.Nil, ErrUnavailable + } + u, _, err := s.identities.Reconcile(ctx, identity) + if err != nil { + if errors.Is(err, users.ErrInvalidIdentity) { + return uuid.Nil, ErrUnauthorized + } + return uuid.Nil, ErrUnavailable + } + if u.ID == uuid.Nil { + return uuid.Nil, ErrUnauthorized + } + return u.ID, nil +} +func normalize(err error) error { + if err == nil { + return nil + } + if errors.Is(err, ErrInvalid) || errors.Is(err, ErrUnauthorized) || errors.Is(err, ErrForbidden) || errors.Is(err, ErrNotFound) || errors.Is(err, ErrConflict) { + return err + } + return ErrUnavailable +} diff --git a/backend/internal/bookings/service_test.go b/backend/internal/bookings/service_test.go new file mode 100644 index 0000000..8fbdbe9 --- /dev/null +++ b/backend/internal/bookings/service_test.go @@ -0,0 +1,81 @@ +package bookings + +import ( + "context" + "errors" + "testing" + "time" + + "github.com/SourceSenseiTheRealOne/juntly/backend/internal/users" + "github.com/google/uuid" +) + +func TestServiceCreatesProposalBookingWithBoundedIdempotencyKey(t *testing.T) { + t.Parallel() + actorID := uuid.MustParse("11111111-1111-4111-8111-111111111111") + proposalID := uuid.MustParse("22222222-2222-4222-8222-222222222222") + store := &recordingStore{booking: Booking{ID: uuid.New(), CustomerID: actorID, State: StatePendingProviderConfirmation, Revision: 1}} + service := NewService(staticIdentity{user: users.InternalUser{ID: actorID}}, store) + + value, err := service.Create(context.Background(), users.VerifiedIdentity{Subject: "user_customer"}, CreateBooking{SourceType: SourceProposal, SourceID: &proposalID, IdempotencyKey: "booking-request-001", ScheduledAt: time.Date(2026, 9, 10, 9, 0, 0, 0, time.UTC), PrivateLocation: "Rua privada 1"}) + if err != nil { + t.Fatalf("Create() error = %v", err) + } + if value.ID != store.booking.ID || store.actorID != actorID || store.created.IdempotencyKey != "booking-request-001" { + t.Fatalf("booking/store = %#v/%s/%#v", value, store.actorID, store.created) + } +} + +func TestServiceRejectsInvalidTransitionBeforeStore(t *testing.T) { + t.Parallel() + store := &recordingStore{} + service := NewService(staticIdentity{user: users.InternalUser{ID: uuid.New()}}, store) + _, err := service.Transition(context.Background(), users.VerifiedIdentity{Subject: "user_provider"}, uuid.New(), Transition{ExpectedState: StatePendingProviderConfirmation, TargetState: StateCompleted, Revision: 1}) + if !errors.Is(err, ErrInvalid) { + t.Fatalf("Transition() error = %v, want ErrInvalid", err) + } + if store.transitioned.Revision != 0 { + t.Fatal("store called for invalid transition") + } +} + +func TestServiceAllowsProviderConfirmationTransition(t *testing.T) { + t.Parallel() + actorID := uuid.New() + bookingID := uuid.New() + store := &recordingStore{booking: Booking{ID: bookingID, State: StateConfirmed, Revision: 2}} + service := NewService(staticIdentity{user: users.InternalUser{ID: actorID}}, store) + value, err := service.Transition(context.Background(), users.VerifiedIdentity{Subject: "user_provider"}, bookingID, Transition{ExpectedState: StatePendingProviderConfirmation, TargetState: StateConfirmed, Revision: 1}) + if err != nil || value.State != StateConfirmed || store.actorID != actorID { + t.Fatalf("Transition() = %#v/%v/%s", value, err, store.actorID) + } +} + +type staticIdentity struct { + user users.InternalUser + err error +} + +func (s staticIdentity) Reconcile(context.Context, users.VerifiedIdentity) (users.InternalUser, bool, error) { + return s.user, false, s.err +} + +type recordingStore struct { + booking Booking + actorID uuid.UUID + created CreateBooking + transitioned Transition +} + +func (s *recordingStore) Create(_ context.Context, actor uuid.UUID, input CreateBooking) (Booking, error) { + s.actorID, s.created = actor, input + return s.booking, nil +} +func (s *recordingStore) List(context.Context, uuid.UUID) ([]Booking, error) { return nil, nil } +func (s *recordingStore) Get(context.Context, uuid.UUID, uuid.UUID) (Booking, error) { + return s.booking, nil +} +func (s *recordingStore) Transition(_ context.Context, actor, id uuid.UUID, input Transition) (Booking, error) { + s.actorID, s.transitioned = actor, input + return s.booking, nil +} diff --git a/backend/internal/bookings/sql_store.go b/backend/internal/bookings/sql_store.go new file mode 100644 index 0000000..e05999f --- /dev/null +++ b/backend/internal/bookings/sql_store.go @@ -0,0 +1,207 @@ +package bookings + +import ( + "context" + "database/sql" + "errors" + + "github.com/google/uuid" +) + +type sqlStore struct{ database *sql.DB } + +func NewSQLStore(database *sql.DB) Store { return sqlStore{database: database} } +func (s sqlStore) Create(ctx context.Context, actor uuid.UUID, input CreateBooking) (Booking, error) { + tx, err := s.database.BeginTx(ctx, nil) + if err != nil { + return Booking{}, err + } + defer tx.Rollback() + provider, price, err := s.resolveSource(ctx, tx, actor, input) + if err != nil { + return Booking{}, err + } + var v Booking + err = tx.QueryRowContext(ctx, `insert into public.bookings(customer_internal_user_id,provider_internal_user_id,source_type,source_id,idempotency_key,scheduled_at,private_location,agreed_price_minor) values($1,$2,$3,$4,$5,$6,$7,$8) on conflict do nothing returning id,customer_internal_user_id,provider_internal_user_id,source_type,source_id,state,revision,scheduled_at,private_location,agreed_price_minor,currency,created_at,updated_at`, actor, provider, input.SourceType, input.SourceID, input.IdempotencyKey, input.ScheduledAt, input.PrivateLocation, price).Scan(bookingScan(&v)...) + if errors.Is(err, sql.ErrNoRows) { + existing, e := s.getByIdempotency(ctx, tx, actor, input.IdempotencyKey) + if e != nil { + if errors.Is(e, sql.ErrNoRows) && input.SourceType == SourceProposal { + return Booking{}, ErrConflict + } + return Booking{}, e + } + if existing.SourceType != input.SourceType || !sameUUID(existing.SourceID, input.SourceID) || existing.ProviderID != provider { + return Booking{}, ErrConflict + } + return existing, tx.Commit() + } + if err != nil { + return Booking{}, err + } + if _, err = tx.ExecContext(ctx, `insert into public.booking_events(booking_id,actor_internal_user_id,from_state,to_state,revision) values($1,$2,null,$3,1)`, v.ID, actor, v.State); err != nil { + return Booking{}, err + } + if err = s.notify(ctx, tx, v.ProviderID, "booking_created", v.ID); err != nil { + return Booking{}, err + } + if err = tx.Commit(); err != nil { + return Booking{}, err + } + return v, nil +} +func (s sqlStore) resolveSource(ctx context.Context, tx *sql.Tx, actor uuid.UUID, input CreateBooking) (uuid.UUID, int, error) { + switch input.SourceType { + case SourceProposal: + var provider uuid.UUID + var price int + err := tx.QueryRowContext(ctx, `select p.provider_internal_user_id,p.price_minor from public.quotation_proposals p join public.quotation_requests r on r.id=p.request_id where p.id=$1 and p.state='accepted' and r.customer_internal_user_id=$2`, *input.SourceID, actor).Scan(&provider, &price) + if errors.Is(err, sql.ErrNoRows) { + return uuid.Nil, 0, ErrForbidden + } + return provider, price, err + case SourceListing: + var provider uuid.UUID + var listedPrice *int + var priceType string + err := tx.QueryRowContext(ctx, `select internal_user_id,price_minor,price_type from public.listings where id=$1 and state='active' and internal_user_id<>$2`, *input.SourceID, actor).Scan(&provider, &listedPrice, &priceType) + if errors.Is(err, sql.ErrNoRows) { + return uuid.Nil, 0, ErrNotFound + } + if err != nil { + return uuid.Nil, 0, err + } + if listedPrice != nil { + return provider, *listedPrice, nil + } + if input.AgreedPriceMinor == nil || *input.AgreedPriceMinor <= 0 { + return uuid.Nil, 0, ErrInvalid + } + return provider, *input.AgreedPriceMinor, nil + case SourceDirect: + var active bool + err := tx.QueryRowContext(ctx, `select exists(select 1 from public.provider_profiles where internal_user_id=$1)`, *input.ProviderID).Scan(&active) + if err != nil { + return uuid.Nil, 0, err + } + if !active || *input.ProviderID == actor { + return uuid.Nil, 0, ErrForbidden + } + return *input.ProviderID, *input.AgreedPriceMinor, nil + default: + return uuid.Nil, 0, ErrInvalid + } +} +func (s sqlStore) List(ctx context.Context, actor uuid.UUID) ([]Booking, error) { + rows, err := s.database.QueryContext(ctx, `select id,customer_internal_user_id,provider_internal_user_id,source_type,source_id,state,revision,scheduled_at,private_location,agreed_price_minor,currency,created_at,updated_at from public.bookings where customer_internal_user_id=$1 or provider_internal_user_id=$1 order by updated_at desc,id`, actor) + if err != nil { + return nil, err + } + defer rows.Close() + values := []Booking{} + for rows.Next() { + var v Booking + if err := rows.Scan(bookingScan(&v)...); err != nil { + return nil, err + } + values = append(values, v) + } + return values, rows.Err() +} +func (s sqlStore) Get(ctx context.Context, actor, id uuid.UUID) (Booking, error) { + var v Booking + err := s.database.QueryRowContext(ctx, `select id,customer_internal_user_id,provider_internal_user_id,source_type,source_id,state,revision,scheduled_at,private_location,agreed_price_minor,currency,created_at,updated_at from public.bookings where id=$1 and (customer_internal_user_id=$2 or provider_internal_user_id=$2)`, id, actor).Scan(bookingScan(&v)...) + if errors.Is(err, sql.ErrNoRows) { + return Booking{}, ErrForbidden + } + return v, err +} +func (s sqlStore) Transition(ctx context.Context, actor, id uuid.UUID, input Transition) (Booking, error) { + tx, err := s.database.BeginTx(ctx, nil) + if err != nil { + return Booking{}, err + } + defer tx.Rollback() + var current Booking + err = tx.QueryRowContext(ctx, `select id,customer_internal_user_id,provider_internal_user_id,source_type,source_id,state,revision,scheduled_at,private_location,agreed_price_minor,currency,created_at,updated_at from public.bookings where id=$1 for update`, id).Scan(bookingScan(¤t)...) + if errors.Is(err, sql.ErrNoRows) { + return Booking{}, ErrNotFound + } + if err != nil { + return Booking{}, err + } + if actor != current.CustomerID && actor != current.ProviderID && !s.moderator(ctx, tx, actor) { + return Booking{}, ErrForbidden + } + if current.State == input.TargetState && current.Revision == input.Revision+1 { + return current, tx.Commit() + } + if current.State != input.ExpectedState || current.Revision != input.Revision { + return Booking{}, ErrConflict + } + if !authorizedTransition(ctx, tx, actor, current, input.TargetState) { + return Booking{}, ErrForbidden + } + result, err := tx.ExecContext(ctx, `update public.bookings set state=$1,revision=revision+1,updated_at=timezone('utc',now()) where id=$2 and state=$3 and revision=$4`, input.TargetState, id, input.ExpectedState, input.Revision) + if err != nil { + return Booking{}, err + } + n, _ := result.RowsAffected() + if n != 1 { + return Booking{}, ErrConflict + } + if _, err = tx.ExecContext(ctx, `insert into public.booking_events(booking_id,actor_internal_user_id,from_state,to_state,revision,reason) values($1,$2,$3,$4,$5,$6)`, id, actor, input.ExpectedState, input.TargetState, input.Revision+1, input.Reason); err != nil { + return Booking{}, err + } + recipient := current.CustomerID + if actor == current.CustomerID { + recipient = current.ProviderID + } + if err = s.notify(ctx, tx, recipient, "booking_updated", id); err != nil { + return Booking{}, err + } + current.State = input.TargetState + current.Revision++ + if err = tx.Commit(); err != nil { + return Booking{}, err + } + return current, nil +} +func authorizedTransition(ctx context.Context, tx *sql.Tx, actor uuid.UUID, b Booking, target State) bool { + switch target { + case StateConfirmed, StateInProgress, StateCompleted: + return actor == b.ProviderID + case StateRefunded: + var ok bool + _ = tx.QueryRowContext(ctx, `select exists(select 1 from public.platform_roles where internal_user_id=$1 and role='moderator')`, actor).Scan(&ok) + return ok + default: + return actor == b.CustomerID || actor == b.ProviderID + } +} +func (s sqlStore) moderator(ctx context.Context, tx *sql.Tx, actor uuid.UUID) bool { + var ok bool + _ = tx.QueryRowContext(ctx, `select exists(select 1 from public.platform_roles where internal_user_id=$1 and role='moderator')`, actor).Scan(&ok) + return ok +} +func (s sqlStore) notify(ctx context.Context, tx *sql.Tx, recipient uuid.UUID, kind string, resource uuid.UUID) error { + if _, err := tx.ExecContext(ctx, `insert into public.notifications(recipient_internal_user_id,kind,resource_id,in_app_visible) select $1,$2,$3,coalesce(p.in_app_enabled,true) from (select 1) x left join public.notification_preferences p on p.internal_user_id=$1 where coalesce(p.in_app_enabled or p.email_enabled,true) on conflict do nothing`, recipient, kind, resource); err != nil { + return err + } + _, err := tx.ExecContext(ctx, `insert into public.notification_email_outbox(notification_id,recipient_internal_user_id) select n.id,n.recipient_internal_user_id from public.notifications n left join public.notification_preferences p on p.internal_user_id=n.recipient_internal_user_id where n.recipient_internal_user_id=$1 and n.kind=$2 and n.resource_id=$3 and coalesce(p.email_enabled,true) on conflict(notification_id) do nothing`, recipient, kind, resource) + return err +} +func (s sqlStore) getByIdempotency(ctx context.Context, tx *sql.Tx, actor uuid.UUID, key string) (Booking, error) { + var v Booking + err := tx.QueryRowContext(ctx, `select id,customer_internal_user_id,provider_internal_user_id,source_type,source_id,state,revision,scheduled_at,private_location,agreed_price_minor,currency,created_at,updated_at from public.bookings where customer_internal_user_id=$1 and idempotency_key=$2`, actor, key).Scan(bookingScan(&v)...) + return v, err +} +func bookingScan(v *Booking) []any { + return []any{&v.ID, &v.CustomerID, &v.ProviderID, &v.SourceType, &v.SourceID, &v.State, &v.Revision, &v.ScheduledAt, &v.PrivateLocation, &v.AgreedPriceMinor, &v.Currency, &v.CreatedAt, &v.UpdatedAt} +} +func sameUUID(a, b *uuid.UUID) bool { + if a == nil || b == nil { + return a == nil && b == nil + } + return *a == *b +} diff --git a/backend/internal/contactreveal/channel_store.go b/backend/internal/contactreveal/channel_store.go new file mode 100644 index 0000000..1ad5f19 --- /dev/null +++ b/backend/internal/contactreveal/channel_store.go @@ -0,0 +1,65 @@ +package contactreveal + +import ( + "context" + "database/sql" + + "github.com/google/uuid" +) + +type sqlChannelStore struct{ database *sql.DB } + +func NewSQLChannelStore(database *sql.DB) ChannelStore { + return sqlChannelStore{database: database} +} + +func (s sqlChannelStore) Replace(ctx context.Context, ownerID uuid.UUID, value EncryptedChannel) (ChannelStatus, error) { + if s.database == nil || ownerID == uuid.Nil || !validChannel(value.Channel) || len(value.Sealed.Ciphertext) == 0 || len(value.Sealed.Nonce) == 0 || value.KeyVersion == "" { + return ChannelStatus{}, ErrUnavailable + } + _, err := s.database.ExecContext(ctx, ` + insert into public.provider_contact_channels + (id, internal_user_id, channel, ciphertext, nonce, key_version, enabled, reveal_consent) + values ($1, $2, $3, $4, $5, $6, $7, $8) + on conflict (internal_user_id, channel) do update set + ciphertext = excluded.ciphertext, + nonce = excluded.nonce, + key_version = excluded.key_version, + enabled = excluded.enabled, + reveal_consent = excluded.reveal_consent, + updated_at = timezone('utc', now()) + `, uuid.New(), ownerID, string(value.Channel), value.Sealed.Ciphertext, value.Sealed.Nonce, value.KeyVersion, value.Enabled, value.RevealConsent) + if err != nil { + return ChannelStatus{}, ErrUnavailable + } + return ChannelStatus{Channel: value.Channel, Configured: true, Enabled: value.Enabled, RevealConsent: value.RevealConsent}, nil +} + +func (s sqlChannelStore) Statuses(ctx context.Context, ownerID uuid.UUID) ([]ChannelStatus, error) { + if s.database == nil || ownerID == uuid.Nil { + return nil, ErrUnavailable + } + rows, err := s.database.QueryContext(ctx, ` + select channel, enabled, reveal_consent + from public.provider_contact_channels + where internal_user_id = $1 + order by channel + `, ownerID) + if err != nil { + return nil, ErrUnavailable + } + defer rows.Close() + values := make([]ChannelStatus, 0) + for rows.Next() { + var value ChannelStatus + if err := rows.Scan(&value.Channel, &value.Enabled, &value.RevealConsent); err != nil { + return nil, ErrUnavailable + } + value.Configured = true + values = append(values, value) + } + if err := rows.Err(); err != nil { + return nil, ErrUnavailable + } + return values, nil +} diff --git a/backend/internal/contactreveal/channel_store_test.go b/backend/internal/contactreveal/channel_store_test.go new file mode 100644 index 0000000..4d757c3 --- /dev/null +++ b/backend/internal/contactreveal/channel_store_test.go @@ -0,0 +1,45 @@ +package contactreveal + +import ( + "context" + "database/sql" + "encoding/base64" + "testing" + + "github.com/google/uuid" +) + +func TestSQLChannelStoreUpsertsEncryptedOwnerChannelWithoutPlaintext(t *testing.T) { + database := openRevealDatabase(t) + ctx := context.Background() + _, listingID, _ := seedRevealFixture(t, database) + var providerID uuid.UUID + if err := database.QueryRowContext(ctx, `select internal_user_id from public.listings where id = $1`, listingID).Scan(&providerID); err != nil { + t.Fatalf("provider: %v", err) + } + key := make([]byte, 32) + for index := range key { + key[index] = byte(index + 1) + } + cipher, err := NewCipher(base64.StdEncoding.EncodeToString(key)) + if err != nil { + t.Fatalf("cipher: %v", err) + } + sealed, err := cipher.Encrypt([]byte("test-contact")) + if err != nil { + t.Fatalf("encrypt: %v", err) + } + status, err := NewSQLChannelStore(database).Replace(ctx, providerID, EncryptedChannel{Channel: ChannelPhone, Sealed: sealed, KeyVersion: "v1", Enabled: true, RevealConsent: true}) + if err != nil || !status.Configured || !status.Enabled || !status.RevealConsent || status.Channel != ChannelPhone { + t.Fatalf("status/error = %#v/%v", status, err) + } + var ciphertext, nonce []byte + if err := database.QueryRowContext(ctx, `select ciphertext, nonce from public.provider_contact_channels where internal_user_id = $1 and channel = 'phone'`, providerID).Scan(&ciphertext, &nonce); err != nil { + t.Fatalf("stored channel: %v", err) + } + if string(ciphertext) != string(sealed.Ciphertext) || string(nonce) != string(sealed.Nonce) { + t.Fatal("stored ciphertext or nonce mismatch") + } +} + +var _ = sql.ErrNoRows diff --git a/backend/internal/contactreveal/crypto.go b/backend/internal/contactreveal/crypto.go new file mode 100644 index 0000000..5efdc76 --- /dev/null +++ b/backend/internal/contactreveal/crypto.go @@ -0,0 +1,55 @@ +package contactreveal + +import ( + "crypto/aes" + "crypto/cipher" + "crypto/rand" + "encoding/base64" + "errors" + "io" +) + +var ErrInvalidEncryptionKey = errors.New("invalid contact encryption key") + +type SealedContact struct { + Ciphertext []byte + Nonce []byte +} + +type Cipher interface { + Encrypt([]byte) (SealedContact, error) + Decrypt(SealedContact) ([]byte, error) +} + +type gcmCipher struct{ gcm cipher.AEAD } + +func NewCipher(encodedKey string) (Cipher, error) { + key, err := base64.StdEncoding.DecodeString(encodedKey) + if err != nil || len(key) != 32 { + return nil, ErrInvalidEncryptionKey + } + block, err := aes.NewCipher(key) + if err != nil { + return nil, ErrInvalidEncryptionKey + } + gcm, err := cipher.NewGCM(block) + if err != nil { + return nil, ErrInvalidEncryptionKey + } + return gcmCipher{gcm: gcm}, nil +} + +func (c gcmCipher) Encrypt(plaintext []byte) (SealedContact, error) { + nonce := make([]byte, c.gcm.NonceSize()) + if _, err := io.ReadFull(rand.Reader, nonce); err != nil { + return SealedContact{}, err + } + return SealedContact{Ciphertext: c.gcm.Seal(nil, nonce, plaintext, nil), Nonce: nonce}, nil +} + +func (c gcmCipher) Decrypt(sealed SealedContact) ([]byte, error) { + if len(sealed.Nonce) != c.gcm.NonceSize() { + return nil, errors.New("invalid contact nonce") + } + return c.gcm.Open(nil, sealed.Nonce, sealed.Ciphertext, nil) +} diff --git a/backend/internal/contactreveal/crypto_test.go b/backend/internal/contactreveal/crypto_test.go new file mode 100644 index 0000000..6b78a47 --- /dev/null +++ b/backend/internal/contactreveal/crypto_test.go @@ -0,0 +1,36 @@ +package contactreveal + +import ( + "encoding/base64" + "errors" + "testing" +) + +func TestCipherRequiresExactServerKeyAndRoundTripsWithoutPlaintextMetadata(t *testing.T) { + t.Parallel() + key := make([]byte, 32) + for index := range key { + key[index] = byte(index + 1) + } + cipher, err := NewCipher(base64.StdEncoding.EncodeToString(key)) + if err != nil { + t.Fatalf("new cipher: %v", err) + } + sealed, err := cipher.Encrypt([]byte("+351912345678")) + if err != nil || len(sealed.Ciphertext) == 0 || len(sealed.Nonce) == 0 { + t.Fatalf("sealed/error = %#v/%v", sealed, err) + } + opened, err := cipher.Decrypt(sealed) + if err != nil || string(opened) != "+351912345678" { + t.Fatalf("opened/error = %q/%v", opened, err) + } +} + +func TestCipherRejectsMalformedOrWrongLengthServerKeys(t *testing.T) { + t.Parallel() + for _, key := range []string{"", "not-base64", base64.StdEncoding.EncodeToString(make([]byte, 31))} { + if _, err := NewCipher(key); !errors.Is(err, ErrInvalidEncryptionKey) { + t.Fatalf("key %q error = %v", key, err) + } + } +} diff --git a/backend/internal/contactreveal/migration_contract_test.go b/backend/internal/contactreveal/migration_contract_test.go new file mode 100644 index 0000000..36a3720 --- /dev/null +++ b/backend/internal/contactreveal/migration_contract_test.go @@ -0,0 +1,53 @@ +package contactreveal + +import ( + "os" + "path/filepath" + "strings" + "testing" +) + +func TestContactRevealMigrationContract(t *testing.T) { + t.Parallel() + directory := filepath.Join("..", "..", "..", "supabase", "migrations") + entries, err := os.ReadDir(directory) + if err != nil { + t.Fatalf("read migrations: %v", err) + } + var migration string + for _, entry := range entries { + if strings.HasSuffix(entry.Name(), "_create_contact_reveals.sql") { + contents, readErr := os.ReadFile(filepath.Join(directory, entry.Name())) + if readErr != nil { + t.Fatalf("read migration: %v", readErr) + } + migration = strings.ToLower(string(contents)) + } + } + if migration == "" { + t.Fatal("contact reveal migration not found") + } + for _, requirement := range []string{ + "create table public.provider_contact_channels", + "ciphertext bytea not null", + "nonce bytea not null", + "key_version text not null", + "channel in ('phone', 'whatsapp')", + "unique (internal_user_id, channel)", + "create table public.contact_reveal_daily_limits", + "unique (customer_internal_user_id, utc_day)", + "create table public.contact_reveal_events", + "unique (customer_internal_user_id, listing_id, channel, utc_day)", + "create index contact_reveal_events_listing_created_idx", + "create index provider_contact_channels_owner_idx", + } { + if !strings.Contains(migration, requirement) { + t.Errorf("migration missing %q", requirement) + } + } + for _, prohibited := range []string{"phone_number", "whatsapp_number", "contact_value", "plaintext"} { + if strings.Contains(migration, prohibited) { + t.Errorf("migration must not persist %q", prohibited) + } + } +} diff --git a/backend/internal/contactreveal/provider_service.go b/backend/internal/contactreveal/provider_service.go new file mode 100644 index 0000000..1f8ffcb --- /dev/null +++ b/backend/internal/contactreveal/provider_service.go @@ -0,0 +1,119 @@ +package contactreveal + +import ( + "context" + "errors" + "strings" + + "github.com/SourceSenseiTheRealOne/juntly/backend/internal/provideraccess" + "github.com/SourceSenseiTheRealOne/juntly/backend/internal/users" + "github.com/google/uuid" +) + +type ProviderAuthorizer interface { + RequireProvider(context.Context, users.VerifiedIdentity) (users.InternalUser, error) +} + +type ChannelStore interface { + Replace(context.Context, uuid.UUID, EncryptedChannel) (ChannelStatus, error) + Statuses(context.Context, uuid.UUID) ([]ChannelStatus, error) +} + +type ProviderChannelService interface { + Get(context.Context, users.VerifiedIdentity) ([]ChannelStatus, error) + Put(context.Context, users.VerifiedIdentity, ReplaceChannel) (ChannelStatus, error) +} + +type ReplaceChannel struct { + Channel Channel + Contact string + Enabled bool + RevealConsent bool +} + +type EncryptedChannel struct { + Channel Channel + Sealed SealedContact + KeyVersion string + Enabled bool + RevealConsent bool +} + +type ChannelStatus struct { + Channel Channel + Configured bool + Enabled bool + RevealConsent bool +} + +type providerChannelService struct { + authorizer ProviderAuthorizer + store ChannelStore + cipher Cipher +} + +func NewProviderChannelService(authorizer ProviderAuthorizer, store ChannelStore, cipher Cipher) ProviderChannelService { + return providerChannelService{authorizer: authorizer, store: store, cipher: cipher} +} + +func (s providerChannelService) Get(ctx context.Context, identity users.VerifiedIdentity) ([]ChannelStatus, error) { + if s.authorizer == nil || s.store == nil { + return nil, ErrUnavailable + } + owner, err := s.authorizer.RequireProvider(ctx, identity) + if err != nil { + if errors.Is(err, provideraccess.ErrUnauthorized) { + return nil, ErrUnauthorized + } + if errors.Is(err, provideraccess.ErrForbidden) { + return nil, ErrForbidden + } + return nil, ErrUnavailable + } + values, err := s.store.Statuses(ctx, owner.ID) + if err != nil { + return nil, ErrUnavailable + } + return append([]ChannelStatus(nil), values...), nil +} + +func (s providerChannelService) Put(ctx context.Context, identity users.VerifiedIdentity, input ReplaceChannel) (ChannelStatus, error) { + if s.authorizer == nil || s.store == nil || s.cipher == nil { + return ChannelStatus{}, ErrUnavailable + } + owner, err := s.authorizer.RequireProvider(ctx, identity) + if err != nil { + if errors.Is(err, provideraccess.ErrUnauthorized) { + return ChannelStatus{}, ErrUnauthorized + } + if errors.Is(err, provideraccess.ErrForbidden) { + return ChannelStatus{}, ErrForbidden + } + return ChannelStatus{}, ErrUnavailable + } + input.Contact = strings.TrimSpace(input.Contact) + if !validChannel(input.Channel) || !validE164(input.Contact) { + return ChannelStatus{}, ErrForbidden + } + sealed, err := s.cipher.Encrypt([]byte(input.Contact)) + if err != nil { + return ChannelStatus{}, ErrUnavailable + } + status, err := s.store.Replace(ctx, owner.ID, EncryptedChannel{Channel: input.Channel, Sealed: sealed, KeyVersion: "v1", Enabled: input.Enabled, RevealConsent: input.RevealConsent}) + if err != nil { + return ChannelStatus{}, ErrUnavailable + } + return status, nil +} + +func validE164(value string) bool { + if len(value) < 9 || len(value) > 16 || value[0] != '+' || value[1] == '0' { + return false + } + for _, char := range value[1:] { + if char < '0' || char > '9' { + return false + } + } + return true +} diff --git a/backend/internal/contactreveal/provider_service_test.go b/backend/internal/contactreveal/provider_service_test.go new file mode 100644 index 0000000..427d9c2 --- /dev/null +++ b/backend/internal/contactreveal/provider_service_test.go @@ -0,0 +1,93 @@ +package contactreveal + +import ( + "context" + "errors" + "reflect" + "testing" + + "github.com/SourceSenseiTheRealOne/juntly/backend/internal/provideraccess" + "github.com/SourceSenseiTheRealOne/juntly/backend/internal/users" + "github.com/google/uuid" +) + +func TestProviderChannelServiceDeniesBeforeEncrypting(t *testing.T) { + t.Parallel() + cipher := &providerCountingCipher{} + store := &recordingChannelStore{} + service := NewProviderChannelService(&recordingProviderAuthorizer{err: provideraccess.ErrForbidden}, store, cipher) + _, err := service.Put(context.Background(), users.VerifiedIdentity{Subject: "provider"}, ReplaceChannel{Channel: ChannelPhone, Contact: "+12025550123", Enabled: true, RevealConsent: true}) + if !errors.Is(err, ErrForbidden) || cipher.encryptCalls != 0 || store.calls != 0 { + t.Fatalf("error/encrypt/store = %v/%d/%d", err, cipher.encryptCalls, store.calls) + } +} + +func TestProviderChannelServiceEncryptsValidatedContactBeforeStore(t *testing.T) { + t.Parallel() + owner := users.InternalUser{ID: uuid.New()} + cipher := &providerCountingCipher{} + store := &recordingChannelStore{} + service := NewProviderChannelService(&recordingProviderAuthorizer{owner: owner}, store, cipher) + status, err := service.Put(context.Background(), users.VerifiedIdentity{Subject: "provider"}, ReplaceChannel{Channel: ChannelWhatsApp, Contact: "+12025550123", Enabled: true, RevealConsent: true}) + if _, exists := reflect.TypeOf(store.value).FieldByName("Contact"); exists { + t.Fatal("encrypted channel must not carry plaintext Contact") + } + if err != nil || status.Channel != ChannelWhatsApp || !status.Configured || cipher.encryptCalls != 1 || store.owner != owner.ID { + t.Fatalf("status/error/cipher/store = %#v/%v/%d/%#v", status, err, cipher.encryptCalls, store) + } +} + +func TestProviderChannelServiceReadsStatusWithoutDecrypting(t *testing.T) { + t.Parallel() + owner := users.InternalUser{ID: uuid.New()} + cipher := &providerCountingCipher{} + store := &recordingChannelStore{statuses: []ChannelStatus{{Channel: ChannelPhone, Configured: true, Enabled: true, RevealConsent: true}}} + service := NewProviderChannelService(&recordingProviderAuthorizer{owner: owner}, store, cipher) + statuses, err := service.Get(context.Background(), users.VerifiedIdentity{Subject: "provider"}) + if err != nil || len(statuses) != 1 || statuses[0].Channel != ChannelPhone || cipher.decryptCalls != 0 || store.statusCalls != 1 { + t.Fatalf("statuses/error/decrypt/store = %#v/%v/%d/%d", statuses, err, cipher.decryptCalls, store.statusCalls) + } +} + +type recordingProviderAuthorizer struct { + owner users.InternalUser + err error +} + +func (a *recordingProviderAuthorizer) RequireProvider(context.Context, users.VerifiedIdentity) (users.InternalUser, error) { + return a.owner, a.err +} + +type recordingChannelStore struct { + calls int + owner uuid.UUID + value EncryptedChannel + statuses []ChannelStatus + statusCalls int +} + +func (s *recordingChannelStore) Replace(_ context.Context, owner uuid.UUID, value EncryptedChannel) (ChannelStatus, error) { + s.calls++ + s.owner = owner + s.value = value + return ChannelStatus{Channel: value.Channel, Configured: true, Enabled: value.Enabled, RevealConsent: value.RevealConsent}, nil +} + +func (s *recordingChannelStore) Statuses(context.Context, uuid.UUID) ([]ChannelStatus, error) { + s.statusCalls++ + return s.statuses, nil +} + +type providerCountingCipher struct { + decryptCalls int + encryptCalls int +} + +func (c *providerCountingCipher) Encrypt([]byte) (SealedContact, error) { + c.encryptCalls++ + return SealedContact{Ciphertext: []byte{1}, Nonce: []byte{2}}, nil +} +func (c *providerCountingCipher) Decrypt(SealedContact) ([]byte, error) { + c.decryptCalls++ + return []byte("revealed-contact"), nil +} diff --git a/backend/internal/contactreveal/reveal_service.go b/backend/internal/contactreveal/reveal_service.go new file mode 100644 index 0000000..258439a --- /dev/null +++ b/backend/internal/contactreveal/reveal_service.go @@ -0,0 +1,88 @@ +package contactreveal + +import ( + "context" + "errors" + "time" + + "github.com/SourceSenseiTheRealOne/juntly/backend/internal/users" + "github.com/google/uuid" +) + +var ( + ErrUnauthorized = errors.New("contact reveal unauthorized") + ErrForbidden = errors.New("contact reveal forbidden") + ErrUnavailable = errors.New("contact reveal unavailable") +) + +type Channel string + +const ( + ChannelPhone Channel = "phone" + ChannelWhatsApp Channel = "whatsapp" +) + +type IdentityReconciler interface { + Reconcile(context.Context, users.VerifiedIdentity) (users.InternalUser, bool, error) +} + +type RevealStore interface { + AuthorizeAndReserve(context.Context, uuid.UUID, uuid.UUID, Channel, time.Time) (SealedContact, error) +} + +type RevealService interface { + Reveal(context.Context, users.VerifiedIdentity, uuid.UUID, Channel) (RevealedContact, error) +} + +type RevealedContact struct { + Channel Channel + Value string +} + +type revealService struct { + identities IdentityReconciler + store RevealStore + cipher Cipher + now func() time.Time +} + +func NewRevealService(identities IdentityReconciler, store RevealStore, cipher Cipher, now func() time.Time) RevealService { + return revealService{identities: identities, store: store, cipher: cipher, now: now} +} + +func (s revealService) Reveal(ctx context.Context, identity users.VerifiedIdentity, listingID uuid.UUID, channel Channel) (RevealedContact, error) { + if listingID == uuid.Nil || !validChannel(channel) { + return RevealedContact{}, ErrForbidden + } + if s.identities == nil || s.store == nil || s.cipher == nil || s.now == nil { + return RevealedContact{}, ErrUnavailable + } + customer, _, err := s.identities.Reconcile(ctx, identity) + if err != nil { + if errors.Is(err, users.ErrInvalidIdentity) { + return RevealedContact{}, ErrUnauthorized + } + return RevealedContact{}, ErrUnavailable + } + sealed, err := s.store.AuthorizeAndReserve(ctx, customer.ID, listingID, channel, utcDay(s.now())) + if err != nil { + if errors.Is(err, ErrForbidden) { + return RevealedContact{}, ErrForbidden + } + return RevealedContact{}, ErrUnavailable + } + value, err := s.cipher.Decrypt(sealed) + if err != nil || len(value) == 0 { + return RevealedContact{}, ErrUnavailable + } + return RevealedContact{Channel: channel, Value: string(value)}, nil +} + +func validChannel(channel Channel) bool { + return channel == ChannelPhone || channel == ChannelWhatsApp +} + +func utcDay(now time.Time) time.Time { + now = now.UTC() + return time.Date(now.Year(), now.Month(), now.Day(), 0, 0, 0, 0, time.UTC) +} diff --git a/backend/internal/contactreveal/reveal_service_test.go b/backend/internal/contactreveal/reveal_service_test.go new file mode 100644 index 0000000..b6665de --- /dev/null +++ b/backend/internal/contactreveal/reveal_service_test.go @@ -0,0 +1,63 @@ +package contactreveal + +import ( + "context" + "errors" + "testing" + "time" + + "github.com/SourceSenseiTheRealOne/juntly/backend/internal/users" + "github.com/google/uuid" +) + +func TestRevealServiceDeniesBeforeDecryptingContact(t *testing.T) { + t.Parallel() + identity := users.VerifiedIdentity{Subject: "customer"} + store := &recordingRevealStore{err: ErrForbidden} + cipher := &countingCipher{} + service := NewRevealService(&recordingIdentityReconciler{user: users.InternalUser{ID: uuid.New()}}, store, cipher, func() time.Time { return time.Date(2026, 8, 24, 12, 0, 0, 0, time.UTC) }) + _, err := service.Reveal(context.Background(), identity, uuid.New(), ChannelPhone) + if !errors.Is(err, ErrForbidden) || cipher.decryptCalls != 0 || store.calls != 1 { + t.Fatalf("error/decrypt/store = %v/%d/%d", err, cipher.decryptCalls, store.calls) + } +} + +func TestRevealServiceDecryptsOnlyAuthorizedReservedContact(t *testing.T) { + t.Parallel() + day := time.Date(2026, 8, 24, 17, 30, 0, 0, time.FixedZone("offset", 3600)) + store := &recordingRevealStore{sealed: SealedContact{Ciphertext: []byte{1}, Nonce: []byte{2}}} + cipher := &countingCipher{} + service := NewRevealService(&recordingIdentityReconciler{user: users.InternalUser{ID: uuid.New()}}, store, cipher, func() time.Time { return day }) + value, err := service.Reveal(context.Background(), users.VerifiedIdentity{Subject: "customer"}, uuid.New(), ChannelWhatsApp) + wantDay := time.Date(2026, 8, 24, 0, 0, 0, 0, time.UTC) + if err != nil || value.Channel != ChannelWhatsApp || value.Value == "" || cipher.decryptCalls != 1 || !store.day.Equal(wantDay) { + t.Fatalf("value/error/decrypt/day = %#v/%v/%d/%s", value, err, cipher.decryptCalls, store.day) + } +} + +type recordingIdentityReconciler struct{ user users.InternalUser } + +func (r *recordingIdentityReconciler) Reconcile(context.Context, users.VerifiedIdentity) (users.InternalUser, bool, error) { + return r.user, false, nil +} + +type recordingRevealStore struct { + calls int + err error + sealed SealedContact + day time.Time +} + +func (r *recordingRevealStore) AuthorizeAndReserve(_ context.Context, _ uuid.UUID, _ uuid.UUID, _ Channel, day time.Time) (SealedContact, error) { + r.calls++ + r.day = day + return r.sealed, r.err +} + +type countingCipher struct{ decryptCalls int } + +func (*countingCipher) Encrypt([]byte) (SealedContact, error) { return SealedContact{}, nil } +func (c *countingCipher) Decrypt(SealedContact) ([]byte, error) { + c.decryptCalls++ + return []byte("revealed-contact"), nil +} diff --git a/backend/internal/contactreveal/sql_store.go b/backend/internal/contactreveal/sql_store.go new file mode 100644 index 0000000..cf6b72c --- /dev/null +++ b/backend/internal/contactreveal/sql_store.go @@ -0,0 +1,115 @@ +package contactreveal + +import ( + "context" + "database/sql" + "errors" + "time" + + "github.com/google/uuid" +) + +type sqlRevealStore struct{ database *sql.DB } + +func NewSQLRevealStore(database *sql.DB) RevealStore { + return sqlRevealStore{database: database} +} + +func (s sqlRevealStore) AuthorizeAndReserve(ctx context.Context, customerID, listingID uuid.UUID, channel Channel, day time.Time) (SealedContact, error) { + if s.database == nil || customerID == uuid.Nil || listingID == uuid.Nil || !validChannel(channel) { + return SealedContact{}, ErrForbidden + } + transaction, err := s.database.BeginTx(ctx, nil) + if err != nil { + return SealedContact{}, ErrUnavailable + } + committed := false + defer func() { + if !committed { + _ = transaction.Rollback() + } + }() + var providerID uuid.UUID + if err := transaction.QueryRowContext(ctx, ` + select internal_user_id from public.listings + where id = $1 and state = 'active' + for key share + `, listingID).Scan(&providerID); err != nil { + return SealedContact{}, policyError(err) + } + if providerID == customerID { + return SealedContact{}, ErrForbidden + } + var sealed SealedContact + if err := transaction.QueryRowContext(ctx, ` + select ciphertext, nonce from public.provider_contact_channels + where internal_user_id = $1 and channel = $2 and enabled and reveal_consent + for key share + `, providerID, string(channel)).Scan(&sealed.Ciphertext, &sealed.Nonce); err != nil { + return SealedContact{}, policyError(err) + } + if _, err := transaction.ExecContext(ctx, ` + insert into public.contact_reveal_daily_limits (id, customer_internal_user_id, utc_day, successful_count) + values ($1, $2, $3, 0) + on conflict (customer_internal_user_id, utc_day) do nothing + `, uuid.New(), customerID, day); err != nil { + return SealedContact{}, ErrUnavailable + } + var count int + if err := transaction.QueryRowContext(ctx, ` + select successful_count from public.contact_reveal_daily_limits + where customer_internal_user_id = $1 and utc_day = $2 + for update + `, customerID, day).Scan(&count); err != nil { + return SealedContact{}, ErrUnavailable + } + var existing bool + err = transaction.QueryRowContext(ctx, ` + select true from public.contact_reveal_events + where customer_internal_user_id = $1 and listing_id = $2 and channel = $3 and utc_day = $4 + limit 1 + `, customerID, listingID, string(channel), day).Scan(&existing) + if err == nil && existing { + if err := transaction.Commit(); err != nil { + return SealedContact{}, ErrUnavailable + } + committed = true + return copySealed(sealed), nil + } + if !errors.Is(err, sql.ErrNoRows) { + return SealedContact{}, ErrUnavailable + } + if count >= 10 { + return SealedContact{}, ErrForbidden + } + if _, err := transaction.ExecContext(ctx, ` + update public.contact_reveal_daily_limits + set successful_count = successful_count + 1, updated_at = timezone('utc', now()) + where customer_internal_user_id = $1 and utc_day = $2 and successful_count < 10 + `, customerID, day); err != nil { + return SealedContact{}, ErrUnavailable + } + if _, err := transaction.ExecContext(ctx, ` + insert into public.contact_reveal_events + (id, customer_internal_user_id, provider_internal_user_id, listing_id, channel, utc_day) + values ($1, $2, $3, $4, $5, $6) + `, uuid.New(), customerID, providerID, listingID, string(channel), day); err != nil { + return SealedContact{}, ErrUnavailable + } + if err := transaction.Commit(); err != nil { + return SealedContact{}, ErrUnavailable + } + committed = true + return copySealed(sealed), nil +} + +func policyError(err error) error { + if errors.Is(err, sql.ErrNoRows) { + return ErrForbidden + } + return ErrUnavailable +} + +func copySealed(value SealedContact) SealedContact { + return SealedContact{Ciphertext: append([]byte(nil), value.Ciphertext...), Nonce: append([]byte(nil), value.Nonce...)} +} diff --git a/backend/internal/contactreveal/sql_store_test.go b/backend/internal/contactreveal/sql_store_test.go new file mode 100644 index 0000000..dd2cb91 --- /dev/null +++ b/backend/internal/contactreveal/sql_store_test.go @@ -0,0 +1,171 @@ +package contactreveal + +import ( + "context" + "database/sql" + "encoding/base64" + "errors" + "os" + "sync" + "testing" + "time" + + "github.com/google/uuid" + _ "github.com/jackc/pgx/v5/stdlib" +) + +func TestSQLRevealStoreCreatesOneSameDayLeadEventAndLimitIncrement(t *testing.T) { + database := openRevealDatabase(t) + ctx := context.Background() + customerID, listingID, day := seedRevealFixture(t, database) + store := NewSQLRevealStore(database) + first, err := store.AuthorizeAndReserve(ctx, customerID, listingID, ChannelPhone, day) + if err != nil || len(first.Ciphertext) == 0 || len(first.Nonce) == 0 { + t.Fatalf("first reservation = %#v, err = %v", first, err) + } + second, err := store.AuthorizeAndReserve(ctx, customerID, listingID, ChannelPhone, day) + if err != nil || string(second.Ciphertext) != string(first.Ciphertext) || string(second.Nonce) != string(first.Nonce) { + t.Fatalf("second reservation = %#v, err = %v", second, err) + } + var events, count int + if err := database.QueryRowContext(ctx, `select count(*) from public.contact_reveal_events where customer_internal_user_id = $1 and listing_id = $2`, customerID, listingID).Scan(&events); err != nil { + t.Fatalf("event count: %v", err) + } + if err := database.QueryRowContext(ctx, `select successful_count from public.contact_reveal_daily_limits where customer_internal_user_id = $1 and utc_day = $2`, customerID, day).Scan(&count); err != nil { + t.Fatalf("daily count: %v", err) + } + if events != 1 || count != 1 { + t.Fatalf("events/count = %d/%d", events, count) + } +} + +func TestSQLRevealStoreEnforcesDailyCapUnderConcurrency(t *testing.T) { + database := openRevealDatabase(t) + ctx := context.Background() + customerID, firstListingID, day := seedRevealFixture(t, database) + var providerID, categoryID, localityID uuid.UUID + if err := database.QueryRowContext(ctx, `select internal_user_id, category_id, primary_locality_id from public.listings where id = $1`, firstListingID).Scan(&providerID, &categoryID, &localityID); err != nil { + t.Fatalf("fixture listing: %v", err) + } + listingIDs := []uuid.UUID{firstListingID} + for index := 1; index < 11; index++ { + id := uuid.New() + if _, err := database.ExecContext(ctx, `insert into public.listings (id, internal_user_id, category_id, primary_locality_id, title, description, price_type, price_minor, currency, travels_to_customer, state) values ($1, $2, $3, $4, $5, 'A synthetic active listing used only for contact reveal rate-limit tests.', 'fixed', 5000, 'EUR', true, 'active')`, id, providerID, categoryID, localityID, "Rate listing "+id.String()); err != nil { + t.Fatalf("seed listing %d: %v", index, err) + } + listingIDs = append(listingIDs, id) + } + store := NewSQLRevealStore(database) + start := make(chan struct{}) + errorsOut := make(chan error, len(listingIDs)) + var workers sync.WaitGroup + for _, listingID := range listingIDs { + listingID := listingID + workers.Add(1) + go func() { + defer workers.Done() + <-start + _, err := store.AuthorizeAndReserve(ctx, customerID, listingID, ChannelPhone, day) + errorsOut <- err + }() + } + close(start) + workers.Wait() + close(errorsOut) + successes, forbidden := 0, 0 + for err := range errorsOut { + if err == nil { + successes++ + } else if errors.Is(err, ErrForbidden) { + forbidden++ + } else { + t.Fatalf("reveal error: %v", err) + } + } + if successes != 10 || forbidden != 1 { + t.Fatalf("successes/forbidden = %d/%d", successes, forbidden) + } + var count, events int + if err := database.QueryRowContext(ctx, `select successful_count from public.contact_reveal_daily_limits where customer_internal_user_id = $1 and utc_day = $2`, customerID, day).Scan(&count); err != nil { + t.Fatalf("daily count: %v", err) + } + if err := database.QueryRowContext(ctx, `select count(*) from public.contact_reveal_events where customer_internal_user_id = $1 and utc_day = $2`, customerID, day).Scan(&events); err != nil { + t.Fatalf("event count: %v", err) + } + if count != 10 || events != 10 { + t.Fatalf("count/events = %d/%d", count, events) + } +} + +func openRevealDatabase(t *testing.T) *sql.DB { + t.Helper() + url := os.Getenv("TEST_DATABASE_URL") + if url == "" { + t.Skip("TEST_DATABASE_URL is required for PostgreSQL integration tests") + } + database, err := sql.Open("pgx", url) + if err != nil { + t.Fatalf("open database: %v", err) + } + t.Cleanup(func() { _ = database.Close() }) + return database +} + +func seedRevealFixture(t *testing.T, database *sql.DB) (uuid.UUID, uuid.UUID, time.Time) { + t.Helper() + ctx := context.Background() + var categoryID, localityID uuid.UUID + if err := database.QueryRowContext(ctx, `select id from public.service_categories where active order by id limit 1`).Scan(&categoryID); err != nil { + t.Fatalf("category: %v", err) + } + if err := database.QueryRowContext(ctx, `select id from public.localities where active order by id limit 1`).Scan(&localityID); err != nil { + t.Fatalf("locality: %v", err) + } + providerID, customerID, listingID := uuid.New(), uuid.New(), uuid.New() + key := make([]byte, 32) + for index := range key { + key[index] = byte(index + 1) + } + cipher, err := NewCipher(base64.StdEncoding.EncodeToString(key)) + if err != nil { + t.Fatalf("cipher: %v", err) + } + sealed, err := cipher.Encrypt([]byte("test-contact")) + if err != nil { + t.Fatalf("encrypt: %v", err) + } + tx, err := database.BeginTx(ctx, nil) + if err != nil { + t.Fatalf("begin fixture: %v", err) + } + defer tx.Rollback() + for _, user := range []struct { + id uuid.UUID + subject string + }{{providerID, "reveal_provider_" + providerID.String()}, {customerID, "reveal_customer_" + customerID.String()}} { + if _, err := tx.ExecContext(ctx, `insert into public.internal_users (id, clerk_subject) values ($1, $2)`, user.id, user.subject); err != nil { + t.Fatalf("user: %v", err) + } + if _, err := tx.ExecContext(ctx, `insert into public.user_accounts (internal_user_id, provider_enabled) values ($1, true)`, user.id); err != nil { + t.Fatalf("account: %v", err) + } + } + if _, err := tx.ExecContext(ctx, `insert into public.provider_profiles (internal_user_id, display_name, provider_type, bio, primary_locality_id, max_travel_distance_km, travels_to_customer) values ($1, 'Reveal provider', 'professional', 'Synthetic provider for reveal tests.', $2, 25, true)`, providerID, localityID); err != nil { + t.Fatalf("profile: %v", err) + } + if _, err := tx.ExecContext(ctx, `insert into public.listings (id, internal_user_id, category_id, primary_locality_id, title, description, price_type, price_minor, currency, travels_to_customer, state) values ($1, $2, $3, $4, 'Reveal listing', 'A synthetic active listing used only for contact reveal persistence tests.', 'fixed', 5000, 'EUR', true, 'active')`, listingID, providerID, categoryID, localityID); err != nil { + t.Fatalf("listing: %v", err) + } + if _, err := tx.ExecContext(ctx, `insert into public.provider_contact_channels (id, internal_user_id, channel, ciphertext, nonce, key_version, enabled, reveal_consent) values ($1, $2, 'phone', $3, $4, 'v1', true, true)`, uuid.New(), providerID, sealed.Ciphertext, sealed.Nonce); err != nil { + t.Fatalf("channel: %v", err) + } + if err := tx.Commit(); err != nil { + t.Fatalf("commit fixture: %v", err) + } + t.Cleanup(func() { + if _, err := database.ExecContext(ctx, `delete from public.internal_users where id in ($1, $2)`, providerID, customerID); err != nil { + t.Errorf("cleanup: %v", err) + } + }) + return customerID, listingID, time.Date(2026, 8, 24, 0, 0, 0, 0, time.UTC) +} diff --git a/backend/internal/discovery/model.go b/backend/internal/discovery/model.go new file mode 100644 index 0000000..0cd0675 --- /dev/null +++ b/backend/internal/discovery/model.go @@ -0,0 +1,70 @@ +package discovery + +import ( + "context" + "errors" + "time" + + "github.com/google/uuid" +) + +var ( + ErrInvalidRequest = errors.New("invalid public discovery request") + ErrNotFound = errors.New("public listing not found") + ErrUnavailable = errors.New("public discovery unavailable") +) + +type PriceType string + +const ( + PriceTypeFixed PriceType = "fixed" + PriceTypeHourly PriceType = "hourly" + PriceTypeDaily PriceType = "daily" + PriceTypeQuote PriceType = "quote" + PriceTypeNegotiable PriceType = "negotiable" +) + +type ServiceMode string + +const ( + ServiceModeTravelsToCustomer ServiceMode = "travels_to_customer" + ServiceModeReceivesCustomer ServiceMode = "receives_customer" + ServiceModeRemoteServices ServiceMode = "remote_services" +) + +type Request struct { + Locale string + CategoryID uuid.UUID + Query string + NearLocalityID uuid.UUID + RadiusKM int + PriceType PriceType + ServiceMode ServiceMode +} + +type Listing struct { + ID uuid.UUID + Title string + Description string + CategoryID uuid.UUID + CategorySlug string + CategoryName string + PrimaryLocalityID uuid.UUID + LocalitySlug string + LocalityName string + PriceType PriceType + PriceMinor *int + Currency string + TravelsToCustomer bool + ReceivesCustomer bool + RemoteServices bool + ProviderDisplayName string + ProviderType string + Promoted bool + UpdatedAt time.Time +} + +type Repository interface { + Search(context.Context, Request) ([]Listing, error) + Get(context.Context, uuid.UUID, string) (*Listing, error) +} diff --git a/backend/internal/discovery/projection_test.go b/backend/internal/discovery/projection_test.go new file mode 100644 index 0000000..fac6544 --- /dev/null +++ b/backend/internal/discovery/projection_test.go @@ -0,0 +1,48 @@ +package discovery + +import ( + "reflect" + "strings" + "testing" +) + +func TestPublicListingProjectionExcludesPrivateColumnsAndFields(t *testing.T) { + t.Parallel() + columns := strings.ToLower(publicListingColumns) + for _, forbidden := range []string{ + "internal_user_id", + "clerk_subject", + "object_reference", + "checksum_sha256", + "listing_event", + "reason", + "latitude", + "longitude", + "center", + "bio", + "service_localit", + "phone", + "email", + } { + if strings.Contains(columns, forbidden) { + t.Fatalf("public select contains private column %q", forbidden) + } + } + allowed := map[string]bool{ + "ID": true, "Title": true, "Description": true, + "CategoryID": true, "CategorySlug": true, "CategoryName": true, + "PrimaryLocalityID": true, "LocalitySlug": true, "LocalityName": true, + "PriceType": true, "PriceMinor": true, "Currency": true, + "TravelsToCustomer": true, "ReceivesCustomer": true, "RemoteServices": true, + "ProviderDisplayName": true, "ProviderType": true, "Promoted": true, "UpdatedAt": true, + } + typeOfListing := reflect.TypeOf(Listing{}) + if typeOfListing.NumField() != len(allowed) { + t.Fatalf("public field count = %d, want %d", typeOfListing.NumField(), len(allowed)) + } + for index := 0; index < typeOfListing.NumField(); index++ { + if !allowed[typeOfListing.Field(index).Name] { + t.Fatalf("private public model field %q", typeOfListing.Field(index).Name) + } + } +} diff --git a/backend/internal/discovery/service.go b/backend/internal/discovery/service.go new file mode 100644 index 0000000..535705e --- /dev/null +++ b/backend/internal/discovery/service.go @@ -0,0 +1,107 @@ +package discovery + +import ( + "context" + "errors" + "strings" + "unicode/utf8" + + "github.com/google/uuid" +) + +type Service interface { + Search(context.Context, Request) ([]Listing, error) + Get(context.Context, string, string) (*Listing, error) +} + +type service struct{ repository Repository } + +func NewService(repository Repository) Service { return service{repository: repository} } + +func (s service) Search(ctx context.Context, request Request) ([]Listing, error) { + request, ok := normalizeRequest(request) + if !ok { + return nil, ErrInvalidRequest + } + if s.repository == nil { + return nil, ErrUnavailable + } + values, err := s.repository.Search(ctx, request) + if err != nil { + return nil, ErrUnavailable + } + return append([]Listing(nil), values...), nil +} + +func (s service) Get(ctx context.Context, rawID, locale string) (*Listing, error) { + if !validLocale(locale) { + return nil, ErrInvalidRequest + } + if s.repository == nil { + return nil, ErrUnavailable + } + id, err := uuid.Parse(rawID) + if err != nil { + return nil, ErrNotFound + } + value, err := s.repository.Get(ctx, id, locale) + switch { + case err == nil && value != nil: + cloned := *value + return &cloned, nil + case errors.Is(err, ErrNotFound): + return nil, ErrNotFound + default: + return nil, ErrUnavailable + } +} + +func normalizeRequest(request Request) (Request, bool) { + if !validLocale(request.Locale) { + return Request{}, false + } + request.Query = strings.Join(strings.Fields(request.Query), " ") + if (request.NearLocalityID == uuid.Nil) != (request.RadiusKM == 0) || request.RadiusKM < 0 || request.RadiusKM > 200 { + return Request{}, false + } + if request.RadiusKM > 0 && request.RadiusKM < 1 { + return Request{}, false + } + if request.Query != "" && (utf8.RuneCountInString(request.Query) < 2 || utf8.RuneCountInString(request.Query) > 80) { + return Request{}, false + } + if request.PriceType != "" && !validPriceType(request.PriceType) { + return Request{}, false + } + if request.ServiceMode != "" && !validServiceMode(request.ServiceMode) { + return Request{}, false + } + return request, true +} + +func validLocale(locale string) bool { + switch locale { + case "pt-PT", "en", "es": + return true + default: + return false + } +} + +func validPriceType(value PriceType) bool { + switch value { + case PriceTypeFixed, PriceTypeHourly, PriceTypeDaily, PriceTypeQuote, PriceTypeNegotiable: + return true + default: + return false + } +} + +func validServiceMode(value ServiceMode) bool { + switch value { + case ServiceModeTravelsToCustomer, ServiceModeReceivesCustomer, ServiceModeRemoteServices: + return true + default: + return false + } +} diff --git a/backend/internal/discovery/service_test.go b/backend/internal/discovery/service_test.go new file mode 100644 index 0000000..fdf68a4 --- /dev/null +++ b/backend/internal/discovery/service_test.go @@ -0,0 +1,75 @@ +package discovery + +import ( + "context" + "errors" + "testing" + + "github.com/google/uuid" +) + +func TestServiceRejectsUnpairedOrInvalidDiscoveryFilters(t *testing.T) { + t.Parallel() + repository := &recordingRepository{} + for _, request := range []Request{ + {Locale: "pt-PT", NearLocalityID: uuid.New()}, + {Locale: "pt-PT", RadiusKM: 25}, + {Locale: "pt-PT", Query: "x"}, + {Locale: "pt-PT", RadiusKM: 201}, + {Locale: "fr"}, + } { + _, err := NewService(repository).Search(context.Background(), request) + if !errors.Is(err, ErrInvalidRequest) || repository.calls != 0 { + t.Fatalf("err/calls=%v/%d", err, repository.calls) + } + } +} +func TestServiceReturnsActivePublicProjectionOnlyThroughRepository(t *testing.T) { + t.Parallel() + repository := &recordingRepository{results: []Listing{{ID: uuid.New(), Title: "Active listing", CategorySlug: "plumbing", LocalitySlug: "zebreira", PriceType: "fixed", Currency: "EUR"}}} + request := Request{Locale: "en", Query: "plumbing"} + values, err := NewService(repository).Search(context.Background(), request) + if err != nil || len(values) != 1 || repository.calls != 1 || repository.request.Query != "plumbing" { + t.Fatalf("values/err/repository=%#v/%v/%#v", values, err, repository) + } +} + +func TestServiceHidesMalformedOrMissingPublicListing(t *testing.T) { + t.Parallel() + repository := &recordingRepository{err: ErrNotFound} + service := NewService(repository) + value, err := service.Get(context.Background(), "not-a-uuid", "pt-PT") + if value != nil || !errors.Is(err, ErrNotFound) || repository.getCalls != 0 { + t.Fatalf("malformed value/error/calls = %#v/%v/%d", value, err, repository.getCalls) + } + value, err = service.Get(context.Background(), uuid.NewString(), "pt-PT") + if value != nil || !errors.Is(err, ErrNotFound) || repository.getCalls != 1 { + t.Fatalf("missing value/error/calls = %#v/%v/%d", value, err, repository.getCalls) + } +} + +func TestServiceRejectsUnsupportedDetailLocale(t *testing.T) { + t.Parallel() + value, err := NewService(&recordingRepository{}).Get(context.Background(), uuid.NewString(), "fr") + if value != nil || !errors.Is(err, ErrInvalidRequest) { + t.Fatalf("value/error = %#v/%v", value, err) + } +} + +type recordingRepository struct { + request Request + results []Listing + calls int + getCalls int + err error +} + +func (r *recordingRepository) Search(_ context.Context, q Request) ([]Listing, error) { + r.calls++ + r.request = q + return r.results, r.err +} +func (r *recordingRepository) Get(context.Context, uuid.UUID, string) (*Listing, error) { + r.getCalls++ + return nil, r.err +} diff --git a/backend/internal/discovery/sql_repository.go b/backend/internal/discovery/sql_repository.go new file mode 100644 index 0000000..75f66d0 --- /dev/null +++ b/backend/internal/discovery/sql_repository.go @@ -0,0 +1,175 @@ +package discovery + +import ( + "context" + "database/sql" + "errors" + + "github.com/google/uuid" +) + +type sqlRepository struct{ database *sql.DB } + +func NewSQLRepository(database *sql.DB) Repository { return sqlRepository{database: database} } + +func (r sqlRepository) Search(ctx context.Context, request Request) ([]Listing, error) { + if r.database == nil { + return nil, errors.New("database is nil") + } + rows, err := r.database.QueryContext(ctx, publicListingSearchSQL, + request.Locale, + nilUUID(request.CategoryID), + request.Query, + nilUUID(request.NearLocalityID), + request.RadiusKM, + nilPriceType(request.PriceType), + nilServiceMode(request.ServiceMode), + ) + if err != nil { + return nil, err + } + defer rows.Close() + return scanListings(rows) +} + +func (r sqlRepository) Get(ctx context.Context, id uuid.UUID, locale string) (*Listing, error) { + if r.database == nil { + return nil, errors.New("database is nil") + } + row := r.database.QueryRowContext(ctx, publicListingDetailSQL, locale, id) + value, err := scanListing(row) + if errors.Is(err, sql.ErrNoRows) { + return nil, ErrNotFound + } + if err != nil { + return nil, err + } + return &value, nil +} + +func nilUUID(value uuid.UUID) any { + if value == uuid.Nil { + return nil + } + return value +} + +func nilPriceType(value PriceType) any { + if value == "" { + return nil + } + return string(value) +} + +func nilServiceMode(value ServiceMode) any { + if value == "" { + return nil + } + return string(value) +} + +const publicListingColumns = ` + listing.id, listing.title, listing.description, + category.id, category.slug, category_translation.name, + locality.id, locality.slug, locality.name, + listing.price_type, listing.price_minor, listing.currency, + listing.travels_to_customer, listing.receives_customer, listing.remote_services, + profile.display_name, profile.provider_type, + exists(select 1 from public.listing_promotions promotion where promotion.listing_id=listing.id and promotion.status='active' and promotion.starts_at<=timezone('utc',now()) and promotion.ends_at>timezone('utc',now())) as promoted, + listing.updated_at +` + +const publicListingFrom = ` +from public.listings listing +join public.provider_profiles profile on profile.internal_user_id = listing.internal_user_id +join public.service_categories category on category.id = listing.category_id and category.active +left join public.service_categories parent on parent.id = category.parent_id +join public.service_category_translations category_translation + on category_translation.category_id = category.id and category_translation.locale = $1 +join public.supported_locales locale on locale.id = category_translation.locale and locale.active +join public.localities locality on locality.id = listing.primary_locality_id and locality.active +where listing.state = 'active' + and (category.parent_id is null or parent.active) +` + +const publicListingSearchSQL = ` +select` + publicListingColumns + publicListingFrom + ` + and ($2::uuid is null or listing.category_id = $2) + and ($3::text = '' or listing.title ilike '%' || $3 || '%' or listing.description ilike '%' || $3 || '%') + and ($4::uuid is null or exists (select 1 from public.localities origin where origin.id = $4 and origin.active)) + and ($4::uuid is null or st_dwithin( + locality.center, + (select origin.center from public.localities origin where origin.id = $4 and origin.active), + $5 * 1000 + )) + and ($6::text is null or listing.price_type = $6) + and ($7::text is null or + ($7 = 'travels_to_customer' and listing.travels_to_customer) or + ($7 = 'receives_customer' and listing.receives_customer) or + ($7 = 'remote_services' and listing.remote_services) + ) +order by + promoted desc, + case when $2::uuid is null then 1 else 0 end, + case when $4::uuid is null then 0 else round(st_distance( + locality.center, + (select origin.center from public.localities origin where origin.id = $4 and origin.active) + ))::integer end, + listing.updated_at desc, + listing.id +` + +const publicListingDetailSQL = ` +select` + publicListingColumns + publicListingFrom + ` + and listing.id = $2 +` + +type rowScanner interface{ Scan(...any) error } + +func scanListings(rows *sql.Rows) ([]Listing, error) { + values := make([]Listing, 0) + for rows.Next() { + value, err := scanListing(rows) + if err != nil { + return nil, err + } + values = append(values, value) + } + if err := rows.Err(); err != nil { + return nil, err + } + return values, nil +} + +func scanListing(row rowScanner) (Listing, error) { + var value Listing + var priceMinor sql.NullInt64 + if err := row.Scan( + &value.ID, + &value.Title, + &value.Description, + &value.CategoryID, + &value.CategorySlug, + &value.CategoryName, + &value.PrimaryLocalityID, + &value.LocalitySlug, + &value.LocalityName, + &value.PriceType, + &priceMinor, + &value.Currency, + &value.TravelsToCustomer, + &value.ReceivesCustomer, + &value.RemoteServices, + &value.ProviderDisplayName, + &value.ProviderType, + &value.Promoted, + &value.UpdatedAt, + ); err != nil { + return Listing{}, err + } + if priceMinor.Valid { + minor := int(priceMinor.Int64) + value.PriceMinor = &minor + } + return value, nil +} diff --git a/backend/internal/discovery/sql_repository_ordering_test.go b/backend/internal/discovery/sql_repository_ordering_test.go new file mode 100644 index 0000000..a602cb8 --- /dev/null +++ b/backend/internal/discovery/sql_repository_ordering_test.go @@ -0,0 +1,92 @@ +package discovery + +import ( + "context" + "testing" + "time" + + "github.com/google/uuid" +) + +func TestSQLRepositoryOrdersRadiusThenUpdatedThenStableID(t *testing.T) { + database := openDiscoveryDatabase(t) + ctx := context.Background() + categoryID, _ := seededDiscoveryReferences(t, database) + var parishID uuid.UUID + if err := database.QueryRow(`select parent_parish_id from public.localities where active order by id limit 1`).Scan(&parishID); err != nil { + t.Fatalf("seeded parish: %v", err) + } + ownerID := uuid.New() + originID := uuid.MustParse("00000000-0000-4000-8000-000000000101") + nearID := uuid.MustParse("00000000-0000-4000-8000-000000000102") + originListingID := uuid.MustParse("00000000-0000-4000-8000-000000000201") + nearListingID := uuid.MustParse("00000000-0000-4000-8000-000000000202") + tiedNearListingID := uuid.MustParse("00000000-0000-4000-8000-000000000203") + fixedUpdatedAt := time.Date(2026, 8, 24, 12, 0, 0, 0, time.UTC) + tx, err := database.BeginTx(ctx, nil) + if err != nil { + t.Fatalf("begin radius seed: %v", err) + } + defer tx.Rollback() + for _, locality := range []struct { + id uuid.UUID + slug string + longitude float64 + }{ + {originID, "discovery-radius-origin", -7.0}, + {nearID, "discovery-radius-near", -7.01}, + } { + if _, err := tx.ExecContext(ctx, ` + insert into public.localities + (id, slug, name, parent_parish_id, source, source_element_id, source_version, source_retrieved_at, latitude, longitude, active) + values ($1, $2, $2, $3, 'synthetic-test', $4, '1', '2026-08-24T00:00:00Z', 40.8, $5, true) + `, locality.id, locality.slug, parishID, "D:"+locality.slug, locality.longitude); err != nil { + t.Fatalf("seed radius locality: %v", err) + } + } + if _, err := tx.ExecContext(ctx, `insert into public.internal_users (id, clerk_subject) values ($1, $2)`, ownerID, "discovery_radius_"+ownerID.String()); err != nil { + t.Fatalf("seed radius owner: %v", err) + } + if _, err := tx.ExecContext(ctx, `insert into public.user_accounts (internal_user_id, provider_enabled) values ($1, true)`, ownerID); err != nil { + t.Fatalf("seed radius account: %v", err) + } + if _, err := tx.ExecContext(ctx, ` + insert into public.provider_profiles + (internal_user_id, display_name, provider_type, bio, primary_locality_id, max_travel_distance_km, travels_to_customer) + values ($1, 'Radius provider', 'professional', 'Radius discovery provider profile.', $2, 25, true) + `, ownerID, originID); err != nil { + t.Fatalf("seed radius profile: %v", err) + } + for _, listing := range []struct { + id uuid.UUID + locality uuid.UUID + }{ + {originListingID, originID}, + {nearListingID, nearID}, + {tiedNearListingID, nearID}, + } { + if _, err := tx.ExecContext(ctx, ` + insert into public.listings + (id, internal_user_id, category_id, primary_locality_id, title, description, price_type, price_minor, currency, travels_to_customer, state, updated_at) + values ($1, $2, $3, $4, 'Radius plumbing', 'A public listing used to prove deterministic radius ordering.', 'fixed', 5000, 'EUR', true, 'active', $5) + `, listing.id, ownerID, categoryID, listing.locality, fixedUpdatedAt); err != nil { + t.Fatalf("seed radius listing: %v", err) + } + } + if err := tx.Commit(); err != nil { + t.Fatalf("commit radius seed: %v", err) + } + t.Cleanup(func() { + if _, err := database.ExecContext(ctx, "delete from public.internal_users where id = $1", ownerID); err != nil { + t.Errorf("cleanup radius owner: %v", err) + } + if _, err := database.ExecContext(ctx, "delete from public.localities where id in ($1, $2)", originID, nearID); err != nil { + t.Errorf("cleanup radius localities: %v", err) + } + }) + + values, err := NewSQLRepository(database).Search(ctx, Request{Locale: "pt-PT", Query: "radius", NearLocalityID: originID, RadiusKM: 2}) + if err != nil || len(values) != 3 || values[0].ID != originListingID || values[1].ID != nearListingID || values[2].ID != tiedNearListingID { + t.Fatalf("radius ordering values/error = %#v/%v", values, err) + } +} diff --git a/backend/internal/discovery/sql_repository_test.go b/backend/internal/discovery/sql_repository_test.go new file mode 100644 index 0000000..3f9a0a3 --- /dev/null +++ b/backend/internal/discovery/sql_repository_test.go @@ -0,0 +1,102 @@ +package discovery + +import ( + "context" + "database/sql" + "errors" + "os" + "testing" + + "github.com/google/uuid" + _ "github.com/jackc/pgx/v5/stdlib" +) + +func TestSQLRepositoryProjectsOnlyActiveListingsWithLocalizedRadiusSearch(t *testing.T) { + database := openDiscoveryDatabase(t) + ctx := context.Background() + categoryID, localityID := seededDiscoveryReferences(t, database) + ownerID, activeID, draftID := uuid.New(), uuid.New(), uuid.New() + tx, err := database.BeginTx(ctx, nil) + if err != nil { + t.Fatalf("begin discovery seed: %v", err) + } + defer tx.Rollback() + if _, err := tx.ExecContext(ctx, `insert into public.internal_users (id, clerk_subject) values ($1, $2)`, ownerID, "discovery_provider_"+ownerID.String()); err != nil { + t.Fatalf("seed discovery owner: %v", err) + } + if _, err := tx.ExecContext(ctx, `insert into public.user_accounts (internal_user_id, provider_enabled) values ($1, true)`, ownerID); err != nil { + t.Fatalf("seed discovery account: %v", err) + } + if _, err := tx.ExecContext(ctx, ` + insert into public.provider_profiles + (internal_user_id, display_name, provider_type, bio, primary_locality_id, max_travel_distance_km, travels_to_customer) + values ($1, 'Public provider', 'professional', 'Discovery test provider profile.', $2, 25, true) + `, ownerID, localityID); err != nil { + t.Fatalf("seed discovery profile: %v", err) + } + if _, err := tx.ExecContext(ctx, ` + insert into public.listings + (id, internal_user_id, category_id, primary_locality_id, title, description, price_type, price_minor, currency, travels_to_customer, state) + values + ($1, $2, $3, $4, 'Discovery active plumbing', 'A public plumbing listing for active discovery testing.', 'fixed', 5000, 'EUR', true, 'active'), + ($5, $2, $3, $4, 'Discovery private draft', 'A draft listing that must never appear in public discovery.', 'fixed', 5000, 'EUR', true, 'draft') + `, activeID, ownerID, categoryID, localityID, draftID); err != nil { + t.Fatalf("seed discovery listings: %v", err) + } + if err := tx.Commit(); err != nil { + t.Fatalf("commit discovery seed: %v", err) + } + t.Cleanup(func() { + if _, err := database.ExecContext(ctx, "delete from public.internal_users where id = $1", ownerID); err != nil { + t.Errorf("cleanup discovery owner: %v", err) + } + }) + + repository := NewSQLRepository(database) + values, err := repository.Search(ctx, Request{Locale: "pt-PT", Query: "plumbing", NearLocalityID: localityID, RadiusKM: 1, PriceType: PriceTypeFixed, ServiceMode: ServiceModeTravelsToCustomer}) + if err != nil || len(values) != 1 || values[0].ID != activeID { + t.Fatalf("public search values/error = %#v/%v", values, err) + } + value := values[0] + if value.CategoryID != categoryID || value.PrimaryLocalityID != localityID || value.CategoryName == "" || value.LocalityName == "" || value.ProviderDisplayName != "Public provider" || value.PriceMinor == nil || *value.PriceMinor != 5000 { + t.Fatalf("incomplete public projection = %#v", value) + } + found, err := repository.Get(ctx, activeID, "pt-PT") + if err != nil || found == nil || found.ID != activeID { + t.Fatalf("active public detail = %#v/%v", found, err) + } + missing, err := repository.Get(ctx, draftID, "pt-PT") + if missing != nil || !errors.Is(err, ErrNotFound) { + t.Fatalf("draft public detail = %#v/%v", missing, err) + } +} + +func openDiscoveryDatabase(t *testing.T) *sql.DB { + t.Helper() + url := os.Getenv("TEST_DATABASE_URL") + if url == "" { + t.Skip("TEST_DATABASE_URL is required for PostgreSQL integration tests") + } + database, err := sql.Open("pgx", url) + if err != nil { + t.Fatalf("open database: %v", err) + } + t.Cleanup(func() { + if err := database.Close(); err != nil { + t.Errorf("close database: %v", err) + } + }) + return database +} + +func seededDiscoveryReferences(t *testing.T, database *sql.DB) (uuid.UUID, uuid.UUID) { + t.Helper() + var categoryID, localityID uuid.UUID + if err := database.QueryRow(`select id from public.service_categories where active order by id limit 1`).Scan(&categoryID); err != nil { + t.Fatalf("seeded category: %v", err) + } + if err := database.QueryRow(`select id from public.localities where active order by id limit 1`).Scan(&localityID); err != nil { + t.Fatalf("seeded locality: %v", err) + } + return categoryID, localityID +} diff --git a/backend/internal/entitlements/model.go b/backend/internal/entitlements/model.go new file mode 100644 index 0000000..e6368ab --- /dev/null +++ b/backend/internal/entitlements/model.go @@ -0,0 +1,74 @@ +package entitlements + +import ( + "errors" + "github.com/google/uuid" + "time" +) + +var ( + ErrInvalid = errors.New("entitlement invalid request") + ErrUnauthorized = errors.New("entitlement unauthorized") + ErrForbidden = errors.New("entitlement forbidden") + ErrConflict = errors.New("entitlement conflict") + ErrUnavailable = errors.New("entitlement unavailable") +) + +type Status string + +const ( + StatusPending Status = "pending" + StatusActive Status = "active" + StatusCancelled Status = "cancelled" + StatusExpired Status = "expired" +) + +type Plan struct { + ID uuid.UUID `json:"id"` + Slug string `json:"slug"` + Name string `json:"name"` + PriceMinor int `json:"priceMinor"` + Currency string `json:"currency"` + BillingDays int `json:"billingDays"` + MaxActiveListings int `json:"maxActiveListings"` + MaxPhotosPerListing int `json:"maxPhotosPerListing"` + AnalyticsEnabled bool `json:"analyticsEnabled"` +} +type PromotionPeriod struct { + ID uuid.UUID `json:"id"` + Slug string `json:"slug"` + Name string `json:"name"` + DurationDays int `json:"durationDays"` + PriceMinor int `json:"priceMinor"` + Currency string `json:"currency"` +} +type Catalog struct { + Plans []Plan `json:"plans"` + PromotionPeriods []PromotionPeriod `json:"promotionPeriods"` +} +type Subscription struct { + ID uuid.UUID `json:"id"` + ProviderID uuid.UUID `json:"providerId"` + PlanID uuid.UUID `json:"planId"` + Status Status `json:"status"` + StartsAt *time.Time `json:"startsAt"` + EndsAt *time.Time `json:"endsAt"` + CreatedAt time.Time `json:"createdAt"` + UpdatedAt time.Time `json:"updatedAt"` +} +type Promotion struct { + ID uuid.UUID `json:"id"` + ListingID uuid.UUID `json:"listingId"` + ProviderID uuid.UUID `json:"providerId"` + PeriodID uuid.UUID `json:"periodId"` + Status Status `json:"status"` + StartsAt *time.Time `json:"startsAt"` + EndsAt *time.Time `json:"endsAt"` + CreatedAt time.Time `json:"createdAt"` + UpdatedAt time.Time `json:"updatedAt"` +} +type Access struct { + MaxActiveListings int `json:"maxActiveListings"` + MaxPhotosPerListing int `json:"maxPhotosPerListing"` + AnalyticsEnabled bool `json:"analyticsEnabled"` +} diff --git a/backend/internal/entitlements/service.go b/backend/internal/entitlements/service.go new file mode 100644 index 0000000..246f0c4 --- /dev/null +++ b/backend/internal/entitlements/service.go @@ -0,0 +1,112 @@ +package entitlements + +import ( + "context" + "errors" + "github.com/SourceSenseiTheRealOne/juntly/backend/internal/users" + "github.com/google/uuid" +) + +type IdentityReconciler interface { + Reconcile(context.Context, users.VerifiedIdentity) (users.InternalUser, bool, error) +} +type Store interface { + Catalog(context.Context) (Catalog, error) + RequestSubscription(context.Context, uuid.UUID, uuid.UUID) (Subscription, error) + CurrentSubscription(context.Context, uuid.UUID) (*Subscription, error) + RequestPromotion(context.Context, uuid.UUID, uuid.UUID, uuid.UUID) (Promotion, error) + ListPromotions(context.Context, uuid.UUID) ([]Promotion, error) + Access(context.Context, uuid.UUID) (Access, error) +} +type Service interface { + Catalog(context.Context) (Catalog, error) + RequestSubscription(context.Context, users.VerifiedIdentity, uuid.UUID) (Subscription, error) + CurrentSubscription(context.Context, users.VerifiedIdentity) (*Subscription, error) + RequestPromotion(context.Context, users.VerifiedIdentity, uuid.UUID, uuid.UUID) (Promotion, error) + ListPromotions(context.Context, users.VerifiedIdentity) ([]Promotion, error) + Access(context.Context, users.VerifiedIdentity) (Access, error) +} +type service struct { + identities IdentityReconciler + store Store +} + +func NewService(i IdentityReconciler, s Store) Service { return service{identities: i, store: s} } +func (s service) Catalog(ctx context.Context) (Catalog, error) { + if s.store == nil { + return Catalog{}, ErrUnavailable + } + v, err := s.store.Catalog(ctx) + return v, normalize(err) +} +func (s service) RequestSubscription(ctx context.Context, identity users.VerifiedIdentity, plan uuid.UUID) (Subscription, error) { + if plan == uuid.Nil { + return Subscription{}, ErrInvalid + } + actor, err := s.actor(ctx, identity) + if err != nil { + return Subscription{}, err + } + v, err := s.store.RequestSubscription(ctx, actor, plan) + return v, normalize(err) +} +func (s service) CurrentSubscription(ctx context.Context, identity users.VerifiedIdentity) (*Subscription, error) { + actor, err := s.actor(ctx, identity) + if err != nil { + return nil, err + } + v, err := s.store.CurrentSubscription(ctx, actor) + return v, normalize(err) +} +func (s service) RequestPromotion(ctx context.Context, identity users.VerifiedIdentity, listing, period uuid.UUID) (Promotion, error) { + if listing == uuid.Nil || period == uuid.Nil { + return Promotion{}, ErrInvalid + } + actor, err := s.actor(ctx, identity) + if err != nil { + return Promotion{}, err + } + v, err := s.store.RequestPromotion(ctx, actor, listing, period) + return v, normalize(err) +} +func (s service) ListPromotions(ctx context.Context, identity users.VerifiedIdentity) ([]Promotion, error) { + actor, err := s.actor(ctx, identity) + if err != nil { + return nil, err + } + v, err := s.store.ListPromotions(ctx, actor) + return v, normalize(err) +} +func (s service) Access(ctx context.Context, identity users.VerifiedIdentity) (Access, error) { + actor, err := s.actor(ctx, identity) + if err != nil { + return Access{}, err + } + v, err := s.store.Access(ctx, actor) + return v, normalize(err) +} +func (s service) actor(ctx context.Context, identity users.VerifiedIdentity) (uuid.UUID, error) { + if s.identities == nil || s.store == nil { + return uuid.Nil, ErrUnavailable + } + u, _, err := s.identities.Reconcile(ctx, identity) + if err != nil { + if errors.Is(err, users.ErrInvalidIdentity) { + return uuid.Nil, ErrUnauthorized + } + return uuid.Nil, ErrUnavailable + } + if u.ID == uuid.Nil { + return uuid.Nil, ErrUnauthorized + } + return u.ID, nil +} +func normalize(err error) error { + if err == nil { + return nil + } + if errors.Is(err, ErrInvalid) || errors.Is(err, ErrUnauthorized) || errors.Is(err, ErrForbidden) || errors.Is(err, ErrConflict) { + return err + } + return ErrUnavailable +} diff --git a/backend/internal/entitlements/service_test.go b/backend/internal/entitlements/service_test.go new file mode 100644 index 0000000..3a52246 --- /dev/null +++ b/backend/internal/entitlements/service_test.go @@ -0,0 +1,78 @@ +package entitlements + +import ( + "context" + "errors" + "github.com/SourceSenseiTheRealOne/juntly/backend/internal/users" + "github.com/google/uuid" + "testing" +) + +func TestServiceRequestsConfiguredSubscriptionForProvider(t *testing.T) { + t.Parallel() + actor := uuid.New() + plan := uuid.New() + store := &recordingStore{subscription: Subscription{ID: uuid.New(), ProviderID: actor, PlanID: plan, Status: StatusActive}} + service := NewService(staticIdentity{user: users.InternalUser{ID: actor}}, store) + value, err := service.RequestSubscription(context.Background(), users.VerifiedIdentity{Subject: "user_provider"}, plan) + if err != nil || value.PlanID != plan || store.actorID != actor { + t.Fatalf("RequestSubscription() = %#v/%v/%s", value, err, store.actorID) + } +} +func TestServiceRejectsMissingPromotionIdentifiers(t *testing.T) { + t.Parallel() + store := &recordingStore{} + service := NewService(staticIdentity{user: users.InternalUser{ID: uuid.New()}}, store) + _, err := service.RequestPromotion(context.Background(), users.VerifiedIdentity{Subject: "user_provider"}, uuid.Nil, uuid.New()) + if !errors.Is(err, ErrInvalid) { + t.Fatalf("error = %v", err) + } + if store.actorID != uuid.Nil { + t.Fatal("store called") + } +} +func TestServiceReadsServerOwnedEntitlements(t *testing.T) { + t.Parallel() + actor := uuid.New() + store := &recordingStore{access: Access{MaxActiveListings: 5, MaxPhotosPerListing: 10, AnalyticsEnabled: true}} + service := NewService(staticIdentity{user: users.InternalUser{ID: actor}}, store) + value, err := service.Access(context.Background(), users.VerifiedIdentity{Subject: "user_provider"}) + if err != nil || value.MaxActiveListings != 5 || store.actorID != actor { + t.Fatalf("Access() = %#v/%v/%s", value, err, store.actorID) + } +} + +type staticIdentity struct { + user users.InternalUser + err error +} + +func (s staticIdentity) Reconcile(context.Context, users.VerifiedIdentity) (users.InternalUser, bool, error) { + return s.user, false, s.err +} + +type recordingStore struct { + subscription Subscription + access Access + actorID uuid.UUID +} + +func (s *recordingStore) Catalog(context.Context) (Catalog, error) { return Catalog{}, nil } +func (s *recordingStore) RequestSubscription(_ context.Context, actor, plan uuid.UUID) (Subscription, error) { + s.actorID = actor + return s.subscription, nil +} +func (s *recordingStore) CurrentSubscription(context.Context, uuid.UUID) (*Subscription, error) { + return nil, nil +} +func (s *recordingStore) RequestPromotion(_ context.Context, actor, listing, period uuid.UUID) (Promotion, error) { + s.actorID = actor + return Promotion{}, nil +} +func (s *recordingStore) ListPromotions(context.Context, uuid.UUID) ([]Promotion, error) { + return nil, nil +} +func (s *recordingStore) Access(_ context.Context, actor uuid.UUID) (Access, error) { + s.actorID = actor + return s.access, nil +} diff --git a/backend/internal/entitlements/sql_store.go b/backend/internal/entitlements/sql_store.go new file mode 100644 index 0000000..08e38ce --- /dev/null +++ b/backend/internal/entitlements/sql_store.go @@ -0,0 +1,88 @@ +package entitlements + +import ( + "context" + "database/sql" + "errors" + "github.com/google/uuid" +) + +type sqlStore struct{ database *sql.DB } + +func NewSQLStore(db *sql.DB) Store { return sqlStore{database: db} } +func (s sqlStore) Catalog(ctx context.Context) (Catalog, error) { + plans, err := s.database.QueryContext(ctx, `select id,slug,name,price_minor,currency,billing_days,max_active_listings,max_photos_per_listing,analytics_enabled from public.professional_plans where active order by price_minor,id`) + if err != nil { + return Catalog{}, err + } + defer plans.Close() + c := Catalog{Plans: []Plan{}, PromotionPeriods: []PromotionPeriod{}} + for plans.Next() { + var v Plan + if err := plans.Scan(&v.ID, &v.Slug, &v.Name, &v.PriceMinor, &v.Currency, &v.BillingDays, &v.MaxActiveListings, &v.MaxPhotosPerListing, &v.AnalyticsEnabled); err != nil { + return Catalog{}, err + } + c.Plans = append(c.Plans, v) + } + if err = plans.Err(); err != nil { + return Catalog{}, err + } + periods, err := s.database.QueryContext(ctx, `select id,slug,name,duration_days,price_minor,currency from public.promotion_periods where active order by duration_days,id`) + if err != nil { + return Catalog{}, err + } + defer periods.Close() + for periods.Next() { + var v PromotionPeriod + if err := periods.Scan(&v.ID, &v.Slug, &v.Name, &v.DurationDays, &v.PriceMinor, &v.Currency); err != nil { + return Catalog{}, err + } + c.PromotionPeriods = append(c.PromotionPeriods, v) + } + return c, periods.Err() +} +func (s sqlStore) RequestSubscription(ctx context.Context, actor, plan uuid.UUID) (Subscription, error) { + var v Subscription + err := s.database.QueryRowContext(ctx, `insert into public.provider_subscriptions(provider_internal_user_id,plan_id,status,starts_at,ends_at) select $1,id,case when price_minor=0 then 'active' else 'pending' end,case when price_minor=0 then timezone('utc',now()) end,case when price_minor=0 then timezone('utc',now())+billing_days*interval '1 day' end from public.professional_plans where id=$2 and active on conflict do nothing returning id,provider_internal_user_id,plan_id,status,starts_at,ends_at,created_at,updated_at`, actor, plan).Scan(&v.ID, &v.ProviderID, &v.PlanID, &v.Status, &v.StartsAt, &v.EndsAt, &v.CreatedAt, &v.UpdatedAt) + if errors.Is(err, sql.ErrNoRows) { + return Subscription{}, ErrConflict + } + return v, err +} +func (s sqlStore) CurrentSubscription(ctx context.Context, actor uuid.UUID) (*Subscription, error) { + var v Subscription + err := s.database.QueryRowContext(ctx, `select id,provider_internal_user_id,plan_id,status,starts_at,ends_at,created_at,updated_at from public.provider_subscriptions where provider_internal_user_id=$1 and status in('pending','active') order by created_at desc limit 1`, actor).Scan(&v.ID, &v.ProviderID, &v.PlanID, &v.Status, &v.StartsAt, &v.EndsAt, &v.CreatedAt, &v.UpdatedAt) + if errors.Is(err, sql.ErrNoRows) { + return nil, nil + } + return &v, err +} +func (s sqlStore) RequestPromotion(ctx context.Context, actor, listing, period uuid.UUID) (Promotion, error) { + var v Promotion + err := s.database.QueryRowContext(ctx, `insert into public.listing_promotions(listing_id,provider_internal_user_id,period_id,status,starts_at,ends_at) select l.id,$1,p.id,case when p.price_minor=0 then 'active' else 'pending' end,case when p.price_minor=0 then timezone('utc',now()) end,case when p.price_minor=0 then timezone('utc',now())+p.duration_days*interval '1 day' end from public.listings l join public.promotion_periods p on p.id=$3 and p.active where l.id=$2 and l.internal_user_id=$1 and l.state='active' on conflict do nothing returning id,listing_id,provider_internal_user_id,period_id,status,starts_at,ends_at,created_at,updated_at`, actor, listing, period).Scan(&v.ID, &v.ListingID, &v.ProviderID, &v.PeriodID, &v.Status, &v.StartsAt, &v.EndsAt, &v.CreatedAt, &v.UpdatedAt) + if errors.Is(err, sql.ErrNoRows) { + return Promotion{}, ErrConflict + } + return v, err +} +func (s sqlStore) ListPromotions(ctx context.Context, actor uuid.UUID) ([]Promotion, error) { + rows, err := s.database.QueryContext(ctx, `select id,listing_id,provider_internal_user_id,period_id,status,starts_at,ends_at,created_at,updated_at from public.listing_promotions where provider_internal_user_id=$1 order by created_at desc,id`, actor) + if err != nil { + return nil, err + } + defer rows.Close() + values := []Promotion{} + for rows.Next() { + var v Promotion + if err := rows.Scan(&v.ID, &v.ListingID, &v.ProviderID, &v.PeriodID, &v.Status, &v.StartsAt, &v.EndsAt, &v.CreatedAt, &v.UpdatedAt); err != nil { + return nil, err + } + values = append(values, v) + } + return values, rows.Err() +} +func (s sqlStore) Access(ctx context.Context, actor uuid.UUID) (Access, error) { + var v Access + err := s.database.QueryRowContext(ctx, `select coalesce(p.max_active_listings,d.max_active_listings),coalesce(p.max_photos_per_listing,d.max_photos_per_listing),coalesce(p.analytics_enabled,d.analytics_enabled) from public.marketplace_entitlement_defaults d left join public.provider_subscriptions s on s.provider_internal_user_id=$1 and s.status='active' and s.ends_at>timezone('utc',now()) left join public.professional_plans p on p.id=s.plan_id where d.id=true`, actor).Scan(&v.MaxActiveListings, &v.MaxPhotosPerListing, &v.AnalyticsEnabled) + return v, err +} diff --git a/backend/internal/health/service.go b/backend/internal/health/service.go new file mode 100644 index 0000000..4a27f9f --- /dev/null +++ b/backend/internal/health/service.go @@ -0,0 +1,44 @@ +package health + +import "time" + +const ServiceName = "juntly-api" + +type Clock func() time.Time + +type Service struct { + version string + now Clock +} + +type Result struct { + Status string `json:"status"` + Service string `json:"service"` + Version string `json:"version"` + CheckedAt time.Time `json:"checkedAt"` + RequestID string `json:"requestId"` +} + +func NewService(version string, now Clock) Service { + if version == "" { + version = "dev" + } + if now == nil { + now = time.Now + } + + return Service{ + version: version, + now: now, + } +} + +func (s Service) Check(requestID string) Result { + return Result{ + Status: "ok", + Service: ServiceName, + Version: s.version, + CheckedAt: s.now().UTC(), + RequestID: requestID, + } +} diff --git a/backend/internal/health/service_test.go b/backend/internal/health/service_test.go new file mode 100644 index 0000000..d56f8ed --- /dev/null +++ b/backend/internal/health/service_test.go @@ -0,0 +1,33 @@ +package health_test + +import ( + "testing" + "time" + + "github.com/SourceSenseiTheRealOne/juntly/backend/internal/health" +) + +func TestServiceCheckReturnsVersionedHealth(t *testing.T) { + now := time.Date(2026, 8, 20, 9, 30, 0, 0, time.UTC) + service := health.NewService("0.1.0", func() time.Time { + return now + }) + + result := service.Check("req_test_123") + + if result.Status != "ok" { + t.Fatalf("Status = %q, want ok", result.Status) + } + if result.Service != "juntly-api" { + t.Fatalf("Service = %q, want juntly-api", result.Service) + } + if result.Version != "0.1.0" { + t.Fatalf("Version = %q, want 0.1.0", result.Version) + } + if !result.CheckedAt.Equal(now) { + t.Fatalf("CheckedAt = %s, want %s", result.CheckedAt, now) + } + if result.RequestID != "req_test_123" { + t.Fatalf("RequestID = %q, want req_test_123", result.RequestID) + } +} diff --git a/backend/internal/httpapi/account_handler.go b/backend/internal/httpapi/account_handler.go new file mode 100644 index 0000000..524990b --- /dev/null +++ b/backend/internal/httpapi/account_handler.go @@ -0,0 +1,105 @@ +package httpapi + +import ( + "context" + "encoding/json" + "errors" + "io" + "net/http" + "time" + + "github.com/SourceSenseiTheRealOne/juntly/backend/internal/accounts" + "github.com/SourceSenseiTheRealOne/juntly/backend/internal/authn" + "github.com/SourceSenseiTheRealOne/juntly/backend/internal/users" +) + +const maxAccountRequestBytes = 1024 + +type AccountService interface { + Get(context.Context, users.VerifiedIdentity) (accounts.Account, error) + SetProviderEnabled(context.Context, users.VerifiedIdentity, bool) (accounts.Account, error) +} + +type accountHandler struct { + service AccountService +} + +type accountCapabilitiesResponse struct { + CustomerEnabled bool `json:"customerEnabled"` + ProviderEnabled bool `json:"providerEnabled"` + OnboardingCompletedAt string `json:"onboardingCompletedAt"` +} + +type updateAccountCapabilitiesRequest struct { + ProviderEnabled *bool `json:"providerEnabled"` +} + +func NewAccountHandler(service AccountService) http.Handler { + return accountHandler{service: service} +} + +func (h accountHandler) ServeHTTP(w http.ResponseWriter, request *http.Request) { + if request.Method != http.MethodGet && request.Method != http.MethodPut { + w.Header().Set("Allow", "GET, PUT") + http.Error(w, http.StatusText(http.StatusMethodNotAllowed), http.StatusMethodNotAllowed) + return + } + + requestID := requestIDFromHeader(request.Header.Get(RequestIDHeader)) + identity, ok := authn.IdentityFromContext(request.Context()) + if !ok { + writeAPIError(w, http.StatusUnauthorized, "UNAUTHORIZED", "Unauthorized", requestID) + return + } + if h.service == nil { + writeAPIError(w, http.StatusServiceUnavailable, "SERVICE_UNAVAILABLE", "Service unavailable", requestID) + return + } + + var ( + account accounts.Account + err error + ) + switch request.Method { + case http.MethodGet: + account, err = h.service.Get(request.Context(), identity) + case http.MethodPut: + enabled, valid := decodeProviderEnabled(request.Body) + if !valid { + writeAPIError(w, http.StatusBadRequest, "INVALID_REQUEST", "Invalid request", requestID) + return + } + account, err = h.service.SetProviderEnabled(request.Context(), identity, enabled) + } + + if err != nil { + if errors.Is(err, accounts.ErrInvalidIdentity) { + writeAPIError(w, http.StatusUnauthorized, "UNAUTHORIZED", "Unauthorized", requestID) + return + } + writeAPIError(w, http.StatusServiceUnavailable, "SERVICE_UNAVAILABLE", "Service unavailable", requestID) + return + } + + writeJSON(w, http.StatusOK, accountCapabilitiesResponse{ + CustomerEnabled: account.CustomerEnabled, + ProviderEnabled: account.ProviderEnabled, + OnboardingCompletedAt: account.OnboardingCompletedAt.UTC().Format(time.RFC3339Nano), + }, requestID) +} + +func decodeProviderEnabled(body io.Reader) (bool, bool) { + decoder := json.NewDecoder(io.LimitReader(body, maxAccountRequestBytes+1)) + decoder.DisallowUnknownFields() + + var payload updateAccountCapabilitiesRequest + if err := decoder.Decode(&payload); err != nil || payload.ProviderEnabled == nil { + return false, false + } + + var extra any + if err := decoder.Decode(&extra); !errors.Is(err, io.EOF) { + return false, false + } + return *payload.ProviderEnabled, true +} diff --git a/backend/internal/httpapi/account_handler_test.go b/backend/internal/httpapi/account_handler_test.go new file mode 100644 index 0000000..c75fc52 --- /dev/null +++ b/backend/internal/httpapi/account_handler_test.go @@ -0,0 +1,232 @@ +package httpapi_test + +import ( + "context" + "encoding/json" + "errors" + "net/http" + "net/http/httptest" + "strings" + "testing" + "time" + + "github.com/SourceSenseiTheRealOne/juntly/backend/internal/accounts" + "github.com/SourceSenseiTheRealOne/juntly/backend/internal/authn" + "github.com/SourceSenseiTheRealOne/juntly/backend/internal/httpapi" + "github.com/SourceSenseiTheRealOne/juntly/backend/internal/users" +) + +func TestAccountHandlerReturnsUnauthorizedWithoutVerifiedIdentity(t *testing.T) { + t.Parallel() + + for _, method := range []string{http.MethodGet, http.MethodPut} { + method := method + t.Run(method, func(t *testing.T) { + t.Parallel() + service := &recordingAccountService{} + response := httptest.NewRecorder() + request := httptest.NewRequest(method, "/api/v1/me/account", strings.NewReader(`{"providerEnabled":true}`)) + request.Header.Set(httpapi.RequestIDHeader, "req_account_unauthorized") + + httpapi.NewAccountHandler(service).ServeHTTP(response, request) + + if response.Code != http.StatusUnauthorized { + t.Fatalf("status = %d, want %d", response.Code, http.StatusUnauthorized) + } + if service.calls != 0 { + t.Fatalf("service calls = %d, want 0", service.calls) + } + assertErrorResponse(t, response, "UNAUTHORIZED", "Unauthorized", "req_account_unauthorized") + }) + } +} + +func TestAccountHandlerGETReturnsClosedCapabilities(t *testing.T) { + t.Parallel() + + completedAt := time.Date(2026, 8, 23, 12, 5, 0, 123456000, time.UTC) + service := &recordingAccountService{account: accounts.Account{ + CustomerEnabled: true, + ProviderEnabled: false, + OnboardingCompletedAt: completedAt, + }} + handler := authn.RequireVerifiedIdentity( + staticVerifier{identity: users.VerifiedIdentity{Subject: "user_synthetic"}}, + httpapi.NewAccountHandler(service), + ) + response := httptest.NewRecorder() + request := httptest.NewRequest(http.MethodGet, "/api/v1/me/account", nil) + request.Header.Set("Authorization", "Bearer synthetic-token") + request.Header.Set(httpapi.RequestIDHeader, "req_account_get") + + handler.ServeHTTP(response, request) + + if response.Code != http.StatusOK { + t.Fatalf("status = %d, want %d", response.Code, http.StatusOK) + } + if response.Header().Get(httpapi.RequestIDHeader) != "req_account_get" { + t.Fatalf("request ID header = %q", response.Header().Get(httpapi.RequestIDHeader)) + } + body := decodeJSONMap(t, response) + want := map[string]any{ + "customerEnabled": true, + "providerEnabled": false, + "onboardingCompletedAt": "2026-08-23T12:05:00.123456Z", + } + if mustJSON(t, body) != mustJSON(t, want) { + t.Fatalf("body = %#v, want %#v", body, want) + } + serialized := mustJSON(t, body) + for _, prohibited := range []string{"user_synthetic", "synthetic-token", "internalUserId", "clerk"} { + if strings.Contains(serialized, prohibited) { + t.Fatalf("response leaks %q: %s", prohibited, serialized) + } + } +} + +func TestAccountHandlerPUTUpdatesProviderCapability(t *testing.T) { + t.Parallel() + + for _, enabled := range []bool{true, false} { + enabled := enabled + t.Run(map[bool]string{true: "enable", false: "disable"}[enabled], func(t *testing.T) { + t.Parallel() + service := &recordingAccountService{account: accounts.Account{ + CustomerEnabled: true, + ProviderEnabled: enabled, + OnboardingCompletedAt: time.Date(2026, 8, 23, 12, 5, 0, 0, time.UTC), + }} + handler := authn.RequireVerifiedIdentity( + staticVerifier{identity: users.VerifiedIdentity{Subject: "user_synthetic"}}, + httpapi.NewAccountHandler(service), + ) + response := httptest.NewRecorder() + request := httptest.NewRequest(http.MethodPut, "/api/v1/me/account", strings.NewReader(map[bool]string{true: `{"providerEnabled":true}`, false: `{"providerEnabled":false}`}[enabled])) + request.Header.Set("Authorization", "Bearer synthetic-token") + request.Header.Set(httpapi.RequestIDHeader, "req_account_put") + + handler.ServeHTTP(response, request) + + if response.Code != http.StatusOK { + t.Fatalf("status = %d, want %d", response.Code, http.StatusOK) + } + if service.calls != 1 || service.providerEnabled == nil || *service.providerEnabled != enabled { + t.Fatalf("service calls=%d providerEnabled=%v, want 1 %t", service.calls, service.providerEnabled, enabled) + } + if service.identity.Subject != "user_synthetic" { + t.Fatalf("service subject = %q", service.identity.Subject) + } + }) + } +} + +func TestAccountHandlerPUTRejectsNonExactJSONBeforeService(t *testing.T) { + t.Parallel() + + cases := map[string]string{ + "empty body": "", + "empty object": `{}`, + "null body": `null`, + "unknown property": `{"providerEnabled":true,"admin":true}`, + "non boolean": `{"providerEnabled":"true"}`, + "null capability": `{"providerEnabled":null}`, + "trailing bytes": `{"providerEnabled":true} trailing`, + "second value": `{"providerEnabled":true}{}`, + } + for name, body := range cases { + name, body := name, body + t.Run(name, func(t *testing.T) { + t.Parallel() + service := &recordingAccountService{} + handler := authn.RequireVerifiedIdentity( + staticVerifier{identity: users.VerifiedIdentity{Subject: "user_synthetic"}}, + httpapi.NewAccountHandler(service), + ) + response := httptest.NewRecorder() + request := httptest.NewRequest(http.MethodPut, "/api/v1/me/account", strings.NewReader(body)) + request.Header.Set("Authorization", "Bearer synthetic-token") + request.Header.Set(httpapi.RequestIDHeader, "req_account_invalid") + + handler.ServeHTTP(response, request) + + if response.Code != http.StatusBadRequest { + t.Fatalf("status = %d, want %d", response.Code, http.StatusBadRequest) + } + if service.calls != 0 { + t.Fatalf("service calls = %d, want 0", service.calls) + } + assertErrorResponse(t, response, "INVALID_REQUEST", "Invalid request", "req_account_invalid") + }) + } +} + +func TestAccountHandlerReturnsSafeUnavailable(t *testing.T) { + t.Parallel() + + service := &recordingAccountService{err: errors.New("database at internal-host failed for user_synthetic")} + handler := authn.RequireVerifiedIdentity( + staticVerifier{identity: users.VerifiedIdentity{Subject: "user_synthetic"}}, + httpapi.NewAccountHandler(service), + ) + response := httptest.NewRecorder() + request := httptest.NewRequest(http.MethodGet, "/api/v1/me/account", nil) + request.Header.Set("Authorization", "Bearer synthetic-token") + request.Header.Set(httpapi.RequestIDHeader, "req_account_unavailable") + + handler.ServeHTTP(response, request) + + if response.Code != http.StatusServiceUnavailable { + t.Fatalf("status = %d, want %d", response.Code, http.StatusServiceUnavailable) + } + assertErrorResponse(t, response, "SERVICE_UNAVAILABLE", "Service unavailable", "req_account_unavailable") +} + +func TestAccountHandlerRejectsOtherMethods(t *testing.T) { + t.Parallel() + + service := &recordingAccountService{} + response := httptest.NewRecorder() + request := httptest.NewRequest(http.MethodPost, "/api/v1/me/account", nil) + + httpapi.NewAccountHandler(service).ServeHTTP(response, request) + + if response.Code != http.StatusMethodNotAllowed { + t.Fatalf("status = %d, want %d", response.Code, http.StatusMethodNotAllowed) + } + if allow := response.Header().Get("Allow"); allow != "GET, PUT" { + t.Fatalf("Allow = %q, want %q", allow, "GET, PUT") + } + if service.calls != 0 { + t.Fatalf("service calls = %d, want 0", service.calls) + } +} + +type recordingAccountService struct { + account accounts.Account + err error + calls int + identity users.VerifiedIdentity + providerEnabled *bool +} + +func (s *recordingAccountService) Get(_ context.Context, identity users.VerifiedIdentity) (accounts.Account, error) { + s.calls++ + s.identity = identity + return s.account, s.err +} + +func (s *recordingAccountService) SetProviderEnabled(_ context.Context, identity users.VerifiedIdentity, enabled bool) (accounts.Account, error) { + s.calls++ + s.identity = identity + s.providerEnabled = &enabled + return s.account, s.err +} + +func decodeJSONMap(t *testing.T, response *httptest.ResponseRecorder) map[string]any { + t.Helper() + var body map[string]any + if err := json.NewDecoder(response.Body).Decode(&body); err != nil { + t.Fatalf("decode response: %v", err) + } + return body +} diff --git a/backend/internal/httpapi/administration_handler.go b/backend/internal/httpapi/administration_handler.go new file mode 100644 index 0000000..e259bac --- /dev/null +++ b/backend/internal/httpapi/administration_handler.go @@ -0,0 +1,103 @@ +package httpapi + +import ( + "context" + "encoding/json" + "errors" + "io" + "net/http" + + "github.com/SourceSenseiTheRealOne/juntly/backend/internal/administration" + "github.com/SourceSenseiTheRealOne/juntly/backend/internal/authn" + "github.com/SourceSenseiTheRealOne/juntly/backend/internal/users" + "github.com/google/uuid" +) + +type AdministrationService interface { + Metrics(context.Context, users.VerifiedIdentity) (administration.Metrics, error) + Queue(context.Context, users.VerifiedIdentity) (administration.Queue, error) + Moderate(context.Context, users.VerifiedIdentity, administration.ModerationAction) error +} +type administrationHandler struct{ service AdministrationService } + +func NewAdministrationHandler(s AdministrationService) http.Handler { + return administrationHandler{service: s} +} + +type moderationRequest struct { + Kind *string `json:"kind"` + TargetID *uuid.UUID `json:"targetId"` + Reason *string `json:"reason"` +} + +func (h administrationHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { + id := requestIDFromHeader(r.Header.Get(RequestIDHeader)) + if h.service == nil { + writeAPIError(w, 503, "SERVICE_UNAVAILABLE", "Service unavailable", id) + return + } + identity, ok := authn.IdentityFromContext(r.Context()) + if !ok { + writeAPIError(w, 401, "UNAUTHORIZED", "Unauthorized", id) + return + } + switch r.URL.Path { + case "/api/v1/admin/dashboard": + if r.Method != http.MethodGet { + http.Error(w, http.StatusText(405), 405) + return + } + metrics, err := h.service.Metrics(r.Context(), identity) + if err != nil { + writeAdministrationError(w, err, id) + return + } + queue, err := h.service.Queue(r.Context(), identity) + if err != nil { + writeAdministrationError(w, err, id) + return + } + writeJSON(w, 200, map[string]any{"metrics": metrics, "queue": queue}, id) + case "/api/v1/admin/moderation": + if r.Method != http.MethodPost { + http.Error(w, http.StatusText(405), 405) + return + } + var body moderationRequest + if !decodeAdministration(r.Body, &body) || body.Kind == nil || body.TargetID == nil || body.Reason == nil { + writeAPIError(w, 400, "INVALID_REQUEST", "Invalid request", id) + return + } + if err := h.service.Moderate(r.Context(), identity, administration.ModerationAction{Kind: *body.Kind, TargetID: *body.TargetID, Reason: *body.Reason}); err != nil { + writeAdministrationError(w, err, id) + return + } + w.Header().Set(RequestIDHeader, id) + w.WriteHeader(204) + default: + writeAPIError(w, 404, "NOT_FOUND", "Not found", id) + } +} +func decodeAdministration(r io.Reader, target any) bool { + d := json.NewDecoder(io.LimitReader(r, 4097)) + d.DisallowUnknownFields() + if d.Decode(target) != nil { + return false + } + var extra any + return errors.Is(d.Decode(&extra), io.EOF) +} +func writeAdministrationError(w http.ResponseWriter, err error, id string) { + switch { + case errors.Is(err, administration.ErrInvalid): + writeAPIError(w, 400, "INVALID_REQUEST", "Invalid request", id) + case errors.Is(err, administration.ErrUnauthorized): + writeAPIError(w, 401, "UNAUTHORIZED", "Unauthorized", id) + case errors.Is(err, administration.ErrForbidden): + writeAPIError(w, 403, "FORBIDDEN", "Forbidden", id) + case errors.Is(err, administration.ErrNotFound): + writeAPIError(w, 404, "NOT_FOUND", "Not found", id) + default: + writeAPIError(w, 503, "SERVICE_UNAVAILABLE", "Service unavailable", id) + } +} diff --git a/backend/internal/httpapi/booking_handler.go b/backend/internal/httpapi/booking_handler.go new file mode 100644 index 0000000..4f09a72 --- /dev/null +++ b/backend/internal/httpapi/booking_handler.go @@ -0,0 +1,169 @@ +package httpapi + +import ( + "context" + "encoding/json" + "errors" + "io" + "net/http" + "strings" + "time" + + "github.com/SourceSenseiTheRealOne/juntly/backend/internal/authn" + "github.com/SourceSenseiTheRealOne/juntly/backend/internal/bookings" + "github.com/SourceSenseiTheRealOne/juntly/backend/internal/users" + "github.com/google/uuid" +) + +const maxBookingRequestBytes = 8 * 1024 + +type BookingService interface { + Create(context.Context, users.VerifiedIdentity, bookings.CreateBooking) (bookings.Booking, error) + List(context.Context, users.VerifiedIdentity) ([]bookings.Booking, error) + Get(context.Context, users.VerifiedIdentity, uuid.UUID) (bookings.Booking, error) + Transition(context.Context, users.VerifiedIdentity, uuid.UUID, bookings.Transition) (bookings.Booking, error) +} +type bookingHandler struct{ service BookingService } + +func NewBookingHandler(service BookingService) http.Handler { return bookingHandler{service: service} } + +type createBookingRequest struct { + SourceType *bookings.SourceType `json:"sourceType"` + SourceID *uuid.UUID `json:"sourceId"` + ProviderID *uuid.UUID `json:"providerId"` + IdempotencyKey *string `json:"idempotencyKey"` + ScheduledAt *time.Time `json:"scheduledAt"` + PrivateLocation *string `json:"privateLocation"` + AgreedPriceMinor *int `json:"agreedPriceMinor"` +} +type transitionBookingRequest struct { + ExpectedState *bookings.State `json:"expectedState"` + TargetState *bookings.State `json:"targetState"` + Revision *int `json:"revision"` + Reason *string `json:"reason"` +} +type bookingResponse struct { + ID uuid.UUID `json:"id"` + CustomerID uuid.UUID `json:"customerId"` + ProviderID uuid.UUID `json:"providerId"` + SourceType bookings.SourceType `json:"sourceType"` + SourceID *uuid.UUID `json:"sourceId"` + State bookings.State `json:"state"` + Revision int `json:"revision"` + ScheduledAt string `json:"scheduledAt"` + PrivateLocation string `json:"privateLocation"` + AgreedPriceMinor int `json:"agreedPriceMinor"` + Currency string `json:"currency"` + CreatedAt string `json:"createdAt"` + UpdatedAt string `json:"updatedAt"` +} + +func (h bookingHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { + requestID := requestIDFromHeader(r.Header.Get(RequestIDHeader)) + identity, ok := authn.IdentityFromContext(r.Context()) + if !ok { + writeAPIError(w, 401, "UNAUTHORIZED", "Unauthorized", requestID) + return + } + if h.service == nil { + writeAPIError(w, 503, "SERVICE_UNAVAILABLE", "Service unavailable", requestID) + return + } + base := "/api/v1/me/bookings" + path := strings.TrimSuffix(r.URL.Path, "/") + if path == base { + switch r.Method { + case http.MethodGet: + values, err := h.service.List(r.Context(), identity) + if err != nil { + writeBookingError(w, err, requestID) + return + } + writeJSON(w, 200, map[string]any{"bookings": bookingResponses(values)}, requestID) + case http.MethodPost: + var body createBookingRequest + if !decodeBooking(r.Body, &body) || body.SourceType == nil || body.IdempotencyKey == nil || body.ScheduledAt == nil || body.PrivateLocation == nil { + writeAPIError(w, 400, "INVALID_REQUEST", "Invalid request", requestID) + return + } + value, err := h.service.Create(r.Context(), identity, bookings.CreateBooking{SourceType: *body.SourceType, SourceID: body.SourceID, ProviderID: body.ProviderID, IdempotencyKey: *body.IdempotencyKey, ScheduledAt: *body.ScheduledAt, PrivateLocation: *body.PrivateLocation, AgreedPriceMinor: body.AgreedPriceMinor}) + if err != nil { + writeBookingError(w, err, requestID) + return + } + writeJSON(w, 201, bookingJSON(value), requestID) + default: + http.Error(w, http.StatusText(405), 405) + } + return + } + parts := strings.Split(strings.TrimPrefix(path, base+"/"), "/") + if len(parts) < 1 { + writeAPIError(w, 404, "NOT_FOUND", "Not found", requestID) + return + } + id, err := uuid.Parse(parts[0]) + if err != nil { + writeAPIError(w, 400, "INVALID_REQUEST", "Invalid request", requestID) + return + } + if len(parts) == 1 && r.Method == http.MethodGet { + value, err := h.service.Get(r.Context(), identity, id) + if err != nil { + writeBookingError(w, err, requestID) + return + } + writeJSON(w, 200, bookingJSON(value), requestID) + return + } + if len(parts) == 2 && parts[1] == "transitions" && r.Method == http.MethodPost { + var body transitionBookingRequest + if !decodeBooking(r.Body, &body) || body.ExpectedState == nil || body.TargetState == nil || body.Revision == nil { + writeAPIError(w, 400, "INVALID_REQUEST", "Invalid request", requestID) + return + } + value, err := h.service.Transition(r.Context(), identity, id, bookings.Transition{ExpectedState: *body.ExpectedState, TargetState: *body.TargetState, Revision: *body.Revision, Reason: body.Reason}) + if err != nil { + writeBookingError(w, err, requestID) + return + } + writeJSON(w, 200, bookingJSON(value), requestID) + return + } + writeAPIError(w, 404, "NOT_FOUND", "Not found", requestID) +} +func decodeBooking(body io.Reader, target any) bool { + d := json.NewDecoder(io.LimitReader(body, maxBookingRequestBytes+1)) + d.DisallowUnknownFields() + if d.Decode(target) != nil { + return false + } + var extra any + return errors.Is(d.Decode(&extra), io.EOF) +} +func bookingJSON(v bookings.Booking) bookingResponse { + return bookingResponse{ID: v.ID, CustomerID: v.CustomerID, ProviderID: v.ProviderID, SourceType: v.SourceType, SourceID: v.SourceID, State: v.State, Revision: v.Revision, ScheduledAt: v.ScheduledAt.UTC().Format(time.RFC3339Nano), PrivateLocation: v.PrivateLocation, AgreedPriceMinor: v.AgreedPriceMinor, Currency: v.Currency, CreatedAt: v.CreatedAt.UTC().Format(time.RFC3339Nano), UpdatedAt: v.UpdatedAt.UTC().Format(time.RFC3339Nano)} +} +func bookingResponses(values []bookings.Booking) []bookingResponse { + out := make([]bookingResponse, 0, len(values)) + for _, v := range values { + out = append(out, bookingJSON(v)) + } + return out +} +func writeBookingError(w http.ResponseWriter, err error, id string) { + switch { + case errors.Is(err, bookings.ErrInvalid): + writeAPIError(w, 400, "INVALID_REQUEST", "Invalid request", id) + case errors.Is(err, bookings.ErrUnauthorized): + writeAPIError(w, 401, "UNAUTHORIZED", "Unauthorized", id) + case errors.Is(err, bookings.ErrForbidden): + writeAPIError(w, 403, "FORBIDDEN", "Forbidden", id) + case errors.Is(err, bookings.ErrNotFound): + writeAPIError(w, 404, "NOT_FOUND", "Not found", id) + case errors.Is(err, bookings.ErrConflict): + writeAPIError(w, 409, "CONFLICT", "Conflict", id) + default: + writeAPIError(w, 503, "SERVICE_UNAVAILABLE", "Service unavailable", id) + } +} diff --git a/backend/internal/httpapi/contact_channel_handler.go b/backend/internal/httpapi/contact_channel_handler.go new file mode 100644 index 0000000..47d36fa --- /dev/null +++ b/backend/internal/httpapi/contact_channel_handler.go @@ -0,0 +1,114 @@ +package httpapi + +import ( + "context" + "encoding/json" + "errors" + "io" + "net/http" + + "github.com/SourceSenseiTheRealOne/juntly/backend/internal/authn" + "github.com/SourceSenseiTheRealOne/juntly/backend/internal/contactreveal" + "github.com/SourceSenseiTheRealOne/juntly/backend/internal/users" +) + +const maxContactChannelRequestBytes = 2048 + +type ContactChannelService interface { + Get(context.Context, users.VerifiedIdentity) ([]contactreveal.ChannelStatus, error) + Put(context.Context, users.VerifiedIdentity, contactreveal.ReplaceChannel) (contactreveal.ChannelStatus, error) +} + +type contactChannelHandler struct{ service ContactChannelService } + +type channelStatusResponse struct { + Channel contactreveal.Channel `json:"channel"` + Configured bool `json:"configured"` + Enabled bool `json:"enabled"` + RevealConsent bool `json:"revealConsent"` +} + +type contactChannelsResponse struct { + Channels []channelStatusResponse `json:"channels"` +} +type replaceContactChannelRequest struct { + Channel *contactreveal.Channel `json:"channel"` + Contact *string `json:"contact"` + Enabled *bool `json:"enabled"` + RevealConsent *bool `json:"revealConsent"` +} + +func NewContactChannelHandler(service ContactChannelService) http.Handler { + return contactChannelHandler{service: service} +} + +func (h contactChannelHandler) ServeHTTP(w http.ResponseWriter, request *http.Request) { + if request.Method != http.MethodGet && request.Method != http.MethodPut { + w.Header().Set("Allow", "GET, PUT") + http.Error(w, http.StatusText(http.StatusMethodNotAllowed), http.StatusMethodNotAllowed) + return + } + id := requestIDFromHeader(request.Header.Get(RequestIDHeader)) + identity, ok := authn.IdentityFromContext(request.Context()) + if !ok { + writeAPIError(w, http.StatusUnauthorized, "UNAUTHORIZED", "Unauthorized", id) + return + } + if h.service == nil { + writeAPIError(w, http.StatusServiceUnavailable, "SERVICE_UNAVAILABLE", "Service unavailable", id) + return + } + if request.Method == http.MethodGet { + values, err := h.service.Get(request.Context(), identity) + if err != nil { + writeContactRevealError(w, err, id) + return + } + out := make([]channelStatusResponse, len(values)) + for index, value := range values { + out[index] = statusResponse(value) + } + writeJSON(w, http.StatusOK, contactChannelsResponse{Channels: out}, id) + return + } + input, valid := decodeContactChannel(request.Body) + if !valid { + writeAPIError(w, http.StatusBadRequest, "INVALID_REQUEST", "Invalid request", id) + return + } + value, err := h.service.Put(request.Context(), identity, input) + if err != nil { + writeContactRevealError(w, err, id) + return + } + writeJSON(w, http.StatusOK, statusResponse(value), id) +} + +func decodeContactChannel(body io.Reader) (contactreveal.ReplaceChannel, bool) { + decoder := json.NewDecoder(io.LimitReader(body, maxContactChannelRequestBytes+1)) + decoder.DisallowUnknownFields() + var value replaceContactChannelRequest + if err := decoder.Decode(&value); err != nil || value.Channel == nil || value.Contact == nil || value.Enabled == nil || value.RevealConsent == nil { + return contactreveal.ReplaceChannel{}, false + } + var extra any + if err := decoder.Decode(&extra); !errors.Is(err, io.EOF) { + return contactreveal.ReplaceChannel{}, false + } + return contactreveal.ReplaceChannel{Channel: *value.Channel, Contact: *value.Contact, Enabled: *value.Enabled, RevealConsent: *value.RevealConsent}, true +} + +func statusResponse(value contactreveal.ChannelStatus) channelStatusResponse { + return channelStatusResponse{Channel: value.Channel, Configured: value.Configured, Enabled: value.Enabled, RevealConsent: value.RevealConsent} +} + +func writeContactRevealError(w http.ResponseWriter, err error, id string) { + switch { + case errors.Is(err, contactreveal.ErrUnauthorized): + writeAPIError(w, http.StatusUnauthorized, "UNAUTHORIZED", "Unauthorized", id) + case errors.Is(err, contactreveal.ErrForbidden): + writeAPIError(w, http.StatusForbidden, "FORBIDDEN", "Forbidden", id) + default: + writeAPIError(w, http.StatusServiceUnavailable, "SERVICE_UNAVAILABLE", "Service unavailable", id) + } +} diff --git a/backend/internal/httpapi/contact_channel_handler_test.go b/backend/internal/httpapi/contact_channel_handler_test.go new file mode 100644 index 0000000..e847fa7 --- /dev/null +++ b/backend/internal/httpapi/contact_channel_handler_test.go @@ -0,0 +1,68 @@ +package httpapi_test + +import ( + "context" + "encoding/json" + "net/http" + "net/http/httptest" + "strings" + "testing" + + "github.com/SourceSenseiTheRealOne/juntly/backend/internal/authn" + "github.com/SourceSenseiTheRealOne/juntly/backend/internal/contactreveal" + "github.com/SourceSenseiTheRealOne/juntly/backend/internal/httpapi" + "github.com/SourceSenseiTheRealOne/juntly/backend/internal/users" +) + +func TestContactChannelHandlerReturnsStatusOnlyAndStrictlyConfiguresOneChannel(t *testing.T) { + t.Parallel() + service := &recordingContactChannelService{statuses: []contactreveal.ChannelStatus{{Channel: contactreveal.ChannelPhone, Configured: true, Enabled: true, RevealConsent: true}}, status: contactreveal.ChannelStatus{Channel: contactreveal.ChannelWhatsApp, Configured: true, Enabled: true, RevealConsent: true}} + handler := authn.RequireVerifiedIdentity(staticVerifier{identity: users.VerifiedIdentity{Subject: "provider"}}, httpapi.NewContactChannelHandler(service)) + getResponse := httptest.NewRecorder() + getRequest := httptest.NewRequest(http.MethodGet, "/api/v1/me/contact-channels", nil) + getRequest.Header.Set("Authorization", "Bearer synthetic-token") + getRequest.Header.Set(httpapi.RequestIDHeader, "req_contact_status") + handler.ServeHTTP(getResponse, getRequest) + if getResponse.Code != http.StatusOK || service.getCalls != 1 { + t.Fatalf("get status/calls = %d/%d", getResponse.Code, service.getCalls) + } + serialized := getResponse.Body.String() + for _, prohibited := range []string{"ciphertext", "nonce", "keyVersion", "contact", "synthetic-token"} { + if strings.Contains(strings.ToLower(serialized), strings.ToLower(prohibited)) { + t.Fatalf("status response leaks %q: %s", prohibited, serialized) + } + } + putResponse := httptest.NewRecorder() + putRequest := httptest.NewRequest(http.MethodPut, "/api/v1/me/contact-channels", strings.NewReader(`{"channel":"whatsapp","contact":"+12025550123","enabled":true,"revealConsent":true}`)) + putRequest.Header.Set("Authorization", "Bearer synthetic-token") + putRequest.Header.Set(httpapi.RequestIDHeader, "req_contact_put") + handler.ServeHTTP(putResponse, putRequest) + if putResponse.Code != http.StatusOK || service.putCalls != 1 || service.input.Contact == "" { + t.Fatalf("put status/calls/input = %d/%d/%#v", putResponse.Code, service.putCalls, service.input) + } + var body map[string]any + if err := json.NewDecoder(putResponse.Body).Decode(&body); err != nil { + t.Fatalf("decode: %v", err) + } + if _, exists := body["contact"]; exists { + t.Fatalf("put response leaks contact: %#v", body) + } +} + +type recordingContactChannelService struct { + statuses []contactreveal.ChannelStatus + status contactreveal.ChannelStatus + input contactreveal.ReplaceChannel + getCalls int + putCalls int +} + +func (s *recordingContactChannelService) Get(context.Context, users.VerifiedIdentity) ([]contactreveal.ChannelStatus, error) { + s.getCalls++ + return s.statuses, nil +} +func (s *recordingContactChannelService) Put(_ context.Context, _ users.VerifiedIdentity, input contactreveal.ReplaceChannel) (contactreveal.ChannelStatus, error) { + s.putCalls++ + s.input = input + return s.status, nil +} diff --git a/backend/internal/httpapi/contact_reveal_handler.go b/backend/internal/httpapi/contact_reveal_handler.go new file mode 100644 index 0000000..7a6212c --- /dev/null +++ b/backend/internal/httpapi/contact_reveal_handler.go @@ -0,0 +1,87 @@ +package httpapi + +import ( + "context" + "encoding/json" + "errors" + "io" + "net/http" + "strings" + + "github.com/SourceSenseiTheRealOne/juntly/backend/internal/authn" + "github.com/SourceSenseiTheRealOne/juntly/backend/internal/contactreveal" + "github.com/SourceSenseiTheRealOne/juntly/backend/internal/users" + "github.com/google/uuid" +) + +const maxContactRevealRequestBytes = 1024 + +type ContactRevealService interface { + Reveal(context.Context, users.VerifiedIdentity, uuid.UUID, contactreveal.Channel) (contactreveal.RevealedContact, error) +} + +type contactRevealHandler struct{ service ContactRevealService } +type contactRevealRequest struct { + Channel *contactreveal.Channel `json:"channel"` +} +type contactRevealResponse struct { + Channel contactreveal.Channel `json:"channel"` + Contact string `json:"contact"` +} + +func NewContactRevealHandler(service ContactRevealService) http.Handler { + return contactRevealHandler{service: service} +} + +func (h contactRevealHandler) ServeHTTP(w http.ResponseWriter, request *http.Request) { + if request.Method != http.MethodPost { + w.Header().Set("Allow", http.MethodPost) + http.Error(w, http.StatusText(http.StatusMethodNotAllowed), http.StatusMethodNotAllowed) + return + } + id := requestIDFromHeader(request.Header.Get(RequestIDHeader)) + identity, ok := authn.IdentityFromContext(request.Context()) + if !ok { + writeAPIError(w, http.StatusUnauthorized, "UNAUTHORIZED", "Unauthorized", id) + return + } + if h.service == nil { + writeAPIError(w, http.StatusServiceUnavailable, "SERVICE_UNAVAILABLE", "Service unavailable", id) + return + } + listingID, valid := revealListingID(request.URL.Path) + channel, validBody := decodeRevealChannel(request.Body) + if !valid || !validBody { + writeAPIError(w, http.StatusBadRequest, "INVALID_REQUEST", "Invalid request", id) + return + } + value, err := h.service.Reveal(request.Context(), identity, listingID, channel) + if err != nil { + writeContactRevealError(w, err, id) + return + } + writeJSON(w, http.StatusOK, contactRevealResponse{Channel: value.Channel, Contact: value.Value}, id) +} + +func revealListingID(path string) (uuid.UUID, bool) { + parts := strings.Split(strings.TrimPrefix(path, "/api/v1/listings/"), "/") + if len(parts) != 2 || parts[1] != "contact-reveals" { + return uuid.Nil, false + } + id, err := uuid.Parse(parts[0]) + return id, err == nil +} + +func decodeRevealChannel(body io.Reader) (contactreveal.Channel, bool) { + decoder := json.NewDecoder(io.LimitReader(body, maxContactRevealRequestBytes+1)) + decoder.DisallowUnknownFields() + var value contactRevealRequest + if err := decoder.Decode(&value); err != nil || value.Channel == nil || (*value.Channel != contactreveal.ChannelPhone && *value.Channel != contactreveal.ChannelWhatsApp) { + return "", false + } + var extra any + if err := decoder.Decode(&extra); !errors.Is(err, io.EOF) { + return "", false + } + return *value.Channel, true +} diff --git a/backend/internal/httpapi/contact_reveal_handler_test.go b/backend/internal/httpapi/contact_reveal_handler_test.go new file mode 100644 index 0000000..2b8e944 --- /dev/null +++ b/backend/internal/httpapi/contact_reveal_handler_test.go @@ -0,0 +1,77 @@ +package httpapi_test + +import ( + "context" + "errors" + "net/http" + "net/http/httptest" + "strings" + "testing" + + "github.com/SourceSenseiTheRealOne/juntly/backend/internal/authn" + "github.com/SourceSenseiTheRealOne/juntly/backend/internal/contactreveal" + "github.com/SourceSenseiTheRealOne/juntly/backend/internal/httpapi" + "github.com/SourceSenseiTheRealOne/juntly/backend/internal/users" + "github.com/google/uuid" +) + +func TestContactRevealHandlerStrictlyRevealsOnlyToVerifiedCustomer(t *testing.T) { + t.Parallel() + listingID := uuid.MustParse("aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa") + service := &recordingContactRevealService{result: contactreveal.RevealedContact{Channel: contactreveal.ChannelPhone, Value: "revealed-contact"}} + handler := authn.RequireVerifiedIdentity(staticVerifier{identity: users.VerifiedIdentity{Subject: "customer"}}, httpapi.NewContactRevealHandler(service)) + response := httptest.NewRecorder() + request := httptest.NewRequest(http.MethodPost, "/api/v1/listings/aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa/contact-reveals", strings.NewReader(`{"channel":"phone"}`)) + request.Header.Set("Authorization", "Bearer synthetic-token") + request.Header.Set(httpapi.RequestIDHeader, "req_contact_reveal") + handler.ServeHTTP(response, request) + if response.Code != http.StatusOK || service.calls != 1 || service.listingID != listingID || service.channel != contactreveal.ChannelPhone { + t.Fatalf("status/service = %d/%#v", response.Code, service) + } + if !strings.Contains(response.Body.String(), "revealed-contact") || strings.Contains(response.Body.String(), "internalUserId") { + t.Fatalf("unexpected reveal response: %s", response.Body.String()) + } + for _, body := range []string{`{}`, `{"channel":"phone","extra":true}`, `{"channel":"email"}`} { + service := &recordingContactRevealService{} + handler := authn.RequireVerifiedIdentity(staticVerifier{identity: users.VerifiedIdentity{Subject: "customer"}}, httpapi.NewContactRevealHandler(service)) + response := httptest.NewRecorder() + request := httptest.NewRequest(http.MethodPost, "/api/v1/listings/aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa/contact-reveals", strings.NewReader(body)) + request.Header.Set("Authorization", "Bearer synthetic-token") + handler.ServeHTTP(response, request) + if response.Code != http.StatusBadRequest || service.calls != 0 { + t.Fatalf("body/status/calls = %s/%d/%d", body, response.Code, service.calls) + } + } +} + +func TestContactRevealHandlerReturnsGenericForbiddenWithoutContact(t *testing.T) { + t.Parallel() + service := &recordingContactRevealService{err: contactreveal.ErrForbidden} + handler := authn.RequireVerifiedIdentity(staticVerifier{identity: users.VerifiedIdentity{Subject: "customer"}}, httpapi.NewContactRevealHandler(service)) + response := httptest.NewRecorder() + request := httptest.NewRequest(http.MethodPost, "/api/v1/listings/aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa/contact-reveals", strings.NewReader(`{"channel":"phone"}`)) + request.Header.Set("Authorization", "Bearer synthetic-token") + request.Header.Set(httpapi.RequestIDHeader, "req_contact_forbidden") + handler.ServeHTTP(response, request) + if response.Code != http.StatusForbidden { + t.Fatalf("status = %d", response.Code) + } + assertErrorResponse(t, response, "FORBIDDEN", "Forbidden", "req_contact_forbidden") +} + +type recordingContactRevealService struct { + calls int + listingID uuid.UUID + channel contactreveal.Channel + result contactreveal.RevealedContact + err error +} + +func (s *recordingContactRevealService) Reveal(_ context.Context, _ users.VerifiedIdentity, listingID uuid.UUID, channel contactreveal.Channel) (contactreveal.RevealedContact, error) { + s.calls++ + s.listingID = listingID + s.channel = channel + return s.result, s.err +} + +var _ = errors.Is diff --git a/backend/internal/httpapi/entitlement_handler.go b/backend/internal/httpapi/entitlement_handler.go new file mode 100644 index 0000000..fa7dee8 --- /dev/null +++ b/backend/internal/httpapi/entitlement_handler.go @@ -0,0 +1,139 @@ +package httpapi + +import ( + "context" + "encoding/json" + "errors" + "github.com/SourceSenseiTheRealOne/juntly/backend/internal/authn" + "github.com/SourceSenseiTheRealOne/juntly/backend/internal/entitlements" + "github.com/SourceSenseiTheRealOne/juntly/backend/internal/users" + "github.com/google/uuid" + "io" + "net/http" +) + +type EntitlementService interface { + Catalog(context.Context) (entitlements.Catalog, error) + RequestSubscription(context.Context, users.VerifiedIdentity, uuid.UUID) (entitlements.Subscription, error) + CurrentSubscription(context.Context, users.VerifiedIdentity) (*entitlements.Subscription, error) + RequestPromotion(context.Context, users.VerifiedIdentity, uuid.UUID, uuid.UUID) (entitlements.Promotion, error) + ListPromotions(context.Context, users.VerifiedIdentity) ([]entitlements.Promotion, error) + Access(context.Context, users.VerifiedIdentity) (entitlements.Access, error) +} +type entitlementHandler struct{ service EntitlementService } + +func NewEntitlementHandler(s EntitlementService) http.Handler { return entitlementHandler{service: s} } + +type planRequest struct { + PlanID *uuid.UUID `json:"planId"` +} +type promotionRequest struct { + ListingID *uuid.UUID `json:"listingId"` + PeriodID *uuid.UUID `json:"periodId"` +} + +func (h entitlementHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { + id := requestIDFromHeader(r.Header.Get(RequestIDHeader)) + if h.service == nil { + writeAPIError(w, 503, "SERVICE_UNAVAILABLE", "Service unavailable", id) + return + } + if r.URL.Path == "/api/v1/entitlements/catalog" { + if r.Method != http.MethodGet { + http.Error(w, http.StatusText(405), 405) + return + } + v, err := h.service.Catalog(r.Context()) + if err != nil { + writeEntitlementError(w, err, id) + return + } + writeJSON(w, 200, v, id) + return + } + identity, ok := authn.IdentityFromContext(r.Context()) + if !ok { + writeAPIError(w, 401, "UNAUTHORIZED", "Unauthorized", id) + return + } + switch r.URL.Path { + case "/api/v1/me/entitlements": + if r.Method != http.MethodGet { + http.Error(w, http.StatusText(405), 405) + return + } + access, err := h.service.Access(r.Context(), identity) + if err != nil { + writeEntitlementError(w, err, id) + return + } + subscription, err := h.service.CurrentSubscription(r.Context(), identity) + if err != nil { + writeEntitlementError(w, err, id) + return + } + promotions, err := h.service.ListPromotions(r.Context(), identity) + if err != nil { + writeEntitlementError(w, err, id) + return + } + writeJSON(w, 200, map[string]any{"access": access, "subscription": subscription, "promotions": promotions}, id) + case "/api/v1/me/subscriptions": + if r.Method != http.MethodPost { + http.Error(w, http.StatusText(405), 405) + return + } + var body planRequest + if !decodeEntitlement(r.Body, &body) || body.PlanID == nil { + writeAPIError(w, 400, "INVALID_REQUEST", "Invalid request", id) + return + } + v, err := h.service.RequestSubscription(r.Context(), identity, *body.PlanID) + if err != nil { + writeEntitlementError(w, err, id) + return + } + writeJSON(w, 201, v, id) + case "/api/v1/me/promotions": + if r.Method != http.MethodPost { + http.Error(w, http.StatusText(405), 405) + return + } + var body promotionRequest + if !decodeEntitlement(r.Body, &body) || body.ListingID == nil || body.PeriodID == nil { + writeAPIError(w, 400, "INVALID_REQUEST", "Invalid request", id) + return + } + v, err := h.service.RequestPromotion(r.Context(), identity, *body.ListingID, *body.PeriodID) + if err != nil { + writeEntitlementError(w, err, id) + return + } + writeJSON(w, 201, v, id) + default: + writeAPIError(w, 404, "NOT_FOUND", "Not found", id) + } +} +func decodeEntitlement(r io.Reader, target any) bool { + d := json.NewDecoder(io.LimitReader(r, 4097)) + d.DisallowUnknownFields() + if d.Decode(target) != nil { + return false + } + var extra any + return errors.Is(d.Decode(&extra), io.EOF) +} +func writeEntitlementError(w http.ResponseWriter, err error, id string) { + switch { + case errors.Is(err, entitlements.ErrInvalid): + writeAPIError(w, 400, "INVALID_REQUEST", "Invalid request", id) + case errors.Is(err, entitlements.ErrUnauthorized): + writeAPIError(w, 401, "UNAUTHORIZED", "Unauthorized", id) + case errors.Is(err, entitlements.ErrForbidden): + writeAPIError(w, 403, "FORBIDDEN", "Forbidden", id) + case errors.Is(err, entitlements.ErrConflict): + writeAPIError(w, 409, "CONFLICT", "Conflict", id) + default: + writeAPIError(w, 503, "SERVICE_UNAVAILABLE", "Service unavailable", id) + } +} diff --git a/backend/internal/httpapi/health_handler.go b/backend/internal/httpapi/health_handler.go new file mode 100644 index 0000000..e4d9924 --- /dev/null +++ b/backend/internal/httpapi/health_handler.go @@ -0,0 +1,125 @@ +package httpapi + +import ( + "crypto/rand" + "encoding/hex" + "encoding/json" + "net/http" + "strings" + + "github.com/SourceSenseiTheRealOne/juntly/backend/internal/authn" + "github.com/SourceSenseiTheRealOne/juntly/backend/internal/health" +) + +const RequestIDHeader = "X-Request-ID" + +type HealthHandler struct { + service health.Service +} + +func NewHealthHandler(service health.Service) HealthHandler { + return HealthHandler{service: service} +} + +func (h HealthHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { + if r.Method != http.MethodGet { + w.Header().Set("Allow", http.MethodGet) + http.Error(w, http.StatusText(http.StatusMethodNotAllowed), http.StatusMethodNotAllowed) + return + } + + requestID := requestIDFromHeader(r.Header.Get(RequestIDHeader)) + w.Header().Set(RequestIDHeader, requestID) + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + + _ = json.NewEncoder(w).Encode(h.service.Check(requestID)) +} + +func NewRouter(service health.Service, readinessService ReadinessService, verifier authn.Verifier, reconcileService ReconcileService, accountService AccountService, referenceService ReferenceService, providerProfileService ProviderProfileService, listingService ListingService, moderationListingService ModerationListingService, publicDiscoveryService PublicDiscoveryService, contactChannelService ContactChannelService, contactRevealService ContactRevealService, messagingService MessagingService, quotationService QuotationService, bookingService BookingService, reviewService ReviewService, entitlementService EntitlementService, administrationService AdministrationService, paymentService PaymentService) http.Handler { + mux := http.NewServeMux() + mux.Handle("/api/v1/health", NewHealthHandler(service)) + mux.Handle("/api/v1/ready", NewReadinessHandler(readinessService)) + mux.Handle("/api/v1/catalog/categories", NewCategoriesHandler(referenceService)) + mux.Handle("/api/v1/reference/languages", NewLanguagesHandler(referenceService)) + mux.Handle("/api/v1/reference/localities", NewLocalitiesHandler(referenceService)) + mux.Handle("/api/v1/discovery/listings", NewPublicDiscoveryHandler(publicDiscoveryService)) + mux.Handle("/api/v1/public/listings/", NewPublicListingHandler(publicDiscoveryService)) + mux.Handle("/api/v1/auth/reconcile", authn.RequireVerifiedIdentity(verifier, NewReconcileHandler(reconcileService))) + mux.Handle("/api/v1/me/account", authn.RequireVerifiedIdentity(verifier, NewAccountHandler(accountService))) + mux.Handle("/api/v1/me/provider-profile", authn.RequireVerifiedIdentity(verifier, NewProviderProfileHandler(providerProfileService))) + mux.Handle("/api/v1/me/contact-channels", authn.RequireVerifiedIdentity(verifier, NewContactChannelHandler(contactChannelService))) + mux.Handle("/api/v1/listings/", authn.RequireVerifiedIdentity(verifier, NewContactRevealHandler(contactRevealService))) + mux.Handle("/api/v1/me/listings", authn.RequireVerifiedIdentity(verifier, NewListingHandler(listingService))) + mux.Handle("/api/v1/me/listings/", authn.RequireVerifiedIdentity(verifier, NewListingHandler(listingService))) + mux.Handle("/api/v1/moderation/listings", authn.RequireVerifiedIdentity(verifier, NewModerationListingHandler(moderationListingService))) + mux.Handle("/api/v1/moderation/listings/", authn.RequireVerifiedIdentity(verifier, NewModerationListingHandler(moderationListingService))) + mux.Handle("/api/v1/me/conversations", authn.RequireVerifiedIdentity(verifier, NewMessagingHandler(messagingService))) + mux.Handle("/api/v1/me/conversations/", authn.RequireVerifiedIdentity(verifier, NewMessagingHandler(messagingService))) + mux.Handle("/api/v1/me/notifications", authn.RequireVerifiedIdentity(verifier, NewMessagingHandler(messagingService))) + mux.Handle("/api/v1/me/notifications/", authn.RequireVerifiedIdentity(verifier, NewMessagingHandler(messagingService))) + mux.Handle("/api/v1/me/quotation-requests", authn.RequireVerifiedIdentity(verifier, NewQuotationHandler(quotationService))) + mux.Handle("/api/v1/me/quotation-requests/", authn.RequireVerifiedIdentity(verifier, NewQuotationHandler(quotationService))) + mux.Handle("/api/v1/me/quotation-opportunities", authn.RequireVerifiedIdentity(verifier, NewQuotationHandler(quotationService))) + bookingHandler := NewBookingHandler(bookingService) + paymentHandler := NewPaymentHandler(paymentService) + mux.Handle("/api/v1/me/bookings", authn.RequireVerifiedIdentity(verifier, bookingHandler)) + mux.Handle("/api/v1/me/bookings/", authn.RequireVerifiedIdentity(verifier, dispatchCheckout(paymentHandler, bookingHandler))) + mux.Handle("/api/v1/me/reviews", authn.RequireVerifiedIdentity(verifier, NewReviewHandler(reviewService))) + mux.Handle("/api/v1/me/reviews/", authn.RequireVerifiedIdentity(verifier, NewReviewHandler(reviewService))) + mux.Handle("/api/v1/public/providers/", NewReviewHandler(reviewService)) + mux.Handle("/api/v1/entitlements/catalog", NewEntitlementHandler(entitlementService)) + mux.Handle("/api/v1/me/entitlements", authn.RequireVerifiedIdentity(verifier, NewEntitlementHandler(entitlementService))) + mux.Handle("/api/v1/me/subscriptions", authn.RequireVerifiedIdentity(verifier, NewEntitlementHandler(entitlementService))) + mux.Handle("/api/v1/me/promotions", authn.RequireVerifiedIdentity(verifier, NewEntitlementHandler(entitlementService))) + mux.Handle("/api/v1/admin/dashboard", authn.RequireVerifiedIdentity(verifier, NewAdministrationHandler(administrationService))) + mux.Handle("/api/v1/admin/moderation", authn.RequireVerifiedIdentity(verifier, NewAdministrationHandler(administrationService))) + mux.Handle("/api/v1/me/payments", authn.RequireVerifiedIdentity(verifier, paymentHandler)) + mux.Handle("/api/v1/me/payout-account", authn.RequireVerifiedIdentity(verifier, paymentHandler)) + mux.Handle("/api/v1/admin/payments/", authn.RequireVerifiedIdentity(verifier, paymentHandler)) + mux.Handle("/api/v1/admin/payments", authn.RequireVerifiedIdentity(verifier, paymentHandler)) + mux.Handle("/api/v1/payments/webhooks/stripe", NewStripeWebhookHandler(paymentService)) + return mux +} + +func dispatchCheckout(payment, booking http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + if strings.HasSuffix(strings.TrimSuffix(r.URL.Path, "/"), "/checkout") { + payment.ServeHTTP(w, r) + return + } + booking.ServeHTTP(w, r) + }) +} + +func requestIDFromHeader(value string) string { + if validRequestID(value) { + return value + } + + var randomBytes [16]byte + if _, err := rand.Read(randomBytes[:]); err != nil { + return "req_unavailable" + } + + return "req_" + hex.EncodeToString(randomBytes[:]) +} + +func validRequestID(value string) bool { + if len(value) < 8 || len(value) > 128 { + return false + } + + for _, char := range value { + switch { + case char >= 'A' && char <= 'Z': + case char >= 'a' && char <= 'z': + case char >= '0' && char <= '9': + case char == '.', char == '_', char == ':', char == '-': + default: + return false + } + } + + return true +} diff --git a/backend/internal/httpapi/health_handler_test.go b/backend/internal/httpapi/health_handler_test.go new file mode 100644 index 0000000..93e2238 --- /dev/null +++ b/backend/internal/httpapi/health_handler_test.go @@ -0,0 +1,77 @@ +package httpapi_test + +import ( + "encoding/json" + "net/http" + "net/http/httptest" + "testing" + "time" + + "github.com/SourceSenseiTheRealOne/juntly/backend/internal/health" + "github.com/SourceSenseiTheRealOne/juntly/backend/internal/httpapi" +) + +func TestHealthHandlerReturnsCorrelationHeaderAndBody(t *testing.T) { + service := health.NewService("0.1.0", func() time.Time { + return time.Date(2026, 8, 20, 9, 30, 0, 0, time.UTC) + }) + handler := httpapi.NewHealthHandler(service) + request := httptest.NewRequest(http.MethodGet, "/api/v1/health", nil) + request.Header.Set("X-Request-ID", "req_from_client") + response := httptest.NewRecorder() + + handler.ServeHTTP(response, request) + + if response.Code != http.StatusOK { + t.Fatalf("status = %d, want %d", response.Code, http.StatusOK) + } + if got := response.Header().Get("X-Request-ID"); got != "req_from_client" { + t.Fatalf("X-Request-ID = %q, want req_from_client", got) + } + if got := response.Header().Get("Content-Type"); got != "application/json" { + t.Fatalf("Content-Type = %q, want application/json", got) + } + + var body struct { + Status string `json:"status"` + Service string `json:"service"` + Version string `json:"version"` + CheckedAt string `json:"checkedAt"` + RequestID string `json:"requestId"` + } + if err := json.NewDecoder(response.Body).Decode(&body); err != nil { + t.Fatalf("decode response: %v", err) + } + if body.RequestID != "req_from_client" { + t.Fatalf("body requestId = %q, want req_from_client", body.RequestID) + } + if body.Status != "ok" || body.Service != "juntly-api" || body.Version != "0.1.0" { + t.Fatalf("unexpected health body: %#v", body) + } +} + +func TestHealthHandlerGeneratesCorrelationIDWhenMissing(t *testing.T) { + service := health.NewService("0.1.0", func() time.Time { + return time.Date(2026, 8, 20, 9, 30, 0, 0, time.UTC) + }) + handler := httpapi.NewHealthHandler(service) + request := httptest.NewRequest(http.MethodGet, "/api/v1/health", nil) + response := httptest.NewRecorder() + + handler.ServeHTTP(response, request) + + headerRequestID := response.Header().Get("X-Request-ID") + if headerRequestID == "" { + t.Fatal("X-Request-ID header is empty") + } + + var body struct { + RequestID string `json:"requestId"` + } + if err := json.NewDecoder(response.Body).Decode(&body); err != nil { + t.Fatalf("decode response: %v", err) + } + if body.RequestID != headerRequestID { + t.Fatalf("body requestId = %q, want header %q", body.RequestID, headerRequestID) + } +} diff --git a/backend/internal/httpapi/listing_handler.go b/backend/internal/httpapi/listing_handler.go new file mode 100644 index 0000000..3956fe6 --- /dev/null +++ b/backend/internal/httpapi/listing_handler.go @@ -0,0 +1,342 @@ +package httpapi + +import ( + "context" + "encoding/json" + "errors" + "io" + "net/http" + "strings" + "time" + + "github.com/SourceSenseiTheRealOne/juntly/backend/internal/authn" + "github.com/SourceSenseiTheRealOne/juntly/backend/internal/listingmedia" + "github.com/SourceSenseiTheRealOne/juntly/backend/internal/listings" + "github.com/SourceSenseiTheRealOne/juntly/backend/internal/provideraccess" + "github.com/SourceSenseiTheRealOne/juntly/backend/internal/users" + "github.com/google/uuid" +) + +const maxListingRequestBytes = 16 * 1024 + +type ListingService interface { + Create(context.Context, users.VerifiedIdentity, listings.CreateListing) (listings.Listing, error) + ReplaceDraft(context.Context, users.VerifiedIdentity, uuid.UUID, int, listings.CreateListing) (listings.Listing, error) + Get(context.Context, users.VerifiedIdentity, uuid.UUID) (*listings.Listing, error) + List(context.Context, users.VerifiedIdentity) ([]listings.Listing, error) + Submit(context.Context, users.VerifiedIdentity, uuid.UUID, int) (listings.Listing, error) + Pause(context.Context, users.VerifiedIdentity, uuid.UUID, int) (listings.Listing, error) + Archive(context.Context, users.VerifiedIdentity, uuid.UUID, listings.State, int) (listings.Listing, error) + CreateUploadIntent(context.Context, users.VerifiedIdentity, uuid.UUID, listingmedia.UploadRequest) (listingmedia.UploadIntent, error) +} +type listingHandler struct{ service ListingService } +type listingRequest struct { + CategoryID *uuid.UUID `json:"categoryId"` + PrimaryLocalityID *uuid.UUID `json:"primaryLocalityId"` + Title *string `json:"title"` + Description *string `json:"description"` + PriceType *string `json:"priceType"` + PriceMinor **int `json:"priceMinor"` + Currency *string `json:"currency"` + TravelsToCustomer *bool `json:"travelsToCustomer"` + ReceivesCustomer *bool `json:"receivesCustomer"` + RemoteServices *bool `json:"remoteServices"` + Revision *int `json:"revision"` +} +type listingResponse struct { + ID uuid.UUID `json:"id"` + CategoryID uuid.UUID `json:"categoryId"` + PrimaryLocalityID uuid.UUID `json:"primaryLocalityId"` + Title string `json:"title"` + Description string `json:"description"` + PriceType listings.PriceType `json:"priceType"` + PriceMinor *int `json:"priceMinor"` + Currency string `json:"currency"` + TravelsToCustomer bool `json:"travelsToCustomer"` + ReceivesCustomer bool `json:"receivesCustomer"` + RemoteServices bool `json:"remoteServices"` + State listings.State `json:"state"` + Revision int `json:"revision"` + CreatedAt string `json:"createdAt"` + UpdatedAt string `json:"updatedAt"` +} +type listingsResponse struct { + Listings []listingResponse `json:"listings"` +} +type uploadCapabilityResponse struct { + URL string `json:"url"` + Method string `json:"method"` + Headers map[string]string `json:"headers"` +} +type uploadIntentResponse struct { + MediaID uuid.UUID `json:"mediaId"` + Capability uploadCapabilityResponse `json:"capability"` +} + +func NewListingHandler(service ListingService) http.Handler { return listingHandler{service: service} } +func (h listingHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { + id := requestIDFromHeader(r.Header.Get(RequestIDHeader)) + identity, ok := authn.IdentityFromContext(r.Context()) + if !ok { + writeAPIError(w, 401, "UNAUTHORIZED", "Unauthorized", id) + return + } + if h.service == nil { + writeAPIError(w, 503, "SERVICE_UNAVAILABLE", "Service unavailable", id) + return + } + suffix := strings.TrimPrefix(r.URL.Path, "/api/v1/me/listings") + if suffix == "" || suffix == "/" { + h.collection(w, r, identity, id) + return + } + parts := strings.Split(strings.Trim(suffix, "/"), "/") + listingID, err := uuid.Parse(parts[0]) + if err != nil { + writeAPIError(w, 400, "INVALID_REQUEST", "Invalid request", id) + return + } + if len(parts) == 2 { + switch parts[1] { + case "submit": + h.submit(w, r, identity, listingID, id) + case "pause": + h.pause(w, r, identity, listingID, id) + case "archive": + h.archive(w, r, identity, listingID, id) + default: + writeAPIError(w, 400, "INVALID_REQUEST", "Invalid request", id) + } + return + } + if len(parts) == 3 && parts[1] == "media" && parts[2] == "upload-intents" { + h.uploadIntent(w, r, identity, listingID, id) + return + } + if len(parts) != 1 { + writeAPIError(w, 400, "INVALID_REQUEST", "Invalid request", id) + return + } + h.item(w, r, identity, listingID, id) +} +func (h listingHandler) collection(w http.ResponseWriter, r *http.Request, identity users.VerifiedIdentity, id string) { + switch r.Method { + case http.MethodGet: + values, err := h.service.List(r.Context(), identity) + if err != nil { + writeListingError(w, err, id) + return + } + out := make([]listingResponse, len(values)) + for i, v := range values { + out[i] = listingResponseFrom(v) + } + writeJSON(w, 200, listingsResponse{Listings: out}, id) + case http.MethodPost: + input, _, ok := decodeListing(r.Body, false) + if !ok { + writeAPIError(w, 400, "INVALID_REQUEST", "Invalid request", id) + return + } + value, err := h.service.Create(r.Context(), identity, input) + if err != nil { + writeListingError(w, err, id) + return + } + writeJSON(w, 200, listingResponseFrom(value), id) + default: + w.Header().Set("Allow", "GET, POST") + http.Error(w, http.StatusText(405), 405) + } +} +func (h listingHandler) item(w http.ResponseWriter, r *http.Request, identity users.VerifiedIdentity, listingID uuid.UUID, id string) { + switch r.Method { + case http.MethodGet: + value, err := h.service.Get(r.Context(), identity, listingID) + if err != nil { + writeListingError(w, err, id) + return + } + if value == nil { + writeListingError(w, listings.ErrConflict, id) + return + } + writeJSON(w, 200, listingResponseFrom(*value), id) + case http.MethodPut: + input, revision, ok := decodeListing(r.Body, true) + if !ok { + writeAPIError(w, 400, "INVALID_REQUEST", "Invalid request", id) + return + } + value, err := h.service.ReplaceDraft(r.Context(), identity, listingID, revision, input) + if err != nil { + writeListingError(w, err, id) + return + } + writeJSON(w, 200, listingResponseFrom(value), id) + default: + w.Header().Set("Allow", "GET, PUT") + http.Error(w, http.StatusText(405), 405) + } +} +func (h listingHandler) submit(w http.ResponseWriter, r *http.Request, identity users.VerifiedIdentity, listingID uuid.UUID, id string) { + if r.Method != http.MethodPost { + w.Header().Set("Allow", "POST") + http.Error(w, http.StatusText(405), 405) + return + } + revision, ok := decodeRevision(r.Body) + if !ok { + writeAPIError(w, 400, "INVALID_REQUEST", "Invalid request", id) + return + } + value, err := h.service.Submit(r.Context(), identity, listingID, revision) + if err != nil { + writeListingError(w, err, id) + return + } + writeJSON(w, 200, listingResponseFrom(value), id) +} +func (h listingHandler) pause(w http.ResponseWriter, r *http.Request, identity users.VerifiedIdentity, listingID uuid.UUID, id string) { + if r.Method != http.MethodPost { + w.Header().Set("Allow", "POST") + http.Error(w, http.StatusText(405), 405) + return + } + revision, ok := decodeRevision(r.Body) + if !ok { + writeAPIError(w, 400, "INVALID_REQUEST", "Invalid request", id) + return + } + value, err := h.service.Pause(r.Context(), identity, listingID, revision) + if err != nil { + writeListingError(w, err, id) + return + } + writeJSON(w, 200, listingResponseFrom(value), id) +} +func (h listingHandler) archive(w http.ResponseWriter, r *http.Request, identity users.VerifiedIdentity, listingID uuid.UUID, id string) { + if r.Method != http.MethodPost { + w.Header().Set("Allow", "POST") + http.Error(w, http.StatusText(405), 405) + return + } + d := json.NewDecoder(io.LimitReader(r.Body, maxListingRequestBytes+1)) + d.DisallowUnknownFields() + var value struct { + Revision *int `json:"revision"` + State *string `json:"state"` + } + if err := d.Decode(&value); err != nil || value.Revision == nil || value.State == nil || *value.Revision < 1 { + writeAPIError(w, 400, "INVALID_REQUEST", "Invalid request", id) + return + } + var extra any + if err := d.Decode(&extra); !errors.Is(err, io.EOF) { + writeAPIError(w, 400, "INVALID_REQUEST", "Invalid request", id) + return + } + from := listings.State(*value.State) + if from != listings.StateDraft && from != listings.StateRejected && from != listings.StateActive && from != listings.StatePaused { + writeAPIError(w, 400, "INVALID_REQUEST", "Invalid request", id) + return + } + listing, err := h.service.Archive(r.Context(), identity, listingID, from, *value.Revision) + if err != nil { + writeListingError(w, err, id) + return + } + writeJSON(w, 200, listingResponseFrom(listing), id) +} +func (h listingHandler) uploadIntent(w http.ResponseWriter, r *http.Request, identity users.VerifiedIdentity, listingID uuid.UUID, id string) { + if r.Method != http.MethodPost { + w.Header().Set("Allow", "POST") + http.Error(w, http.StatusText(405), 405) + return + } + d := json.NewDecoder(io.LimitReader(r.Body, maxListingRequestBytes+1)) + d.DisallowUnknownFields() + var value struct { + Ordinal *int `json:"ordinal"` + ContentType *string `json:"contentType"` + ByteSize *int64 `json:"byteSize"` + ChecksumSHA256 *string `json:"checksumSha256"` + } + if err := d.Decode(&value); err != nil || value.Ordinal == nil || value.ContentType == nil || value.ByteSize == nil || value.ChecksumSHA256 == nil { + writeAPIError(w, 400, "INVALID_REQUEST", "Invalid request", id) + return + } + var extra any + if err := d.Decode(&extra); !errors.Is(err, io.EOF) { + writeAPIError(w, 400, "INVALID_REQUEST", "Invalid request", id) + return + } + intent, err := h.service.CreateUploadIntent(r.Context(), identity, listingID, listingmedia.UploadRequest{Ordinal: *value.Ordinal, ContentType: *value.ContentType, ByteSize: *value.ByteSize, ChecksumSHA256: *value.ChecksumSHA256}) + if err != nil { + writeListingError(w, err, id) + return + } + writeJSON(w, 200, uploadIntentResponse{MediaID: intent.MediaID, Capability: uploadCapabilityResponse{URL: intent.Capability.URL, Method: intent.Capability.Method, Headers: intent.Capability.Headers}}, id) +} +func decodeListing(body io.Reader, revisionRequired bool) (listings.CreateListing, int, bool) { + d := json.NewDecoder(io.LimitReader(body, maxListingRequestBytes+1)) + d.DisallowUnknownFields() + var v listingRequest + if err := d.Decode(&v); err != nil { + return listings.CreateListing{}, 0, false + } + var extra any + if err := d.Decode(&extra); !errors.Is(err, io.EOF) { + return listings.CreateListing{}, 0, false + } + if v.CategoryID == nil || v.PrimaryLocalityID == nil || v.Title == nil || v.Description == nil || v.PriceType == nil || v.PriceMinor == nil || v.Currency == nil || v.TravelsToCustomer == nil || v.ReceivesCustomer == nil || v.RemoteServices == nil || (revisionRequired && v.Revision == nil) { + return listings.CreateListing{}, 0, false + } + var price *int + if *v.PriceMinor != nil { + value := **v.PriceMinor + price = &value + } + return listings.CreateListing{CategoryID: *v.CategoryID, PrimaryLocalityID: *v.PrimaryLocalityID, Title: *v.Title, Description: *v.Description, PriceType: listings.PriceType(*v.PriceType), PriceMinor: price, Currency: *v.Currency, TravelsToCustomer: *v.TravelsToCustomer, ReceivesCustomer: *v.ReceivesCustomer, RemoteServices: *v.RemoteServices}, valueOrZero(v.Revision), true +} +func decodeRevision(body io.Reader) (int, bool) { + d := json.NewDecoder(io.LimitReader(body, maxListingRequestBytes+1)) + d.DisallowUnknownFields() + var v struct { + Revision *int `json:"revision"` + } + if err := d.Decode(&v); err != nil || v.Revision == nil { + return 0, false + } + var extra any + if err := d.Decode(&extra); !errors.Is(err, io.EOF) { + return 0, false + } + if *v.Revision < 1 { + return 0, false + } + return *v.Revision, true +} +func valueOrZero(v *int) int { + if v == nil { + return 0 + } + return *v +} +func listingResponseFrom(v listings.Listing) listingResponse { + return listingResponse{ID: v.ID, CategoryID: v.CategoryID, PrimaryLocalityID: v.PrimaryLocalityID, Title: v.Title, Description: v.Description, PriceType: v.PriceType, PriceMinor: v.PriceMinor, Currency: v.Currency, TravelsToCustomer: v.TravelsToCustomer, ReceivesCustomer: v.ReceivesCustomer, RemoteServices: v.RemoteServices, State: v.State, Revision: v.Revision, CreatedAt: v.CreatedAt.UTC().Format(time.RFC3339Nano), UpdatedAt: v.UpdatedAt.UTC().Format(time.RFC3339Nano)} +} +func writeListingError(w http.ResponseWriter, err error, id string) { + switch { + case errors.Is(err, provideraccess.ErrUnauthorized): + writeAPIError(w, 401, "UNAUTHORIZED", "Unauthorized", id) + case errors.Is(err, provideraccess.ErrForbidden): + writeAPIError(w, 403, "FORBIDDEN", "Forbidden", id) + case errors.Is(err, listings.ErrInvalidListing): + writeAPIError(w, 400, "INVALID_REQUEST", "Invalid request", id) + case errors.Is(err, listings.ErrConflict): + writeAPIError(w, 409, "CONFLICT", "Conflict", id) + default: + writeAPIError(w, 503, "SERVICE_UNAVAILABLE", "Service unavailable", id) + } +} diff --git a/backend/internal/httpapi/listing_handler_test.go b/backend/internal/httpapi/listing_handler_test.go new file mode 100644 index 0000000..1343cab --- /dev/null +++ b/backend/internal/httpapi/listing_handler_test.go @@ -0,0 +1,118 @@ +package httpapi_test + +import ( + "context" + "net/http" + "net/http/httptest" + "strings" + "testing" + + "github.com/SourceSenseiTheRealOne/juntly/backend/internal/authn" + "github.com/SourceSenseiTheRealOne/juntly/backend/internal/httpapi" + "github.com/SourceSenseiTheRealOne/juntly/backend/internal/listingmedia" + "github.com/SourceSenseiTheRealOne/juntly/backend/internal/listings" + "github.com/SourceSenseiTheRealOne/juntly/backend/internal/users" + "github.com/google/uuid" +) + +func TestListingHandlerRequiresIdentityAndStrictlyCreatesOwnerDraft(t *testing.T) { + t.Parallel() + service := &recordingListingService{created: sampleListing()} + handler := httpapi.NewListingHandler(service) + unauthorized := httptest.NewRecorder() + handler.ServeHTTP(unauthorized, httptest.NewRequest(http.MethodPost, "/api/v1/me/listings", strings.NewReader(validListingJSON()))) + if unauthorized.Code != http.StatusUnauthorized || service.calls != 0 { + t.Fatalf("unauthorized status/calls = %d/%d", unauthorized.Code, service.calls) + } + + response := httptest.NewRecorder() + request := httptest.NewRequest(http.MethodPost, "/api/v1/me/listings", strings.NewReader(validListingJSON())) + request.Header.Set("Authorization", "Bearer synthetic-token") + request.Header.Set(httpapi.RequestIDHeader, "req_listing_create") + authn.RequireVerifiedIdentity(staticVerifier{identity: users.VerifiedIdentity{Subject: "provider"}}, handler).ServeHTTP(response, request) + if response.Code != http.StatusOK || service.createCalls != 1 || strings.Contains(response.Body.String(), "internalUserId") { + t.Fatalf("create status/body = %d/%s", response.Code, response.Body.String()) + } +} + +func TestListingHandlerMapsConflictWithoutInternalDetails(t *testing.T) { + t.Parallel() + service := &recordingListingService{err: listings.ErrConflict} + response := httptest.NewRecorder() + request := httptest.NewRequest(http.MethodPost, "/api/v1/me/listings/aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa/submit", strings.NewReader(`{"revision":1}`)) + request.Header.Set("Authorization", "Bearer synthetic-token") + request.Header.Set(httpapi.RequestIDHeader, "req_listing_conflict") + authn.RequireVerifiedIdentity(staticVerifier{identity: users.VerifiedIdentity{Subject: "provider"}}, httpapi.NewListingHandler(service)).ServeHTTP(response, request) + if response.Code != http.StatusConflict || !strings.Contains(response.Body.String(), "CONFLICT") || strings.Contains(response.Body.String(), "private") { + t.Fatalf("conflict status/body=%d/%s", response.Code, response.Body.String()) + } +} + +func TestListingHandlerHandlesOwnerPauseArchiveAndUploadIntent(t *testing.T) { + t.Parallel() + service := &recordingListingService{created: sampleListing(), intent: listingmedia.UploadIntent{MediaID: uuid.MustParse("dddddddd-dddd-4ddd-8ddd-dddddddddddd"), Capability: listingmedia.UploadCapability{URL: "https://upload.example.invalid/capability", Method: "PUT", Headers: map[string]string{"Content-Type": "image/webp"}}}} + handler := authn.RequireVerifiedIdentity(staticVerifier{identity: users.VerifiedIdentity{Subject: "provider"}}, httpapi.NewListingHandler(service)) + for path, body := range map[string]string{ + "/api/v1/me/listings/aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa/pause": `{"revision":1}`, + "/api/v1/me/listings/aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa/archive": `{"revision":1,"state":"paused"}`, + "/api/v1/me/listings/aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa/media/upload-intents": `{"ordinal":1,"contentType":"image/webp","byteSize":1024,"checksumSha256":"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"}`, + } { + response := httptest.NewRecorder() + request := httptest.NewRequest(http.MethodPost, path, strings.NewReader(body)) + request.Header.Set("Authorization", "Bearer synthetic-token") + request.Header.Set(httpapi.RequestIDHeader, "req_listing_owner_action") + handler.ServeHTTP(response, request) + if response.Code != http.StatusOK || strings.Contains(response.Body.String(), "objectReference") { + t.Fatalf("%s status/body=%d/%s", path, response.Code, response.Body.String()) + } + } +} + +type recordingListingService struct { + created listings.Listing + intent listingmedia.UploadIntent + err error + calls int + createCalls int +} + +func (s *recordingListingService) Create(context.Context, users.VerifiedIdentity, listings.CreateListing) (listings.Listing, error) { + s.calls++ + s.createCalls++ + return s.created, s.err +} +func (s *recordingListingService) ReplaceDraft(context.Context, users.VerifiedIdentity, uuid.UUID, int, listings.CreateListing) (listings.Listing, error) { + s.calls++ + return s.created, s.err +} +func (s *recordingListingService) Get(context.Context, users.VerifiedIdentity, uuid.UUID) (*listings.Listing, error) { + s.calls++ + return &s.created, s.err +} +func (s *recordingListingService) List(context.Context, users.VerifiedIdentity) ([]listings.Listing, error) { + s.calls++ + return []listings.Listing{s.created}, s.err +} +func (s *recordingListingService) Submit(context.Context, users.VerifiedIdentity, uuid.UUID, int) (listings.Listing, error) { + s.calls++ + return s.created, s.err +} +func (s *recordingListingService) Pause(context.Context, users.VerifiedIdentity, uuid.UUID, int) (listings.Listing, error) { + s.calls++ + return s.created, s.err +} +func (s *recordingListingService) Archive(context.Context, users.VerifiedIdentity, uuid.UUID, listings.State, int) (listings.Listing, error) { + s.calls++ + return s.created, s.err +} +func (s *recordingListingService) CreateUploadIntent(context.Context, users.VerifiedIdentity, uuid.UUID, listingmedia.UploadRequest) (listingmedia.UploadIntent, error) { + s.calls++ + return s.intent, s.err +} +func sampleListing() listings.Listing { + price := 5000 + return listings.Listing{ID: uuid.MustParse("aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa"), CreateListing: listings.CreateListing{CategoryID: uuid.MustParse("bbbbbbbb-bbbb-4bbb-8bbb-bbbbbbbbbbbb"), PrimaryLocalityID: uuid.MustParse("cccccccc-cccc-4ccc-8ccc-cccccccccccc"), Title: "Listing test", Description: "Synthetic listing handler contract description.", PriceType: listings.PriceTypeFixed, PriceMinor: &price, Currency: "EUR", TravelsToCustomer: true}, State: listings.StateDraft, Revision: 1} +} +func validListingJSON() string { + return `{"categoryId":"bbbbbbbb-bbbb-4bbb-8bbb-bbbbbbbbbbbb","primaryLocalityId":"cccccccc-cccc-4ccc-8ccc-cccccccccccc","title":"Listing test","description":"Synthetic listing handler contract description.","priceType":"fixed","priceMinor":5000,"currency":"EUR","travelsToCustomer":true,"receivesCustomer":false,"remoteServices":false}` +} diff --git a/backend/internal/httpapi/messaging_handler.go b/backend/internal/httpapi/messaging_handler.go new file mode 100644 index 0000000..20ae516 --- /dev/null +++ b/backend/internal/httpapi/messaging_handler.go @@ -0,0 +1,309 @@ +package httpapi + +import ( + "context" + "encoding/json" + "errors" + "io" + "net/http" + "strings" + "time" + + "github.com/SourceSenseiTheRealOne/juntly/backend/internal/authn" + "github.com/SourceSenseiTheRealOne/juntly/backend/internal/messaging" + "github.com/SourceSenseiTheRealOne/juntly/backend/internal/users" + "github.com/google/uuid" +) + +const maxMessagingRequestBytes = 8 * 1024 + +type MessagingService interface { + Start(context.Context, users.VerifiedIdentity, uuid.UUID) (messaging.Conversation, error) + List(context.Context, users.VerifiedIdentity) ([]messaging.Conversation, error) + ListMessages(context.Context, users.VerifiedIdentity, uuid.UUID) ([]messaging.Message, error) + Send(context.Context, users.VerifiedIdentity, uuid.UUID, string) (messaging.Message, error) + SetBlocked(context.Context, users.VerifiedIdentity, uuid.UUID, bool) error + Report(context.Context, users.VerifiedIdentity, uuid.UUID, *uuid.UUID, string) error + Preferences(context.Context, users.VerifiedIdentity) (messaging.NotificationPreferences, error) + ReplacePreferences(context.Context, users.VerifiedIdentity, messaging.NotificationPreferences) (messaging.NotificationPreferences, error) + Notifications(context.Context, users.VerifiedIdentity) ([]messaging.Notification, error) + MarkNotificationRead(context.Context, users.VerifiedIdentity, uuid.UUID) error +} + +type messagingHandler struct{ service MessagingService } + +func NewMessagingHandler(service MessagingService) http.Handler { + return messagingHandler{service: service} +} + +type conversationResponse struct { + ID uuid.UUID `json:"id"` + ListingID *uuid.UUID `json:"listingId"` + CustomerID uuid.UUID `json:"customerId"` + ProviderID uuid.UUID `json:"providerId"` + Blocked bool `json:"blocked"` + CreatedAt string `json:"createdAt"` + UpdatedAt string `json:"updatedAt"` +} +type messageResponse struct { + ID uuid.UUID `json:"id"` + ConversationID uuid.UUID `json:"conversationId"` + SenderID uuid.UUID `json:"senderId"` + Body string `json:"body"` + CreatedAt string `json:"createdAt"` +} +type notificationResponse struct { + ID uuid.UUID `json:"id"` + Kind string `json:"kind"` + Read bool `json:"read"` + CreatedAt string `json:"createdAt"` +} +type startConversationRequest struct { + ListingID *uuid.UUID `json:"listingId"` +} +type sendMessageRequest struct { + Body *string `json:"body"` +} +type blockConversationRequest struct { + Blocked *bool `json:"blocked"` +} +type reportConversationRequest struct { + MessageID *uuid.UUID `json:"messageId"` + Reason *string `json:"reason"` +} +type notificationPreferencesRequest struct { + InAppEnabled *bool `json:"inAppEnabled"` + EmailEnabled *bool `json:"emailEnabled"` +} +type notificationPreferencesResponse struct { + InAppEnabled bool `json:"inAppEnabled"` + EmailEnabled bool `json:"emailEnabled"` +} + +func (h messagingHandler) ServeHTTP(w http.ResponseWriter, request *http.Request) { + requestID := requestIDFromHeader(request.Header.Get(RequestIDHeader)) + identity, ok := authn.IdentityFromContext(request.Context()) + if !ok { + writeAPIError(w, http.StatusUnauthorized, "UNAUTHORIZED", "Unauthorized", requestID) + return + } + if h.service == nil { + writeAPIError(w, http.StatusServiceUnavailable, "SERVICE_UNAVAILABLE", "Service unavailable", requestID) + return + } + path := strings.TrimSuffix(request.URL.Path, "/") + if strings.HasPrefix(path, "/api/v1/me/conversations") { + h.conversations(w, request, identity, requestID, path) + return + } + if strings.HasPrefix(path, "/api/v1/me/notifications") { + h.notifications(w, request, identity, requestID, path) + return + } + writeAPIError(w, http.StatusNotFound, "NOT_FOUND", "Not found", requestID) +} + +func (h messagingHandler) conversations(w http.ResponseWriter, r *http.Request, identity users.VerifiedIdentity, requestID, path string) { + base := "/api/v1/me/conversations" + if path == base { + switch r.Method { + case http.MethodGet: + values, err := h.service.List(r.Context(), identity) + if err != nil { + writeMessagingError(w, err, requestID) + return + } + responses := make([]conversationResponse, 0, len(values)) + for _, v := range values { + responses = append(responses, conversationJSON(v)) + } + writeJSON(w, http.StatusOK, map[string]any{"conversations": responses}, requestID) + case http.MethodPost: + var input startConversationRequest + if !decodeMessaging(r.Body, &input) || input.ListingID == nil { + writeAPIError(w, http.StatusBadRequest, "INVALID_REQUEST", "Invalid request", requestID) + return + } + v, err := h.service.Start(r.Context(), identity, *input.ListingID) + if err != nil { + writeMessagingError(w, err, requestID) + return + } + writeJSON(w, http.StatusCreated, conversationJSON(v), requestID) + default: + w.Header().Set("Allow", "GET, POST") + http.Error(w, http.StatusText(http.StatusMethodNotAllowed), http.StatusMethodNotAllowed) + } + return + } + parts := strings.Split(strings.TrimPrefix(path, base+"/"), "/") + if len(parts) != 2 { + writeAPIError(w, http.StatusNotFound, "NOT_FOUND", "Not found", requestID) + return + } + conversationID, err := uuid.Parse(parts[0]) + if err != nil { + writeAPIError(w, http.StatusBadRequest, "INVALID_REQUEST", "Invalid request", requestID) + return + } + switch parts[1] { + case "messages": + if r.Method == http.MethodGet { + values, err := h.service.ListMessages(r.Context(), identity, conversationID) + if err != nil { + writeMessagingError(w, err, requestID) + return + } + responses := make([]messageResponse, 0, len(values)) + for _, v := range values { + responses = append(responses, messageJSON(v)) + } + writeJSON(w, http.StatusOK, map[string]any{"messages": responses}, requestID) + return + } + if r.Method == http.MethodPost { + var input sendMessageRequest + if !decodeMessaging(r.Body, &input) || input.Body == nil { + writeAPIError(w, http.StatusBadRequest, "INVALID_REQUEST", "Invalid request", requestID) + return + } + v, err := h.service.Send(r.Context(), identity, conversationID, *input.Body) + if err != nil { + writeMessagingError(w, err, requestID) + return + } + writeJSON(w, http.StatusCreated, messageJSON(v), requestID) + return + } + case "block": + if r.Method == http.MethodPut { + var input blockConversationRequest + if !decodeMessaging(r.Body, &input) || input.Blocked == nil { + writeAPIError(w, http.StatusBadRequest, "INVALID_REQUEST", "Invalid request", requestID) + return + } + if err := h.service.SetBlocked(r.Context(), identity, conversationID, *input.Blocked); err != nil { + writeMessagingError(w, err, requestID) + return + } + writeJSON(w, http.StatusOK, map[string]bool{"blocked": *input.Blocked}, requestID) + return + } + case "reports": + if r.Method == http.MethodPost { + var input reportConversationRequest + if !decodeMessaging(r.Body, &input) || input.Reason == nil { + writeAPIError(w, http.StatusBadRequest, "INVALID_REQUEST", "Invalid request", requestID) + return + } + if err := h.service.Report(r.Context(), identity, conversationID, input.MessageID, *input.Reason); err != nil { + writeMessagingError(w, err, requestID) + return + } + writeJSON(w, http.StatusCreated, map[string]bool{"reported": true}, requestID) + return + } + } + w.Header().Set("Allow", "GET, POST, PUT") + http.Error(w, http.StatusText(http.StatusMethodNotAllowed), http.StatusMethodNotAllowed) +} + +func (h messagingHandler) notifications(w http.ResponseWriter, r *http.Request, identity users.VerifiedIdentity, requestID, path string) { + base := "/api/v1/me/notifications" + if path == base { + if r.Method != http.MethodGet { + w.Header().Set("Allow", http.MethodGet) + http.Error(w, http.StatusText(http.StatusMethodNotAllowed), http.StatusMethodNotAllowed) + return + } + values, err := h.service.Notifications(r.Context(), identity) + if err != nil { + writeMessagingError(w, err, requestID) + return + } + responses := make([]notificationResponse, 0, len(values)) + for _, v := range values { + responses = append(responses, notificationJSON(v)) + } + writeJSON(w, http.StatusOK, map[string]any{"notifications": responses}, requestID) + return + } + if path == base+"/preferences" { + switch r.Method { + case http.MethodGet: + v, err := h.service.Preferences(r.Context(), identity) + if err != nil { + writeMessagingError(w, err, requestID) + return + } + writeJSON(w, http.StatusOK, preferencesJSON(v), requestID) + case http.MethodPut: + var input notificationPreferencesRequest + if !decodeMessaging(r.Body, &input) || input.InAppEnabled == nil || input.EmailEnabled == nil { + writeAPIError(w, http.StatusBadRequest, "INVALID_REQUEST", "Invalid request", requestID) + return + } + v, err := h.service.ReplacePreferences(r.Context(), identity, messaging.NotificationPreferences{InAppEnabled: *input.InAppEnabled, EmailEnabled: *input.EmailEnabled}) + if err != nil { + writeMessagingError(w, err, requestID) + return + } + writeJSON(w, http.StatusOK, preferencesJSON(v), requestID) + default: + w.Header().Set("Allow", "GET, PUT") + http.Error(w, http.StatusText(http.StatusMethodNotAllowed), http.StatusMethodNotAllowed) + } + return + } + parts := strings.Split(strings.TrimPrefix(path, base+"/"), "/") + if len(parts) == 2 && parts[1] == "read" && r.Method == http.MethodPost { + id, err := uuid.Parse(parts[0]) + if err != nil { + writeAPIError(w, http.StatusBadRequest, "INVALID_REQUEST", "Invalid request", requestID) + return + } + if err = h.service.MarkNotificationRead(r.Context(), identity, id); err != nil { + writeMessagingError(w, err, requestID) + return + } + writeJSON(w, http.StatusOK, map[string]bool{"read": true}, requestID) + return + } + writeAPIError(w, http.StatusNotFound, "NOT_FOUND", "Not found", requestID) +} + +func decodeMessaging(body io.Reader, target any) bool { + decoder := json.NewDecoder(io.LimitReader(body, maxMessagingRequestBytes+1)) + decoder.DisallowUnknownFields() + if err := decoder.Decode(target); err != nil { + return false + } + var extra any + return errors.Is(decoder.Decode(&extra), io.EOF) +} +func conversationJSON(v messaging.Conversation) conversationResponse { + return conversationResponse{ID: v.ID, ListingID: v.ListingID, CustomerID: v.CustomerID, ProviderID: v.ProviderID, Blocked: v.BlockedBy != nil, CreatedAt: v.CreatedAt.UTC().Format(time.RFC3339Nano), UpdatedAt: v.UpdatedAt.UTC().Format(time.RFC3339Nano)} +} +func messageJSON(v messaging.Message) messageResponse { + return messageResponse{ID: v.ID, ConversationID: v.ConversationID, SenderID: v.SenderID, Body: v.Body, CreatedAt: v.CreatedAt.UTC().Format(time.RFC3339Nano)} +} +func notificationJSON(v messaging.Notification) notificationResponse { + return notificationResponse{ID: v.ID, Kind: v.Kind, Read: v.ReadAt != nil, CreatedAt: v.CreatedAt.UTC().Format(time.RFC3339Nano)} +} +func preferencesJSON(v messaging.NotificationPreferences) notificationPreferencesResponse { + return notificationPreferencesResponse{InAppEnabled: v.InAppEnabled, EmailEnabled: v.EmailEnabled} +} +func writeMessagingError(w http.ResponseWriter, err error, requestID string) { + switch { + case errors.Is(err, messaging.ErrInvalid): + writeAPIError(w, http.StatusBadRequest, "INVALID_REQUEST", "Invalid request", requestID) + case errors.Is(err, messaging.ErrUnauthorized): + writeAPIError(w, http.StatusUnauthorized, "UNAUTHORIZED", "Unauthorized", requestID) + case errors.Is(err, messaging.ErrForbidden): + writeAPIError(w, http.StatusForbidden, "FORBIDDEN", "Forbidden", requestID) + case errors.Is(err, messaging.ErrNotFound): + writeAPIError(w, http.StatusNotFound, "NOT_FOUND", "Not found", requestID) + default: + writeAPIError(w, http.StatusServiceUnavailable, "SERVICE_UNAVAILABLE", "Service unavailable", requestID) + } +} diff --git a/backend/internal/httpapi/messaging_handler_test.go b/backend/internal/httpapi/messaging_handler_test.go new file mode 100644 index 0000000..8392403 --- /dev/null +++ b/backend/internal/httpapi/messaging_handler_test.go @@ -0,0 +1,93 @@ +package httpapi_test + +import ( + "context" + "net/http" + "net/http/httptest" + "strings" + "testing" + + "github.com/SourceSenseiTheRealOne/juntly/backend/internal/authn" + "github.com/SourceSenseiTheRealOne/juntly/backend/internal/httpapi" + "github.com/SourceSenseiTheRealOne/juntly/backend/internal/messaging" + "github.com/SourceSenseiTheRealOne/juntly/backend/internal/users" + "github.com/google/uuid" +) + +func TestMessagingHandlerSendsParticipantMessage(t *testing.T) { + t.Parallel() + conversationID := uuid.MustParse("aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa") + service := &recordingMessagingService{message: messaging.Message{ID: uuid.New(), ConversationID: conversationID, SenderID: uuid.New(), Body: "Boa tarde"}} + handler := authn.RequireVerifiedIdentity(handlerVerifier{}, httpapi.NewMessagingHandler(service)) + request := httptest.NewRequest(http.MethodPost, "/api/v1/me/conversations/"+conversationID.String()+"/messages", strings.NewReader(`{"body":"Boa tarde"}`)) + request.Header.Set("Authorization", "Bearer synthetic") + response := httptest.NewRecorder() + + handler.ServeHTTP(response, request) + + if response.Code != http.StatusCreated || service.sentBody != "Boa tarde" || service.conversationID != conversationID { + t.Fatalf("status/body/conversation = %d/%q/%s", response.Code, service.sentBody, service.conversationID) + } + if !strings.Contains(response.Body.String(), `"body":"Boa tarde"`) { + t.Fatalf("response = %s", response.Body.String()) + } +} + +func TestMessagingHandlerRejectsUnknownMessageFields(t *testing.T) { + t.Parallel() + service := &recordingMessagingService{} + handler := authn.RequireVerifiedIdentity(handlerVerifier{}, httpapi.NewMessagingHandler(service)) + request := httptest.NewRequest(http.MethodPost, "/api/v1/me/conversations/aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa/messages", strings.NewReader(`{"body":"hello","admin":true}`)) + request.Header.Set("Authorization", "Bearer synthetic") + response := httptest.NewRecorder() + + handler.ServeHTTP(response, request) + + if response.Code != http.StatusBadRequest || service.sentBody != "" { + t.Fatalf("status/body = %d/%q", response.Code, service.sentBody) + } +} + +type handlerVerifier struct{} + +func (handlerVerifier) Verify(context.Context, string) (users.VerifiedIdentity, error) { + return users.VerifiedIdentity{Subject: "user_customer"}, nil +} + +type recordingMessagingService struct { + message messaging.Message + sentBody string + conversationID uuid.UUID +} + +func (s *recordingMessagingService) Start(context.Context, users.VerifiedIdentity, uuid.UUID) (messaging.Conversation, error) { + return messaging.Conversation{}, nil +} +func (s *recordingMessagingService) List(context.Context, users.VerifiedIdentity) ([]messaging.Conversation, error) { + return nil, nil +} +func (s *recordingMessagingService) ListMessages(context.Context, users.VerifiedIdentity, uuid.UUID) ([]messaging.Message, error) { + return nil, nil +} +func (s *recordingMessagingService) Send(_ context.Context, _ users.VerifiedIdentity, id uuid.UUID, body string) (messaging.Message, error) { + s.conversationID, s.sentBody = id, body + return s.message, nil +} +func (s *recordingMessagingService) SetBlocked(context.Context, users.VerifiedIdentity, uuid.UUID, bool) error { + return nil +} +func (s *recordingMessagingService) Report(context.Context, users.VerifiedIdentity, uuid.UUID, *uuid.UUID, string) error { + return nil +} +func (s *recordingMessagingService) Preferences(context.Context, users.VerifiedIdentity) (messaging.NotificationPreferences, error) { + return messaging.NotificationPreferences{}, nil +} +func (s *recordingMessagingService) ReplacePreferences(context.Context, users.VerifiedIdentity, messaging.NotificationPreferences) (messaging.NotificationPreferences, error) { + return messaging.NotificationPreferences{}, nil +} +func (s *recordingMessagingService) Notifications(context.Context, users.VerifiedIdentity) ([]messaging.Notification, error) { + return nil, nil +} +func (s *recordingMessagingService) MarkNotificationRead(context.Context, users.VerifiedIdentity, uuid.UUID) error { + return nil +} diff --git a/backend/internal/httpapi/moderation_listing_handler.go b/backend/internal/httpapi/moderation_listing_handler.go new file mode 100644 index 0000000..a28d36f --- /dev/null +++ b/backend/internal/httpapi/moderation_listing_handler.go @@ -0,0 +1,118 @@ +package httpapi + +import ( + "context" + "encoding/json" + "errors" + "io" + "net/http" + "strings" + + "github.com/SourceSenseiTheRealOne/juntly/backend/internal/authn" + "github.com/SourceSenseiTheRealOne/juntly/backend/internal/listings" + "github.com/SourceSenseiTheRealOne/juntly/backend/internal/moderation" + "github.com/SourceSenseiTheRealOne/juntly/backend/internal/users" + "github.com/google/uuid" +) + +type ModerationListingService interface { + ListPending(context.Context, users.VerifiedIdentity) ([]listings.Listing, error) + Approve(context.Context, users.VerifiedIdentity, uuid.UUID, int) (listings.Listing, error) + Reject(context.Context, users.VerifiedIdentity, uuid.UUID, int, string) (listings.Listing, error) +} +type moderationListingHandler struct{ service ModerationListingService } + +func NewModerationListingHandler(service ModerationListingService) http.Handler { + return moderationListingHandler{service: service} +} +func (h moderationListingHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { + id := requestIDFromHeader(r.Header.Get(RequestIDHeader)) + identity, ok := authn.IdentityFromContext(r.Context()) + if !ok { + writeAPIError(w, 401, "UNAUTHORIZED", "Unauthorized", id) + return + } + if h.service == nil { + writeAPIError(w, 503, "SERVICE_UNAVAILABLE", "Service unavailable", id) + return + } + suffix := strings.TrimPrefix(r.URL.Path, "/api/v1/moderation/listings") + if suffix == "" || suffix == "/" { + if r.Method != http.MethodGet { + w.Header().Set("Allow", "GET") + http.Error(w, http.StatusText(405), 405) + return + } + values, err := h.service.ListPending(r.Context(), identity) + if err != nil { + writeModerationError(w, err, id) + return + } + out := make([]listingResponse, len(values)) + for i, v := range values { + out[i] = listingResponseFrom(v) + } + writeJSON(w, 200, listingsResponse{Listings: out}, id) + return + } + parts := strings.Split(strings.Trim(suffix, "/"), "/") + if len(parts) != 2 { + writeAPIError(w, 400, "INVALID_REQUEST", "Invalid request", id) + return + } + listingID, err := uuid.Parse(parts[0]) + if err != nil || r.Method != http.MethodPost { + writeAPIError(w, 400, "INVALID_REQUEST", "Invalid request", id) + return + } + switch parts[1] { + case "approve": + revision, ok := decodeRevision(r.Body) + if !ok { + writeAPIError(w, 400, "INVALID_REQUEST", "Invalid request", id) + return + } + value, err := h.service.Approve(r.Context(), identity, listingID, revision) + if err != nil { + writeModerationError(w, err, id) + return + } + writeJSON(w, 200, listingResponseFrom(value), id) + case "reject": + d := json.NewDecoder(io.LimitReader(r.Body, maxListingRequestBytes+1)) + d.DisallowUnknownFields() + var value struct { + Revision *int `json:"revision"` + Reason *string `json:"reason"` + } + if err := d.Decode(&value); err != nil || value.Revision == nil || value.Reason == nil || *value.Revision < 1 { + writeAPIError(w, 400, "INVALID_REQUEST", "Invalid request", id) + return + } + var extra any + if err := d.Decode(&extra); !errors.Is(err, io.EOF) { + writeAPIError(w, 400, "INVALID_REQUEST", "Invalid request", id) + return + } + listing, err := h.service.Reject(r.Context(), identity, listingID, *value.Revision, *value.Reason) + if err != nil { + writeModerationError(w, err, id) + return + } + writeJSON(w, 200, listingResponseFrom(listing), id) + default: + writeAPIError(w, 400, "INVALID_REQUEST", "Invalid request", id) + } +} +func writeModerationError(w http.ResponseWriter, err error, id string) { + switch { + case errors.Is(err, moderation.ErrUnauthorized): + writeAPIError(w, 401, "UNAUTHORIZED", "Unauthorized", id) + case errors.Is(err, moderation.ErrForbidden): + writeAPIError(w, 403, "FORBIDDEN", "Forbidden", id) + case errors.Is(err, moderation.ErrUnavailable): + writeAPIError(w, 503, "SERVICE_UNAVAILABLE", "Service unavailable", id) + default: + writeListingError(w, err, id) + } +} diff --git a/backend/internal/httpapi/moderation_listing_handler_test.go b/backend/internal/httpapi/moderation_listing_handler_test.go new file mode 100644 index 0000000..3b01179 --- /dev/null +++ b/backend/internal/httpapi/moderation_listing_handler_test.go @@ -0,0 +1,58 @@ +package httpapi_test + +import ( + "context" + "net/http" + "net/http/httptest" + "strings" + "testing" + + "github.com/SourceSenseiTheRealOne/juntly/backend/internal/authn" + "github.com/SourceSenseiTheRealOne/juntly/backend/internal/httpapi" + "github.com/SourceSenseiTheRealOne/juntly/backend/internal/listings" + "github.com/SourceSenseiTheRealOne/juntly/backend/internal/users" + "github.com/google/uuid" +) + +func TestModerationListingHandlerRequiresIdentityAndRoutesReview(t *testing.T) { + t.Parallel() + service := &recordingModerationReview{listing: sampleListing()} + handler := httpapi.NewModerationListingHandler(service) + unauthorized := httptest.NewRecorder() + handler.ServeHTTP(unauthorized, httptest.NewRequest(http.MethodGet, "/api/v1/moderation/listings", nil)) + if unauthorized.Code != http.StatusUnauthorized || service.calls != 0 { + t.Fatalf("unauthorized=%d/%d", unauthorized.Code, service.calls) + } + + for path, body := range map[string]string{"/api/v1/moderation/listings": "", "/api/v1/moderation/listings/aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa/approve": `{"revision":1}`, "/api/v1/moderation/listings/aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa/reject": `{"revision":1,"reason":"Needs clearer scope"}`} { + response := httptest.NewRecorder() + request := httptest.NewRequest(http.MethodGet, path, strings.NewReader(body)) + if body != "" { + request.Method = http.MethodPost + } + request.Header.Set("Authorization", "Bearer synthetic-token") + request.Header.Set(httpapi.RequestIDHeader, "req_moderation") + authn.RequireVerifiedIdentity(staticVerifier{identity: users.VerifiedIdentity{Subject: "moderator"}}, handler).ServeHTTP(response, request) + if response.Code != http.StatusOK || strings.Contains(response.Body.String(), "internalUserId") { + t.Fatalf("%s=%d/%s", path, response.Code, response.Body.String()) + } + } +} + +type recordingModerationReview struct { + listing listings.Listing + calls int +} + +func (s *recordingModerationReview) ListPending(context.Context, users.VerifiedIdentity) ([]listings.Listing, error) { + s.calls++ + return []listings.Listing{s.listing}, nil +} +func (s *recordingModerationReview) Approve(context.Context, users.VerifiedIdentity, uuid.UUID, int) (listings.Listing, error) { + s.calls++ + return s.listing, nil +} +func (s *recordingModerationReview) Reject(context.Context, users.VerifiedIdentity, uuid.UUID, int, string) (listings.Listing, error) { + s.calls++ + return s.listing, nil +} diff --git a/backend/internal/httpapi/openapi_contract_test.go b/backend/internal/httpapi/openapi_contract_test.go new file mode 100644 index 0000000..0c64fcc --- /dev/null +++ b/backend/internal/httpapi/openapi_contract_test.go @@ -0,0 +1,154 @@ +package httpapi_test + +import ( + "os" + "strings" + "testing" +) + +func TestReconcileOpenAPIContract(t *testing.T) { + t.Parallel() + + contract, err := os.ReadFile("../../../openapi/juntly-api.v1.yaml") + if err != nil { + t.Fatalf("read OpenAPI contract: %v", err) + } + contents := string(contract) + + for _, required := range []string{ + "/api/v1/auth/reconcile:", + "post:", + "operationId: reconcileInternalUser", + "clerkSession: []", + "InternalUserResponse:", + "format: uuid", + "createdAt:", + "format: date-time", + "UNAUTHORIZED", + "SERVICE_UNAVAILABLE", + } { + if !strings.Contains(contents, required) { + t.Fatalf("OpenAPI contract does not contain %q", required) + } + } + + operation := openAPIPathBlock(t, contents, "/api/v1/auth/reconcile:") + if strings.Contains(operation, "requestBody:") { + t.Fatal("reconciliation operation must not accept a request body") + } +} + +func openAPIPathBlock(t *testing.T, contents, path string) string { + t.Helper() + start := strings.Index(contents, path) + if start < 0 { + t.Fatalf("OpenAPI contract does not contain path %q", path) + } + remainder := contents[start:] + end := len(remainder) + for _, boundary := range []string{"\n /api/", "\ncomponents:"} { + if index := strings.Index(remainder[1:], boundary); index >= 0 && index+1 < end { + end = index + 1 + } + } + return remainder[:end] +} + +func TestAccountCapabilitiesOpenAPIContract(t *testing.T) { + t.Parallel() + + contract, err := os.ReadFile("../../../openapi/juntly-api.v1.yaml") + if err != nil { + t.Fatalf("read OpenAPI contract: %v", err) + } + contents := string(contract) + + for _, required := range []string{ + "/api/v1/me/account:", + "operationId: getAccountCapabilities", + "operationId: updateAccountCapabilities", + "AccountCapabilitiesResponse:", + "UpdateAccountCapabilitiesRequest:", + "customerEnabled:", + "const: true", + "providerEnabled:", + "onboardingCompletedAt:", + "INVALID_REQUEST", + "clerkSession: []", + } { + if !strings.Contains(contents, required) { + t.Fatalf("OpenAPI contract does not contain %q", required) + } + } + + accountPath := contents[strings.Index(contents, "/api/v1/me/account:"):] + if !strings.Contains(accountPath, "get:") || !strings.Contains(accountPath, "put:") { + t.Fatal("account contract must declare GET and PUT") + } +} + +func TestTaxonomyLocationsProviderProfileOpenAPIContract(t *testing.T) { + t.Parallel() + + contract, err := os.ReadFile("../../../openapi/juntly-api.v1.yaml") + if err != nil { + t.Fatalf("read OpenAPI contract: %v", err) + } + contents := string(contract) + for _, required := range []string{ + "/api/v1/catalog/categories:", + "operationId: listServiceCategories", + "/api/v1/reference/languages:", + "operationId: listSpokenLanguages", + "/api/v1/reference/localities:", + "operationId: listLocalities", + "nearLocalityId", + "radiusKm", + "/api/v1/me/provider-profile:", + "operationId: getProviderProfile", + "operationId: replaceProviderProfile", + "ProviderProfileResponse:", + "ReplaceProviderProfileRequest:", + "serviceLocalityIds:", + "languageCodes:", + "FORBIDDEN", + "OpenStreetMap contributors", + } { + if !strings.Contains(contents, required) { + t.Fatalf("OpenAPI contract does not contain %q", required) + } + } + for _, prohibited := range []string{"phoneNumber:", "whatsapp:", "exactAddress:", "latitude:", "longitude:", "internalUserId:"} { + if strings.Contains(contents, prohibited) { + t.Fatalf("OpenAPI contract must not contain %q", prohibited) + } + } +} + +func TestListingsModerationMediaOpenAPIContract(t *testing.T) { + t.Parallel() + contract, err := os.ReadFile("../../../openapi/juntly-api.v1.yaml") + if err != nil { + t.Fatalf("read OpenAPI contract: %v", err) + } + contents := string(contract) + for _, required := range []string{ + "/api/v1/me/listings:", "operationId: listMyListings", "operationId: createListing", + "/api/v1/me/listings/{listingId}:", "operationId: getMyListing", "operationId: replaceMyDraftListing", + "/api/v1/me/listings/{listingId}/submit:", "operationId: submitListingForReview", + "/api/v1/me/listings/{listingId}/pause:", "operationId: pauseListing", + "/api/v1/me/listings/{listingId}/archive:", "operationId: archiveListing", + "/api/v1/me/listings/{listingId}/media/upload-intents:", "operationId: createListingMediaUploadIntent", + "/api/v1/moderation/listings:", "operationId: listPendingModerationListings", "operationId: approveListing", "operationId: rejectListing", + "ListingResponse:", "CreateListingRequest:", "UploadIntentResponse:", "CONFLICT", "clerkSession: []", + } { + if !strings.Contains(contents, required) { + t.Fatalf("OpenAPI contract does not contain %q", required) + } + } + for _, prohibited := range []string{"phoneNumber:", "whatsapp:", "exactAddress:", "objectReference:", "accessKey:", "bucket:"} { + if strings.Contains(contents, prohibited) { + t.Fatalf("OpenAPI contract must not contain %q", prohibited) + } + } +} diff --git a/backend/internal/httpapi/payment_handler.go b/backend/internal/httpapi/payment_handler.go new file mode 100644 index 0000000..84daa88 --- /dev/null +++ b/backend/internal/httpapi/payment_handler.go @@ -0,0 +1,192 @@ +package httpapi + +import ( + "context" + "encoding/json" + "errors" + "io" + "net/http" + "strings" + + "github.com/SourceSenseiTheRealOne/juntly/backend/internal/authn" + "github.com/SourceSenseiTheRealOne/juntly/backend/internal/payments" + "github.com/SourceSenseiTheRealOne/juntly/backend/internal/users" + "github.com/google/uuid" +) + +const maxPaymentRequestBytes = 256 * 1024 + +type PaymentService interface { + BeginCheckout(context.Context, users.VerifiedIdentity, uuid.UUID, string, string) (payments.CheckoutResult, error) + ListOrders(context.Context, users.VerifiedIdentity) ([]payments.Order, error) + ListAdminOrders(context.Context, users.VerifiedIdentity) ([]payments.Order, error) + BeginPayoutOnboarding(context.Context, users.VerifiedIdentity, string) (payments.PayoutOnboardingResult, error) + PayoutStatus(context.Context, users.VerifiedIdentity) (payments.ProviderAccount, error) + HandleWebhook(context.Context, []byte, string) error + Refund(context.Context, users.VerifiedIdentity, uuid.UUID, string) (payments.Order, error) +} + +type paymentHandler struct{ service PaymentService } + +type stripeWebhookHandler struct{ service PaymentService } + +func NewPaymentHandler(service PaymentService) http.Handler { return paymentHandler{service: service} } +func NewStripeWebhookHandler(service PaymentService) http.Handler { + return stripeWebhookHandler{service: service} +} + +func (h paymentHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { + requestID := requestIDFromHeader(r.Header.Get(RequestIDHeader)) + identity, ok := authn.IdentityFromContext(r.Context()) + if !ok { + writeAPIError(w, 401, "UNAUTHORIZED", "Unauthorized", requestID) + return + } + if h.service == nil { + writeAPIError(w, 503, "SERVICE_UNAVAILABLE", "Service unavailable", requestID) + return + } + path := strings.TrimSuffix(r.URL.Path, "/") + switch { + case path == "/api/v1/me/payments" && r.Method == http.MethodGet: + orders, err := h.service.ListOrders(r.Context(), identity) + if err != nil { + writePaymentError(w, err, requestID) + return + } + writeJSON(w, 200, map[string]any{"orders": orders}, requestID) + case path == "/api/v1/admin/payments" && r.Method == http.MethodGet: + orders, err := h.service.ListAdminOrders(r.Context(), identity) + if err != nil { + writePaymentError(w, err, requestID) + return + } + writeJSON(w, 200, map[string]any{"orders": orders}, requestID) + case path == "/api/v1/me/payout-account" && r.Method == http.MethodGet: + account, err := h.service.PayoutStatus(r.Context(), identity) + if err != nil { + writePaymentError(w, err, requestID) + return + } + writeJSON(w, 200, account, requestID) + case path == "/api/v1/me/payout-account" && r.Method == http.MethodPost: + var body struct { + Locale *string `json:"locale"` + } + if !decodePayment(r.Body, &body) || body.Locale == nil { + writeAPIError(w, 400, "INVALID_REQUEST", "Invalid request", requestID) + return + } + result, err := h.service.BeginPayoutOnboarding(r.Context(), identity, *body.Locale) + if err != nil { + writePaymentError(w, err, requestID) + return + } + writeJSON(w, 200, result, requestID) + case strings.HasPrefix(path, "/api/v1/me/bookings/") && strings.HasSuffix(path, "/checkout") && r.Method == http.MethodPost: + id, ok := paymentPathID(path, "/api/v1/me/bookings/", "/checkout") + if !ok { + writeAPIError(w, 400, "INVALID_REQUEST", "Invalid request", requestID) + return + } + var body struct { + IdempotencyKey *string `json:"idempotencyKey"` + Locale *string `json:"locale"` + } + if !decodePayment(r.Body, &body) || body.IdempotencyKey == nil || body.Locale == nil { + writeAPIError(w, 400, "INVALID_REQUEST", "Invalid request", requestID) + return + } + result, err := h.service.BeginCheckout(r.Context(), identity, id, *body.IdempotencyKey, *body.Locale) + if err != nil { + writePaymentError(w, err, requestID) + return + } + writeJSON(w, 201, result, requestID) + case strings.HasPrefix(path, "/api/v1/admin/payments/") && strings.HasSuffix(path, "/refund") && r.Method == http.MethodPost: + id, ok := paymentPathID(path, "/api/v1/admin/payments/", "/refund") + if !ok { + writeAPIError(w, 400, "INVALID_REQUEST", "Invalid request", requestID) + return + } + var body struct { + IdempotencyKey *string `json:"idempotencyKey"` + } + if !decodePayment(r.Body, &body) || body.IdempotencyKey == nil { + writeAPIError(w, 400, "INVALID_REQUEST", "Invalid request", requestID) + return + } + order, err := h.service.Refund(r.Context(), identity, id, *body.IdempotencyKey) + if err != nil { + writePaymentError(w, err, requestID) + return + } + writeJSON(w, 200, order, requestID) + default: + writeAPIError(w, 404, "NOT_FOUND", "Not found", requestID) + } +} + +func (h stripeWebhookHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { + requestID := requestIDFromHeader(r.Header.Get(RequestIDHeader)) + if r.Method != http.MethodPost { + w.Header().Set("Allow", http.MethodPost) + http.Error(w, http.StatusText(405), 405) + return + } + if h.service == nil { + writeAPIError(w, 503, "SERVICE_UNAVAILABLE", "Service unavailable", requestID) + return + } + signature := strings.TrimSpace(r.Header.Get("Stripe-Signature")) + if signature == "" { + writeAPIError(w, 401, "UNAUTHORIZED", "Unauthorized", requestID) + return + } + payload, err := io.ReadAll(io.LimitReader(r.Body, maxPaymentRequestBytes+1)) + if err != nil || len(payload) == 0 || len(payload) > maxPaymentRequestBytes { + writeAPIError(w, 400, "INVALID_REQUEST", "Invalid request", requestID) + return + } + if err := h.service.HandleWebhook(r.Context(), payload, signature); err != nil { + writePaymentError(w, err, requestID) + return + } + writeJSON(w, 200, map[string]bool{"received": true}, requestID) +} + +func paymentPathID(path, prefix, suffix string) (uuid.UUID, bool) { + raw := strings.TrimSuffix(strings.TrimPrefix(path, prefix), suffix) + if strings.Contains(raw, "/") { + return uuid.Nil, false + } + id, err := uuid.Parse(raw) + return id, err == nil +} + +func decodePayment(body io.Reader, target any) bool { + decoder := json.NewDecoder(io.LimitReader(body, 8*1024+1)) + decoder.DisallowUnknownFields() + if decoder.Decode(target) != nil { + return false + } + var extra any + return errors.Is(decoder.Decode(&extra), io.EOF) +} + +func writePaymentError(w http.ResponseWriter, err error, requestID string) { + switch { + case errors.Is(err, payments.ErrInvalid): + writeAPIError(w, 400, "INVALID_REQUEST", "Invalid request", requestID) + case errors.Is(err, payments.ErrUnauthorized): + writeAPIError(w, 401, "UNAUTHORIZED", "Unauthorized", requestID) + case errors.Is(err, payments.ErrForbidden): + writeAPIError(w, 403, "FORBIDDEN", "Forbidden", requestID) + case errors.Is(err, payments.ErrNotFound): + writeAPIError(w, 404, "NOT_FOUND", "Not found", requestID) + case errors.Is(err, payments.ErrConflict): + writeAPIError(w, 409, "CONFLICT", "Conflict", requestID) + default: + writeAPIError(w, 503, "SERVICE_UNAVAILABLE", "Service unavailable", requestID) + } +} diff --git a/backend/internal/httpapi/payment_handler_test.go b/backend/internal/httpapi/payment_handler_test.go new file mode 100644 index 0000000..03b5ca9 --- /dev/null +++ b/backend/internal/httpapi/payment_handler_test.go @@ -0,0 +1,86 @@ +package httpapi_test + +import ( + "context" + "net/http" + "net/http/httptest" + "strings" + "testing" + + "github.com/SourceSenseiTheRealOne/juntly/backend/internal/authn" + "github.com/SourceSenseiTheRealOne/juntly/backend/internal/httpapi" + "github.com/SourceSenseiTheRealOne/juntly/backend/internal/payments" + "github.com/SourceSenseiTheRealOne/juntly/backend/internal/users" + "github.com/google/uuid" +) + +func TestPaymentHandlerRoutesCheckoutWithoutAcceptingMoneyFromBrowser(t *testing.T) { + service := &recordingPaymentService{checkout: payments.CheckoutResult{Order: payments.Order{ID: "aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa", GrossMinor: 12500, PlatformFeeMinor: 1250, ProviderNetMinor: 11250, Currency: "EUR"}, URL: "https://checkout.stripe.test/session"}} + handler := authn.RequireVerifiedIdentity(staticVerifier{identity: users.VerifiedIdentity{Subject: "customer"}}, httpapi.NewPaymentHandler(service)) + request := httptest.NewRequest(http.MethodPost, "/api/v1/me/bookings/bbbbbbbb-bbbb-4bbb-8bbb-bbbbbbbbbbbb/checkout", strings.NewReader(`{"idempotencyKey":"checkout-key-123","locale":"pt-PT"}`)) + request.Header.Set("Authorization", "Bearer synthetic-token") + response := httptest.NewRecorder() + handler.ServeHTTP(response, request) + if response.Code != http.StatusCreated || service.bookingID.String() != "bbbbbbbb-bbbb-4bbb-8bbb-bbbbbbbbbbbb" || service.idempotencyKey != "checkout-key-123" || strings.Contains(response.Body.String(), "stripeAccount") { + t.Fatalf("response=%d/%s service=%#v", response.Code, response.Body.String(), service) + } +} + +func TestStripeWebhookHandlerPassesExactRawBodyAndSignature(t *testing.T) { + service := &recordingPaymentService{} + handler := httpapi.NewStripeWebhookHandler(service) + request := httptest.NewRequest(http.MethodPost, "/api/v1/payments/webhooks/stripe", strings.NewReader(`{"id":"evt_test"}`)) + request.Header.Set("Stripe-Signature", "t=1,v1=synthetic") + response := httptest.NewRecorder() + handler.ServeHTTP(response, request) + if response.Code != http.StatusOK || string(service.payload) != `{"id":"evt_test"}` || service.signature != "t=1,v1=synthetic" { + t.Fatalf("response=%d body=%q signature=%q", response.Code, service.payload, service.signature) + } +} + +func TestPaymentHandlerListsAdministratorPaymentOrders(t *testing.T) { + service := &recordingPaymentService{orders: []payments.Order{{ID: "aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa", State: payments.StateDisputed}}} + handler := authn.RequireVerifiedIdentity(staticVerifier{identity: users.VerifiedIdentity{Subject: "moderator"}}, httpapi.NewPaymentHandler(service)) + request := httptest.NewRequest(http.MethodGet, "/api/v1/admin/payments", nil) + request.Header.Set("Authorization", "Bearer synthetic-token") + response := httptest.NewRecorder() + handler.ServeHTTP(response, request) + if response.Code != http.StatusOK || !strings.Contains(response.Body.String(), "disputed") || service.adminListCalls != 1 { + t.Fatalf("response=%d/%s calls=%d", response.Code, response.Body.String(), service.adminListCalls) + } +} + +type recordingPaymentService struct { + checkout payments.CheckoutResult + bookingID uuid.UUID + idempotencyKey string + payload []byte + signature string + orders []payments.Order + adminListCalls int +} + +func (s *recordingPaymentService) BeginCheckout(_ context.Context, _ users.VerifiedIdentity, bookingID uuid.UUID, key, _ string) (payments.CheckoutResult, error) { + s.bookingID, s.idempotencyKey = bookingID, key + return s.checkout, nil +} +func (s *recordingPaymentService) ListOrders(context.Context, users.VerifiedIdentity) ([]payments.Order, error) { + return nil, nil +} +func (s *recordingPaymentService) ListAdminOrders(context.Context, users.VerifiedIdentity) ([]payments.Order, error) { + s.adminListCalls++ + return s.orders, nil +} +func (s *recordingPaymentService) BeginPayoutOnboarding(context.Context, users.VerifiedIdentity, string) (payments.PayoutOnboardingResult, error) { + return payments.PayoutOnboardingResult{}, nil +} +func (s *recordingPaymentService) PayoutStatus(context.Context, users.VerifiedIdentity) (payments.ProviderAccount, error) { + return payments.ProviderAccount{}, nil +} +func (s *recordingPaymentService) HandleWebhook(_ context.Context, payload []byte, signature string) error { + s.payload, s.signature = append([]byte(nil), payload...), signature + return nil +} +func (s *recordingPaymentService) Refund(context.Context, users.VerifiedIdentity, uuid.UUID, string) (payments.Order, error) { + return payments.Order{}, nil +} diff --git a/backend/internal/httpapi/provider_profile_handler.go b/backend/internal/httpapi/provider_profile_handler.go new file mode 100644 index 0000000..f919819 --- /dev/null +++ b/backend/internal/httpapi/provider_profile_handler.go @@ -0,0 +1,169 @@ +package httpapi + +import ( + "context" + "encoding/json" + "errors" + "io" + "net/http" + "time" + + "github.com/SourceSenseiTheRealOne/juntly/backend/internal/authn" + "github.com/SourceSenseiTheRealOne/juntly/backend/internal/provideraccess" + "github.com/SourceSenseiTheRealOne/juntly/backend/internal/providers" + "github.com/SourceSenseiTheRealOne/juntly/backend/internal/users" + "github.com/google/uuid" +) + +const maxProviderProfileRequestBytes = 16 * 1024 + +type ProviderProfileService interface { + Get(context.Context, users.VerifiedIdentity) (*providers.Profile, error) + Put(context.Context, users.VerifiedIdentity, providers.ReplaceProfile) (providers.Profile, error) +} + +type providerProfileHandler struct{ service ProviderProfileService } + +type providerProfileEnvelope struct { + Profile *providerProfileResponse `json:"profile"` +} + +type providerProfileResponse struct { + DisplayName string `json:"displayName"` + ProviderType providers.ProviderType `json:"providerType"` + Bio string `json:"bio"` + PrimaryLocalityID uuid.UUID `json:"primaryLocalityId"` + ServiceLocalityIDs []uuid.UUID `json:"serviceLocalityIds"` + MaxTravelDistanceKM int `json:"maxTravelDistanceKm"` + TravelsToCustomer bool `json:"travelsToCustomer"` + ReceivesCustomer bool `json:"receivesCustomer"` + RemoteServices bool `json:"remoteServices"` + LanguageCodes []string `json:"languageCodes"` + CreatedAt string `json:"createdAt"` + UpdatedAt string `json:"updatedAt"` +} + +type replaceProviderProfileRequest struct { + DisplayName *string `json:"displayName"` + ProviderType *string `json:"providerType"` + Bio *string `json:"bio"` + PrimaryLocalityID *uuid.UUID `json:"primaryLocalityId"` + ServiceLocalityIDs *[]uuid.UUID `json:"serviceLocalityIds"` + MaxTravelDistanceKM *int `json:"maxTravelDistanceKm"` + TravelsToCustomer *bool `json:"travelsToCustomer"` + ReceivesCustomer *bool `json:"receivesCustomer"` + RemoteServices *bool `json:"remoteServices"` + LanguageCodes *[]string `json:"languageCodes"` +} + +func NewProviderProfileHandler(service ProviderProfileService) http.Handler { + return providerProfileHandler{service: service} +} + +func (h providerProfileHandler) ServeHTTP(w http.ResponseWriter, request *http.Request) { + if request.Method != http.MethodGet && request.Method != http.MethodPut { + w.Header().Set("Allow", "GET, PUT") + http.Error(w, http.StatusText(http.StatusMethodNotAllowed), http.StatusMethodNotAllowed) + return + } + requestID := requestIDFromHeader(request.Header.Get(RequestIDHeader)) + identity, ok := authn.IdentityFromContext(request.Context()) + if !ok { + writeAPIError(w, http.StatusUnauthorized, "UNAUTHORIZED", "Unauthorized", requestID) + return + } + if h.service == nil { + writeAPIError(w, http.StatusServiceUnavailable, "SERVICE_UNAVAILABLE", "Service unavailable", requestID) + return + } + + if request.Method == http.MethodGet { + profile, err := h.service.Get(request.Context(), identity) + if err != nil { + writeProviderProfileError(w, err, requestID) + return + } + var response *providerProfileResponse + if profile != nil { + value := providerProfileResponseFromDomain(*profile) + response = &value + } + writeJSON(w, http.StatusOK, providerProfileEnvelope{Profile: response}, requestID) + return + } + + input, valid := decodeProviderProfileRequest(request.Body) + if !valid { + writeAPIError(w, http.StatusBadRequest, "INVALID_REQUEST", "Invalid request", requestID) + return + } + profile, err := h.service.Put(request.Context(), identity, input) + if err != nil { + writeProviderProfileError(w, err, requestID) + return + } + response := providerProfileResponseFromDomain(profile) + writeJSON(w, http.StatusOK, providerProfileEnvelope{Profile: &response}, requestID) +} + +func decodeProviderProfileRequest(body io.Reader) (providers.ReplaceProfile, bool) { + decoder := json.NewDecoder(io.LimitReader(body, maxProviderProfileRequestBytes+1)) + decoder.DisallowUnknownFields() + var value replaceProviderProfileRequest + if err := decoder.Decode(&value); err != nil || !completeProviderProfileRequest(value) { + return providers.ReplaceProfile{}, false + } + var extra any + if err := decoder.Decode(&extra); !errors.Is(err, io.EOF) { + return providers.ReplaceProfile{}, false + } + return providers.ReplaceProfile{ + DisplayName: *value.DisplayName, + ProviderType: providers.ProviderType(*value.ProviderType), + Bio: *value.Bio, + PrimaryLocalityID: *value.PrimaryLocalityID, + ServiceLocalityIDs: append([]uuid.UUID(nil), (*value.ServiceLocalityIDs)...), + MaxTravelDistanceKM: *value.MaxTravelDistanceKM, + TravelsToCustomer: *value.TravelsToCustomer, + ReceivesCustomer: *value.ReceivesCustomer, + RemoteServices: *value.RemoteServices, + LanguageCodes: append([]string(nil), (*value.LanguageCodes)...), + }, true +} + +func completeProviderProfileRequest(value replaceProviderProfileRequest) bool { + return value.DisplayName != nil && value.ProviderType != nil && value.Bio != nil && + value.PrimaryLocalityID != nil && value.ServiceLocalityIDs != nil && + value.MaxTravelDistanceKM != nil && value.TravelsToCustomer != nil && + value.ReceivesCustomer != nil && value.RemoteServices != nil && value.LanguageCodes != nil +} + +func providerProfileResponseFromDomain(profile providers.Profile) providerProfileResponse { + return providerProfileResponse{ + DisplayName: profile.DisplayName, + ProviderType: profile.ProviderType, + Bio: profile.Bio, + PrimaryLocalityID: profile.PrimaryLocalityID, + ServiceLocalityIDs: append([]uuid.UUID(nil), profile.ServiceLocalityIDs...), + MaxTravelDistanceKM: profile.MaxTravelDistanceKM, + TravelsToCustomer: profile.TravelsToCustomer, + ReceivesCustomer: profile.ReceivesCustomer, + RemoteServices: profile.RemoteServices, + LanguageCodes: append([]string(nil), profile.LanguageCodes...), + CreatedAt: profile.CreatedAt.UTC().Format(time.RFC3339Nano), + UpdatedAt: profile.UpdatedAt.UTC().Format(time.RFC3339Nano), + } +} + +func writeProviderProfileError(w http.ResponseWriter, err error, requestID string) { + switch { + case errors.Is(err, provideraccess.ErrUnauthorized): + writeAPIError(w, http.StatusUnauthorized, "UNAUTHORIZED", "Unauthorized", requestID) + case errors.Is(err, provideraccess.ErrForbidden): + writeAPIError(w, http.StatusForbidden, "FORBIDDEN", "Forbidden", requestID) + case errors.Is(err, providers.ErrInvalidProfile): + writeAPIError(w, http.StatusBadRequest, "INVALID_REQUEST", "Invalid request", requestID) + default: + writeAPIError(w, http.StatusServiceUnavailable, "SERVICE_UNAVAILABLE", "Service unavailable", requestID) + } +} diff --git a/backend/internal/httpapi/provider_profile_handler_test.go b/backend/internal/httpapi/provider_profile_handler_test.go new file mode 100644 index 0000000..4a19e1b --- /dev/null +++ b/backend/internal/httpapi/provider_profile_handler_test.go @@ -0,0 +1,159 @@ +package httpapi_test + +import ( + "context" + "encoding/json" + "net/http" + "net/http/httptest" + "strings" + "testing" + "time" + + "github.com/SourceSenseiTheRealOne/juntly/backend/internal/authn" + "github.com/SourceSenseiTheRealOne/juntly/backend/internal/httpapi" + "github.com/SourceSenseiTheRealOne/juntly/backend/internal/provideraccess" + "github.com/SourceSenseiTheRealOne/juntly/backend/internal/providers" + "github.com/SourceSenseiTheRealOne/juntly/backend/internal/users" + "github.com/google/uuid" +) + +func TestProviderProfileHandlerRequiresVerifiedIdentity(t *testing.T) { + t.Parallel() + + service := &recordingProviderProfileService{} + response := httptest.NewRecorder() + request := httptest.NewRequest(http.MethodGet, "/api/v1/me/provider-profile", nil) + request.Header.Set(httpapi.RequestIDHeader, "req_provider_unauthorized") + + httpapi.NewProviderProfileHandler(service).ServeHTTP(response, request) + + if response.Code != http.StatusUnauthorized || service.calls != 0 { + t.Fatalf("status/calls = %d/%d, want 401/0", response.Code, service.calls) + } + assertErrorResponse(t, response, "UNAUTHORIZED", "Unauthorized", "req_provider_unauthorized") +} + +func TestProviderProfileHandlerReturnsNullableAndClosedOwnerProfile(t *testing.T) { + t.Parallel() + + for name, profile := range map[string]*providers.Profile{ + "missing": nil, + "existing": { + DisplayName: "Prestador local", + ProviderType: providers.ProviderTypeProfessional, + Bio: "Serviço de confiança.", + PrimaryLocalityID: uuid.MustParse("11111111-1111-4111-8111-111111111111"), + ServiceLocalityIDs: []uuid.UUID{uuid.MustParse("11111111-1111-4111-8111-111111111111")}, + MaxTravelDistanceKM: 25, + TravelsToCustomer: true, + LanguageCodes: []string{"pt-PT"}, + CreatedAt: time.Date(2026, 8, 23, 16, 0, 0, 0, time.UTC), + UpdatedAt: time.Date(2026, 8, 23, 16, 0, 0, 0, time.UTC), + }, + } { + name, profile := name, profile + t.Run(name, func(t *testing.T) { + t.Parallel() + service := &recordingProviderProfileService{profile: profile} + handler := authn.RequireVerifiedIdentity(staticVerifier{identity: users.VerifiedIdentity{Subject: "user_provider"}}, httpapi.NewProviderProfileHandler(service)) + response := httptest.NewRecorder() + request := httptest.NewRequest(http.MethodGet, "/api/v1/me/provider-profile", nil) + request.Header.Set("Authorization", "Bearer synthetic-token") + request.Header.Set(httpapi.RequestIDHeader, "req_provider_get") + + handler.ServeHTTP(response, request) + + if response.Code != http.StatusOK { + t.Fatalf("status = %d", response.Code) + } + var body map[string]any + if err := json.NewDecoder(response.Body).Decode(&body); err != nil { + t.Fatalf("decode: %v", err) + } + if len(body) != 1 { + t.Fatalf("body fields = %#v", body) + } + serialized := mustJSON(t, body) + for _, prohibited := range []string{"user_provider", "synthetic-token", "internalUserId", "clerk", "phone", "address"} { + if strings.Contains(serialized, prohibited) { + t.Fatalf("response exposes %q: %s", prohibited, serialized) + } + } + }) + } +} + +func TestProviderProfileHandlerPUTStrictlyDecodesReplacement(t *testing.T) { + t.Parallel() + + service := &recordingProviderProfileService{replacement: providers.Profile{DisplayName: "Prestador local", ProviderType: providers.ProviderTypeIndividual, Bio: "", PrimaryLocalityID: uuid.MustParse("11111111-1111-4111-8111-111111111111"), ServiceLocalityIDs: []uuid.UUID{uuid.MustParse("11111111-1111-4111-8111-111111111111")}, MaxTravelDistanceKM: 10, TravelsToCustomer: true, LanguageCodes: []string{"pt-PT"}, CreatedAt: time.Now(), UpdatedAt: time.Now()}} + handler := authn.RequireVerifiedIdentity(staticVerifier{identity: users.VerifiedIdentity{Subject: "user_provider"}}, httpapi.NewProviderProfileHandler(service)) + valid := `{"displayName":"Prestador local","providerType":"individual","bio":"","primaryLocalityId":"11111111-1111-4111-8111-111111111111","serviceLocalityIds":["11111111-1111-4111-8111-111111111111"],"maxTravelDistanceKm":10,"travelsToCustomer":true,"receivesCustomer":false,"remoteServices":false,"languageCodes":["pt-PT"]}` + response := httptest.NewRecorder() + request := httptest.NewRequest(http.MethodPut, "/api/v1/me/provider-profile", strings.NewReader(valid)) + request.Header.Set("Authorization", "Bearer synthetic-token") + request.Header.Set(httpapi.RequestIDHeader, "req_provider_put") + + handler.ServeHTTP(response, request) + + if response.Code != http.StatusOK || service.putCalls != 1 || service.input.DisplayName != "Prestador local" { + t.Fatalf("status/calls/input = %d/%d/%#v", response.Code, service.putCalls, service.input) + } + + for _, body := range []string{`{}`, `null`, valid + `{}`, strings.Replace(valid, `}`, `,"admin":true}`, 1)} { + service := &recordingProviderProfileService{} + handler := authn.RequireVerifiedIdentity(staticVerifier{identity: users.VerifiedIdentity{Subject: "user_provider"}}, httpapi.NewProviderProfileHandler(service)) + response := httptest.NewRecorder() + request := httptest.NewRequest(http.MethodPut, "/api/v1/me/provider-profile", strings.NewReader(body)) + request.Header.Set("Authorization", "Bearer synthetic-token") + request.Header.Set(httpapi.RequestIDHeader, "req_provider_invalid") + handler.ServeHTTP(response, request) + if response.Code != http.StatusBadRequest || service.putCalls != 0 { + t.Fatalf("invalid body status/calls = %d/%d", response.Code, service.putCalls) + } + } +} + +func TestProviderProfileHandlerMapsForbiddenAndUnavailable(t *testing.T) { + t.Parallel() + + for _, test := range []struct { + err error + status int + code string + }{ + {provideraccess.ErrForbidden, http.StatusForbidden, "FORBIDDEN"}, + {provideraccess.ErrUnavailable, http.StatusServiceUnavailable, "SERVICE_UNAVAILABLE"}, + } { + handler := authn.RequireVerifiedIdentity( + staticVerifier{identity: users.VerifiedIdentity{Subject: "user_provider"}}, + httpapi.NewProviderProfileHandler(&recordingProviderProfileService{err: test.err}), + ) + response := httptest.NewRecorder() + request := httptest.NewRequest(http.MethodGet, "/api/v1/me/provider-profile", nil) + request.Header.Set("Authorization", "Bearer synthetic-token") + handler.ServeHTTP(response, request) + if response.Code != test.status || !strings.Contains(response.Body.String(), test.code) { + t.Fatalf("error %v status/body = %d/%s", test.err, response.Code, response.Body.String()) + } + } +} + +type recordingProviderProfileService struct { + profile *providers.Profile + replacement providers.Profile + err error + calls int + putCalls int + input providers.ReplaceProfile +} + +func (s *recordingProviderProfileService) Get(context.Context, users.VerifiedIdentity) (*providers.Profile, error) { + s.calls++ + return s.profile, s.err +} +func (s *recordingProviderProfileService) Put(_ context.Context, _ users.VerifiedIdentity, input providers.ReplaceProfile) (providers.Profile, error) { + s.putCalls++ + s.input = input + return s.replacement, s.err +} diff --git a/backend/internal/httpapi/public_discovery_handler.go b/backend/internal/httpapi/public_discovery_handler.go new file mode 100644 index 0000000..fc3ca41 --- /dev/null +++ b/backend/internal/httpapi/public_discovery_handler.go @@ -0,0 +1,230 @@ +package httpapi + +import ( + "context" + "errors" + "net/http" + "net/url" + "strconv" + "strings" + "time" + + "github.com/SourceSenseiTheRealOne/juntly/backend/internal/discovery" + "github.com/google/uuid" +) + +type PublicDiscoveryService interface { + Search(context.Context, discovery.Request) ([]discovery.Listing, error) + Get(context.Context, string, string) (*discovery.Listing, error) +} + +type publicDiscoveryHandler struct{ service PublicDiscoveryService } +type publicListingHandler struct{ service PublicDiscoveryService } + +type publicListingResponse struct { + ID uuid.UUID `json:"id"` + Title string `json:"title"` + Description string `json:"description"` + CategoryID uuid.UUID `json:"categoryId"` + CategorySlug string `json:"categorySlug"` + CategoryName string `json:"categoryName"` + PrimaryLocalityID uuid.UUID `json:"primaryLocalityId"` + LocalitySlug string `json:"localitySlug"` + LocalityName string `json:"localityName"` + PriceType string `json:"priceType"` + PriceMinor *int `json:"priceMinor"` + Currency string `json:"currency"` + TravelsToCustomer bool `json:"travelsToCustomer"` + ReceivesCustomer bool `json:"receivesCustomer"` + RemoteServices bool `json:"remoteServices"` + ProviderDisplayName string `json:"providerDisplayName"` + ProviderType string `json:"providerType"` + Promoted bool `json:"promoted"` + UpdatedAt string `json:"updatedAt"` +} + +type publicListingsResponse struct { + Listings []publicListingResponse `json:"listings"` +} + +func NewPublicDiscoveryHandler(service PublicDiscoveryService) http.Handler { + return publicDiscoveryHandler{service: service} +} + +func NewPublicListingHandler(service PublicDiscoveryService) http.Handler { + return publicListingHandler{service: service} +} + +func (h publicDiscoveryHandler) ServeHTTP(w http.ResponseWriter, request *http.Request) { + id := requestIDFromHeader(request.Header.Get(RequestIDHeader)) + if request.Method != http.MethodGet { + w.Header().Set("Allow", http.MethodGet) + http.Error(w, http.StatusText(http.StatusMethodNotAllowed), http.StatusMethodNotAllowed) + return + } + if h.service == nil { + writeAPIError(w, http.StatusServiceUnavailable, "SERVICE_UNAVAILABLE", "Service unavailable", id) + return + } + query, ok := publicDiscoveryRequest(request.URL.Query()) + if !ok { + writeAPIError(w, http.StatusBadRequest, "INVALID_REQUEST", "Invalid request", id) + return + } + values, err := h.service.Search(request.Context(), query) + if err != nil { + writePublicDiscoveryError(w, err, id) + return + } + response := publicListingsResponse{Listings: make([]publicListingResponse, len(values))} + for index, value := range values { + response.Listings[index] = publicListingResponseFrom(value) + } + writeJSON(w, http.StatusOK, response, id) +} + +func (h publicListingHandler) ServeHTTP(w http.ResponseWriter, request *http.Request) { + id := requestIDFromHeader(request.Header.Get(RequestIDHeader)) + if request.Method != http.MethodGet { + w.Header().Set("Allow", http.MethodGet) + http.Error(w, http.StatusText(http.StatusMethodNotAllowed), http.StatusMethodNotAllowed) + return + } + if h.service == nil { + writeAPIError(w, http.StatusServiceUnavailable, "SERVICE_UNAVAILABLE", "Service unavailable", id) + return + } + if !exactQueryKeys(request.URL.Query(), "locale") { + writeAPIError(w, http.StatusBadRequest, "INVALID_REQUEST", "Invalid request", id) + return + } + locale, ok := exactQueryValue(request.URL.Query(), "locale") + if !ok || !validReferenceLocale(locale) { + writeAPIError(w, http.StatusBadRequest, "INVALID_REQUEST", "Invalid request", id) + return + } + rawID := strings.TrimPrefix(request.URL.Path, "/api/v1/public/listings/") + if rawID == request.URL.Path || strings.Contains(rawID, "/") { + writePublicNotFound(w, id) + return + } + value, err := h.service.Get(request.Context(), rawID, locale) + if errors.Is(err, discovery.ErrNotFound) || value == nil { + writePublicNotFound(w, id) + return + } + if err != nil { + writePublicDiscoveryError(w, err, id) + return + } + writeJSON(w, http.StatusOK, publicListingResponseFrom(*value), id) +} + +func publicDiscoveryRequest(values url.Values) (discovery.Request, bool) { + if !exactQueryKeys(values, "locale", "categoryId", "q", "nearLocalityId", "radiusKm", "priceType", "serviceMode") { + return discovery.Request{}, false + } + locale, ok := exactQueryValue(values, "locale") + if !ok || !validReferenceLocale(locale) { + return discovery.Request{}, false + } + request := discovery.Request{Locale: locale} + var present bool + if request.CategoryID, present = optionalUUIDQuery(values, "categoryId"); !present { + return discovery.Request{}, false + } + if request.Query, present = optionalExactQueryValue(values, "q"); !present && values.Has("q") { + return discovery.Request{}, false + } + var near string + near, present = optionalExactQueryValue(values, "nearLocalityId") + if !present && values.Has("nearLocalityId") { + return discovery.Request{}, false + } + radius, hasRadius := optionalExactQueryValue(values, "radiusKm") + if !hasRadius && values.Has("radiusKm") { + return discovery.Request{}, false + } + if present != hasRadius { + return discovery.Request{}, false + } + if present { + parsed, err := uuid.Parse(near) + if err != nil { + return discovery.Request{}, false + } + request.NearLocalityID = parsed + request.RadiusKM, err = strconv.Atoi(radius) + if err != nil { + return discovery.Request{}, false + } + } + if request.PriceType, present = optionalPriceTypeQuery(values); !present { + return discovery.Request{}, false + } + if request.ServiceMode, present = optionalServiceModeQuery(values); !present { + return discovery.Request{}, false + } + return request, true +} + +func optionalUUIDQuery(values url.Values, key string) (uuid.UUID, bool) { + value, present := optionalExactQueryValue(values, key) + if !present { + return uuid.Nil, !values.Has(key) + } + parsed, err := uuid.Parse(value) + return parsed, err == nil +} + +func optionalPriceTypeQuery(values url.Values) (discovery.PriceType, bool) { + value, present := optionalExactQueryValue(values, "priceType") + if !present { + return "", !values.Has("priceType") + } + candidate := discovery.PriceType(value) + switch candidate { + case discovery.PriceTypeFixed, discovery.PriceTypeHourly, discovery.PriceTypeDaily, discovery.PriceTypeQuote, discovery.PriceTypeNegotiable: + return candidate, true + default: + return "", false + } +} + +func optionalServiceModeQuery(values url.Values) (discovery.ServiceMode, bool) { + value, present := optionalExactQueryValue(values, "serviceMode") + if !present { + return "", !values.Has("serviceMode") + } + candidate := discovery.ServiceMode(value) + switch candidate { + case discovery.ServiceModeTravelsToCustomer, discovery.ServiceModeReceivesCustomer, discovery.ServiceModeRemoteServices: + return candidate, true + default: + return "", false + } +} + +func publicListingResponseFrom(value discovery.Listing) publicListingResponse { + return publicListingResponse{ + ID: value.ID, Title: value.Title, Description: value.Description, + CategoryID: value.CategoryID, CategorySlug: value.CategorySlug, CategoryName: value.CategoryName, + PrimaryLocalityID: value.PrimaryLocalityID, LocalitySlug: value.LocalitySlug, LocalityName: value.LocalityName, + PriceType: string(value.PriceType), PriceMinor: value.PriceMinor, Currency: value.Currency, + TravelsToCustomer: value.TravelsToCustomer, ReceivesCustomer: value.ReceivesCustomer, RemoteServices: value.RemoteServices, + ProviderDisplayName: value.ProviderDisplayName, ProviderType: value.ProviderType, Promoted: value.Promoted, + UpdatedAt: value.UpdatedAt.UTC().Format(time.RFC3339Nano), + } +} + +func writePublicDiscoveryError(w http.ResponseWriter, err error, id string) { + if errors.Is(err, discovery.ErrInvalidRequest) { + writeAPIError(w, http.StatusBadRequest, "INVALID_REQUEST", "Invalid request", id) + return + } + writeAPIError(w, http.StatusServiceUnavailable, "SERVICE_UNAVAILABLE", "Service unavailable", id) +} + +func writePublicNotFound(w http.ResponseWriter, id string) { + writeAPIError(w, http.StatusNotFound, "NOT_FOUND", "Not found", id) +} diff --git a/backend/internal/httpapi/public_discovery_handler_test.go b/backend/internal/httpapi/public_discovery_handler_test.go new file mode 100644 index 0000000..2c0f0fa --- /dev/null +++ b/backend/internal/httpapi/public_discovery_handler_test.go @@ -0,0 +1,123 @@ +package httpapi + +import ( + "context" + "encoding/json" + "net/http" + "net/http/httptest" + "testing" + "time" + + "github.com/SourceSenseiTheRealOne/juntly/backend/internal/discovery" + "github.com/google/uuid" +) + +func TestPublicDiscoveryHandlerServesClosedActiveProjectionWithoutIdentity(t *testing.T) { + listingID := uuid.MustParse("aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa") + service := &recordingDiscoveryService{values: []discovery.Listing{{ + ID: listingID, Title: "Public plumbing", Description: "Public plumbing listing with enough descriptive text.", + CategoryID: uuid.MustParse("bbbbbbbb-bbbb-4bbb-8bbb-bbbbbbbbbbbb"), CategorySlug: "plumbing", CategoryName: "Canalização", + PrimaryLocalityID: uuid.MustParse("cccccccc-cccc-4ccc-8ccc-cccccccccccc"), LocalitySlug: "zebreira", LocalityName: "Zebreira", + PriceType: discovery.PriceTypeFixed, Currency: "EUR", TravelsToCustomer: true, + ProviderDisplayName: "Public provider", ProviderType: "professional", UpdatedAt: time.Date(2026, 8, 24, 12, 0, 0, 0, time.UTC), + }}} + handler := NewPublicDiscoveryHandler(service) + request := httptest.NewRequest(http.MethodGet, "/api/v1/discovery/listings?locale=pt-PT&q=plumbing", nil) + request.Header.Set(RequestIDHeader, "req_discovery_public") + response := httptest.NewRecorder() + handler.ServeHTTP(response, request) + if response.Code != http.StatusOK || service.searches != 1 || service.request.Locale != "pt-PT" || service.request.Query != "plumbing" { + t.Fatalf("status/service = %d/%#v", response.Code, service) + } + var body map[string]any + if err := json.NewDecoder(response.Body).Decode(&body); err != nil { + t.Fatalf("decode response: %v", err) + } + encoded, _ := json.Marshal(body) + for _, forbidden := range []string{"internalUserId", "clerkSubject", "objectReference", "latitude", "longitude", "bio", "reason"} { + if string(encoded) != "" && containsJSONField(body, forbidden) { + t.Fatalf("public response contains %q: %s", forbidden, encoded) + } + } +} + +func TestPublicDiscoveryHandlerRejectsUnknownOrUnpairedQueries(t *testing.T) { + service := &recordingDiscoveryService{} + handler := NewPublicDiscoveryHandler(service) + for _, target := range []string{ + "/api/v1/discovery/listings?locale=pt-PT&admin=true", + "/api/v1/discovery/listings?locale=pt-PT&radiusKm=25", + "/api/v1/discovery/listings?locale=fr", + } { + response := httptest.NewRecorder() + handler.ServeHTTP(response, httptest.NewRequest(http.MethodGet, target, nil)) + if response.Code != http.StatusBadRequest || service.searches != 0 { + t.Fatalf("target/status/calls = %s/%d/%d", target, response.Code, service.searches) + } + } +} + +func TestPublicListingHandlerReturnsActiveLocalizedDetail(t *testing.T) { + listingID := uuid.MustParse("aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa") + service := &detailDiscoveryService{value: &discovery.Listing{ + ID: listingID, Title: "Public plumbing", Description: "Public plumbing listing with enough descriptive text.", + CategoryID: uuid.MustParse("bbbbbbbb-bbbb-4bbb-8bbb-bbbbbbbbbbbb"), CategorySlug: "plumbing", CategoryName: "Canalização", + PrimaryLocalityID: uuid.MustParse("cccccccc-cccc-4ccc-8ccc-cccccccccccc"), LocalitySlug: "zebreira", LocalityName: "Zebreira", + PriceType: discovery.PriceTypeFixed, Currency: "EUR", TravelsToCustomer: true, + ProviderDisplayName: "Public provider", ProviderType: "professional", UpdatedAt: time.Date(2026, 8, 24, 12, 0, 0, 0, time.UTC), + }} + handler := NewPublicListingHandler(service) + request := httptest.NewRequest(http.MethodGet, "/api/v1/public/listings/aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa?locale=pt-PT", nil) + request.Header.Set(RequestIDHeader, "req_public_detail") + response := httptest.NewRecorder() + handler.ServeHTTP(response, request) + if response.Code != http.StatusOK || service.gets != 1 || service.locale != "pt-PT" { + t.Fatalf("status/service = %d/%#v", response.Code, service) + } +} + +type detailDiscoveryService struct { + value *discovery.Listing + gets int + locale string +} + +func (s *detailDiscoveryService) Search(context.Context, discovery.Request) ([]discovery.Listing, error) { + return nil, nil +} +func (s *detailDiscoveryService) Get(_ context.Context, _ string, locale string) (*discovery.Listing, error) { + s.gets++ + s.locale = locale + return s.value, nil +} + +type recordingDiscoveryService struct { + request discovery.Request + values []discovery.Listing + searches int +} + +func (s *recordingDiscoveryService) Search(_ context.Context, request discovery.Request) ([]discovery.Listing, error) { + s.searches++ + s.request = request + return s.values, nil +} +func (s *recordingDiscoveryService) Get(context.Context, string, string) (*discovery.Listing, error) { + return nil, discovery.ErrNotFound +} + +func containsJSONField(value any, field string) bool { + object, ok := value.(map[string]any) + if !ok { + return false + } + if _, exists := object[field]; exists { + return true + } + for _, nested := range object { + if containsJSONField(nested, field) { + return true + } + } + return false +} diff --git a/backend/internal/httpapi/quotation_handler.go b/backend/internal/httpapi/quotation_handler.go new file mode 100644 index 0000000..7496aab --- /dev/null +++ b/backend/internal/httpapi/quotation_handler.go @@ -0,0 +1,229 @@ +package httpapi + +import ( + "context" + "encoding/json" + "errors" + "io" + "net/http" + "strings" + "time" + + "github.com/SourceSenseiTheRealOne/juntly/backend/internal/authn" + "github.com/SourceSenseiTheRealOne/juntly/backend/internal/quotations" + "github.com/SourceSenseiTheRealOne/juntly/backend/internal/users" + "github.com/google/uuid" +) + +const maxQuotationRequestBytes = 16 * 1024 + +type QuotationService interface { + CreateRequest(context.Context, users.VerifiedIdentity, quotations.CreateRequest) (quotations.Request, error) + ListCustomerRequests(context.Context, users.VerifiedIdentity) ([]quotations.Request, error) + ListOpportunities(context.Context, users.VerifiedIdentity) ([]quotations.Request, error) + SubmitProposal(context.Context, users.VerifiedIdentity, uuid.UUID, quotations.SubmitProposal) (quotations.Proposal, error) + ListProposals(context.Context, users.VerifiedIdentity, uuid.UUID) ([]quotations.Proposal, error) + AcceptProposal(context.Context, users.VerifiedIdentity, uuid.UUID, uuid.UUID) (quotations.Proposal, error) +} +type quotationHandler struct{ service QuotationService } + +func NewQuotationHandler(service QuotationService) http.Handler { + return quotationHandler{service: service} +} + +type quotationRequestBody struct { + Title *string `json:"title"` + Description *string `json:"description"` + CategoryID *uuid.UUID `json:"categoryId"` + LocalityID *uuid.UUID `json:"localityId"` + BudgetMinor *int `json:"budgetMinor"` + ProposalDeadline *time.Time `json:"proposalDeadline"` +} +type proposalBody struct { + PriceMinor *int `json:"priceMinor"` + Message *string `json:"message"` + AvailableAt *time.Time `json:"availableAt"` + EstimatedMinutes *int `json:"estimatedMinutes"` + ExpiresAt *time.Time `json:"expiresAt"` +} +type quotationRequestResponse struct { + ID uuid.UUID `json:"id"` + CustomerID uuid.UUID `json:"customerId"` + Title string `json:"title"` + Description string `json:"description"` + CategoryID uuid.UUID `json:"categoryId"` + LocalityID uuid.UUID `json:"localityId"` + BudgetMinor *int `json:"budgetMinor"` + ProposalDeadline string `json:"proposalDeadline"` + State quotations.RequestState `json:"state"` + CreatedAt string `json:"createdAt"` + UpdatedAt string `json:"updatedAt"` +} +type proposalResponse struct { + ID uuid.UUID `json:"id"` + RequestID uuid.UUID `json:"requestId"` + ProviderID uuid.UUID `json:"providerId"` + PriceMinor int `json:"priceMinor"` + Message string `json:"message"` + AvailableAt string `json:"availableAt"` + EstimatedMinutes *int `json:"estimatedMinutes"` + ExpiresAt *string `json:"expiresAt"` + State quotations.ProposalState `json:"state"` + CreatedAt string `json:"createdAt"` + UpdatedAt string `json:"updatedAt"` +} + +func (h quotationHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { + id := requestIDFromHeader(r.Header.Get(RequestIDHeader)) + identity, ok := authn.IdentityFromContext(r.Context()) + if !ok { + writeAPIError(w, 401, "UNAUTHORIZED", "Unauthorized", id) + return + } + if h.service == nil { + writeAPIError(w, 503, "SERVICE_UNAVAILABLE", "Service unavailable", id) + return + } + path := strings.TrimSuffix(r.URL.Path, "/") + if path == "/api/v1/me/quotation-opportunities" { + if r.Method != http.MethodGet { + http.Error(w, http.StatusText(405), 405) + return + } + values, err := h.service.ListOpportunities(r.Context(), identity) + if err != nil { + writeQuotationError(w, err, id) + return + } + writeJSON(w, 200, map[string]any{"requests": requestResponses(values)}, id) + return + } + base := "/api/v1/me/quotation-requests" + if path == base { + switch r.Method { + case http.MethodGet: + values, err := h.service.ListCustomerRequests(r.Context(), identity) + if err != nil { + writeQuotationError(w, err, id) + return + } + writeJSON(w, 200, map[string]any{"requests": requestResponses(values)}, id) + case http.MethodPost: + var body quotationRequestBody + if !decodeQuotation(r.Body, &body) || body.Title == nil || body.Description == nil || body.CategoryID == nil || body.LocalityID == nil || body.ProposalDeadline == nil { + writeAPIError(w, 400, "INVALID_REQUEST", "Invalid request", id) + return + } + value, err := h.service.CreateRequest(r.Context(), identity, quotations.CreateRequest{Title: *body.Title, Description: *body.Description, CategoryID: *body.CategoryID, LocalityID: *body.LocalityID, BudgetMinor: body.BudgetMinor, ProposalDeadline: *body.ProposalDeadline}) + if err != nil { + writeQuotationError(w, err, id) + return + } + writeJSON(w, 201, requestResponse(value), id) + default: + http.Error(w, http.StatusText(405), 405) + } + return + } + parts := strings.Split(strings.TrimPrefix(path, base+"/"), "/") + if len(parts) < 2 || parts[1] != "proposals" { + writeAPIError(w, 404, "NOT_FOUND", "Not found", id) + return + } + requestID, err := uuid.Parse(parts[0]) + if err != nil { + writeAPIError(w, 400, "INVALID_REQUEST", "Invalid request", id) + return + } + if len(parts) == 2 { + switch r.Method { + case http.MethodGet: + values, err := h.service.ListProposals(r.Context(), identity, requestID) + if err != nil { + writeQuotationError(w, err, id) + return + } + writeJSON(w, 200, map[string]any{"proposals": proposalResponses(values)}, id) + case http.MethodPost: + var body proposalBody + if !decodeQuotation(r.Body, &body) || body.PriceMinor == nil || body.Message == nil || body.AvailableAt == nil { + writeAPIError(w, 400, "INVALID_REQUEST", "Invalid request", id) + return + } + value, err := h.service.SubmitProposal(r.Context(), identity, requestID, quotations.SubmitProposal{PriceMinor: *body.PriceMinor, Message: *body.Message, AvailableAt: *body.AvailableAt, EstimatedMinutes: body.EstimatedMinutes, ExpiresAt: body.ExpiresAt}) + if err != nil { + writeQuotationError(w, err, id) + return + } + writeJSON(w, 201, proposalJSON(value), id) + default: + http.Error(w, http.StatusText(405), 405) + } + return + } + if len(parts) == 4 && parts[3] == "accept" && r.Method == http.MethodPost { + proposalID, err := uuid.Parse(parts[2]) + if err != nil { + writeAPIError(w, 400, "INVALID_REQUEST", "Invalid request", id) + return + } + value, err := h.service.AcceptProposal(r.Context(), identity, requestID, proposalID) + if err != nil { + writeQuotationError(w, err, id) + return + } + writeJSON(w, 200, proposalJSON(value), id) + return + } + writeAPIError(w, 404, "NOT_FOUND", "Not found", id) +} +func decodeQuotation(body io.Reader, target any) bool { + d := json.NewDecoder(io.LimitReader(body, maxQuotationRequestBytes+1)) + d.DisallowUnknownFields() + if d.Decode(target) != nil { + return false + } + var extra any + return errors.Is(d.Decode(&extra), io.EOF) +} +func requestResponse(v quotations.Request) quotationRequestResponse { + return quotationRequestResponse{ID: v.ID, CustomerID: v.CustomerID, Title: v.Title, Description: v.Description, CategoryID: v.CategoryID, LocalityID: v.LocalityID, BudgetMinor: v.BudgetMinor, ProposalDeadline: v.ProposalDeadline.UTC().Format(time.RFC3339Nano), State: v.State, CreatedAt: v.CreatedAt.UTC().Format(time.RFC3339Nano), UpdatedAt: v.UpdatedAt.UTC().Format(time.RFC3339Nano)} +} +func requestResponses(values []quotations.Request) []quotationRequestResponse { + out := make([]quotationRequestResponse, 0, len(values)) + for _, v := range values { + out = append(out, requestResponse(v)) + } + return out +} +func proposalJSON(v quotations.Proposal) proposalResponse { + var expires *string + if v.ExpiresAt != nil { + s := v.ExpiresAt.UTC().Format(time.RFC3339Nano) + expires = &s + } + return proposalResponse{ID: v.ID, RequestID: v.RequestID, ProviderID: v.ProviderID, PriceMinor: v.PriceMinor, Message: v.Message, AvailableAt: v.AvailableAt.UTC().Format(time.RFC3339Nano), EstimatedMinutes: v.EstimatedMinutes, ExpiresAt: expires, State: v.State, CreatedAt: v.CreatedAt.UTC().Format(time.RFC3339Nano), UpdatedAt: v.UpdatedAt.UTC().Format(time.RFC3339Nano)} +} +func proposalResponses(values []quotations.Proposal) []proposalResponse { + out := make([]proposalResponse, 0, len(values)) + for _, v := range values { + out = append(out, proposalJSON(v)) + } + return out +} +func writeQuotationError(w http.ResponseWriter, err error, id string) { + switch { + case errors.Is(err, quotations.ErrInvalid): + writeAPIError(w, 400, "INVALID_REQUEST", "Invalid request", id) + case errors.Is(err, quotations.ErrUnauthorized): + writeAPIError(w, 401, "UNAUTHORIZED", "Unauthorized", id) + case errors.Is(err, quotations.ErrForbidden): + writeAPIError(w, 403, "FORBIDDEN", "Forbidden", id) + case errors.Is(err, quotations.ErrNotFound): + writeAPIError(w, 404, "NOT_FOUND", "Not found", id) + case errors.Is(err, quotations.ErrConflict): + writeAPIError(w, 409, "CONFLICT", "Conflict", id) + default: + writeAPIError(w, 503, "SERVICE_UNAVAILABLE", "Service unavailable", id) + } +} diff --git a/backend/internal/httpapi/readiness_handler.go b/backend/internal/httpapi/readiness_handler.go new file mode 100644 index 0000000..128639b --- /dev/null +++ b/backend/internal/httpapi/readiness_handler.go @@ -0,0 +1,34 @@ +package httpapi + +import ( + "context" + "github.com/SourceSenseiTheRealOne/juntly/backend/internal/readiness" + "net/http" + "time" +) + +type ReadinessService interface { + Check(context.Context) readiness.Result +} +type readinessHandler struct{ service ReadinessService } + +func NewReadinessHandler(s ReadinessService) http.Handler { return readinessHandler{service: s} } +func (h readinessHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { + id := requestIDFromHeader(r.Header.Get(RequestIDHeader)) + if r.Method != http.MethodGet { + http.Error(w, http.StatusText(405), 405) + return + } + if h.service == nil { + writeJSON(w, 503, readiness.Result{Database: "unavailable"}, id) + return + } + ctx, cancel := context.WithTimeout(r.Context(), 2*time.Second) + defer cancel() + result := h.service.Check(ctx) + status := 200 + if !result.Ready { + status = 503 + } + writeJSON(w, status, result, id) +} diff --git a/backend/internal/httpapi/reconcile_handler.go b/backend/internal/httpapi/reconcile_handler.go new file mode 100644 index 0000000..7e1df09 --- /dev/null +++ b/backend/internal/httpapi/reconcile_handler.go @@ -0,0 +1,80 @@ +package httpapi + +import ( + "context" + "encoding/json" + "net/http" + "time" + + "github.com/SourceSenseiTheRealOne/juntly/backend/internal/authn" + "github.com/SourceSenseiTheRealOne/juntly/backend/internal/users" + "github.com/google/uuid" +) + +type ReconcileService interface { + Reconcile(context.Context, users.VerifiedIdentity) (users.InternalUser, bool, error) +} + +type reconcileHandler struct { + service ReconcileService +} + +type internalUserResponse struct { + ID uuid.UUID `json:"id"` + CreatedAt string `json:"createdAt"` +} + +type apiErrorResponse struct { + Error apiErrorDetail `json:"error"` +} + +type apiErrorDetail struct { + Code string `json:"code"` + Message string `json:"message"` + RequestID string `json:"requestId"` +} + +func NewReconcileHandler(service ReconcileService) http.Handler { + return reconcileHandler{service: service} +} + +func (h reconcileHandler) ServeHTTP(w http.ResponseWriter, request *http.Request) { + if request.Method != http.MethodPost { + w.Header().Set("Allow", http.MethodPost) + http.Error(w, http.StatusText(http.StatusMethodNotAllowed), http.StatusMethodNotAllowed) + return + } + + requestID := requestIDFromHeader(request.Header.Get(RequestIDHeader)) + identity, ok := authn.IdentityFromContext(request.Context()) + if !ok || h.service == nil { + writeAPIError(w, http.StatusUnauthorized, "UNAUTHORIZED", "Unauthorized", requestID) + return + } + + user, _, err := h.service.Reconcile(request.Context(), identity) + if err != nil { + writeAPIError(w, http.StatusServiceUnavailable, "SERVICE_UNAVAILABLE", "Service unavailable", requestID) + return + } + + writeJSON(w, http.StatusOK, internalUserResponse{ + ID: user.ID, + CreatedAt: user.CreatedAt.UTC().Format(time.RFC3339Nano), + }, requestID) +} + +func writeAPIError(w http.ResponseWriter, status int, code, message, requestID string) { + writeJSON(w, status, apiErrorResponse{Error: apiErrorDetail{ + Code: code, + Message: message, + RequestID: requestID, + }}, requestID) +} + +func writeJSON(w http.ResponseWriter, status int, body any, requestID string) { + w.Header().Set(RequestIDHeader, requestID) + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(status) + _ = json.NewEncoder(w).Encode(body) +} diff --git a/backend/internal/httpapi/reconcile_handler_test.go b/backend/internal/httpapi/reconcile_handler_test.go new file mode 100644 index 0000000..99a6cda --- /dev/null +++ b/backend/internal/httpapi/reconcile_handler_test.go @@ -0,0 +1,170 @@ +package httpapi_test + +import ( + "context" + "encoding/json" + "errors" + "net/http" + "net/http/httptest" + "strings" + "testing" + "time" + + "github.com/SourceSenseiTheRealOne/juntly/backend/internal/authn" + "github.com/SourceSenseiTheRealOne/juntly/backend/internal/httpapi" + "github.com/SourceSenseiTheRealOne/juntly/backend/internal/users" + "github.com/google/uuid" +) + +func TestReconcileHandlerReturnsUnauthorizedWithoutVerifiedIdentity(t *testing.T) { + t.Parallel() + + service := &recordingReconcileService{} + response := httptest.NewRecorder() + request := httptest.NewRequest(http.MethodPost, "/api/v1/auth/reconcile", nil) + request.Header.Set(httpapi.RequestIDHeader, "req_reconcile_unauthorized") + + httpapi.NewReconcileHandler(service).ServeHTTP(response, request) + + if response.Code != http.StatusUnauthorized { + t.Fatalf("status = %d, want %d", response.Code, http.StatusUnauthorized) + } + if service.calls != 0 { + t.Fatalf("service calls = %d, want 0", service.calls) + } + assertErrorResponse(t, response, "UNAUTHORIZED", "Unauthorized", "req_reconcile_unauthorized") +} + +func TestReconcileHandlerReturnsOpaqueInternalUser(t *testing.T) { + t.Parallel() + + createdAt := time.Date(2026, 8, 21, 12, 0, 0, 123456000, time.UTC) + service := &recordingReconcileService{user: users.InternalUser{ + ID: uuid.MustParse("7b7b7d7e-38f9-4f0c-8a10-0fce9cf6f82b"), + CreatedAt: createdAt, + }} + handler := authn.RequireVerifiedIdentity(staticVerifier{identity: users.VerifiedIdentity{Subject: "user_synthetic"}}, httpapi.NewReconcileHandler(service)) + response := httptest.NewRecorder() + request := httptest.NewRequest(http.MethodPost, "/api/v1/auth/reconcile", nil) + request.Header.Set("Authorization", "Bearer synthetic-token") + request.Header.Set(httpapi.RequestIDHeader, "req_reconcile_success") + + handler.ServeHTTP(response, request) + + if response.Code != http.StatusOK { + t.Fatalf("status = %d, want %d", response.Code, http.StatusOK) + } + if response.Header().Get(httpapi.RequestIDHeader) != "req_reconcile_success" { + t.Fatalf("request ID header = %q", response.Header().Get(httpapi.RequestIDHeader)) + } + if service.identity.Subject != "user_synthetic" { + t.Fatalf("service subject = %q, want user_synthetic", service.identity.Subject) + } + + var body map[string]any + if err := json.NewDecoder(response.Body).Decode(&body); err != nil { + t.Fatalf("decode response: %v", err) + } + if len(body) != 2 { + t.Fatalf("response field count = %d, want 2 (%#v)", len(body), body) + } + if body["id"] != "7b7b7d7e-38f9-4f0c-8a10-0fce9cf6f82b" { + t.Fatalf("id = %#v", body["id"]) + } + if body["createdAt"] != "2026-08-21T12:00:00.123456Z" { + t.Fatalf("createdAt = %#v", body["createdAt"]) + } + serialized := mustJSON(t, body) + if strings.Contains(serialized, "user_synthetic") || strings.Contains(serialized, "synthetic-token") { + t.Fatalf("response leaks identity material: %s", serialized) + } +} + +func TestReconcileHandlerReturnsSafeUnavailableForDependencyFailure(t *testing.T) { + t.Parallel() + + service := &recordingReconcileService{err: errors.New("database at internal-host rejected user_synthetic")} + handler := authn.RequireVerifiedIdentity(staticVerifier{identity: users.VerifiedIdentity{Subject: "user_synthetic"}}, httpapi.NewReconcileHandler(service)) + response := httptest.NewRecorder() + request := httptest.NewRequest(http.MethodPost, "/api/v1/auth/reconcile", nil) + request.Header.Set("Authorization", "Bearer synthetic-token") + request.Header.Set(httpapi.RequestIDHeader, "req_reconcile_unavailable") + + handler.ServeHTTP(response, request) + + if response.Code != http.StatusServiceUnavailable { + t.Fatalf("status = %d, want %d", response.Code, http.StatusServiceUnavailable) + } + assertErrorResponse(t, response, "SERVICE_UNAVAILABLE", "Service unavailable", "req_reconcile_unavailable") +} + +func TestReconcileHandlerRejectsNonPOSTMethods(t *testing.T) { + t.Parallel() + + service := &recordingReconcileService{} + response := httptest.NewRecorder() + request := httptest.NewRequest(http.MethodGet, "/api/v1/auth/reconcile", nil) + + httpapi.NewReconcileHandler(service).ServeHTTP(response, request) + + if response.Code != http.StatusMethodNotAllowed { + t.Fatalf("status = %d, want %d", response.Code, http.StatusMethodNotAllowed) + } + if allow := response.Header().Get("Allow"); allow != http.MethodPost { + t.Fatalf("Allow = %q, want %q", allow, http.MethodPost) + } + if service.calls != 0 { + t.Fatalf("service calls = %d, want 0", service.calls) + } +} + +type staticVerifier struct { + identity users.VerifiedIdentity + err error +} + +func (v staticVerifier) Verify(context.Context, string) (users.VerifiedIdentity, error) { + return v.identity, v.err +} + +type recordingReconcileService struct { + user users.InternalUser + err error + calls int + identity users.VerifiedIdentity +} + +func (s *recordingReconcileService) Reconcile(_ context.Context, identity users.VerifiedIdentity) (users.InternalUser, bool, error) { + s.calls++ + s.identity = identity + return s.user, false, s.err +} + +func assertErrorResponse(t *testing.T, response *httptest.ResponseRecorder, code, message, requestID string) { + t.Helper() + + if response.Header().Get(httpapi.RequestIDHeader) != requestID { + t.Fatalf("request ID header = %q, want %q", response.Header().Get(httpapi.RequestIDHeader), requestID) + } + var body map[string]any + if err := json.NewDecoder(response.Body).Decode(&body); err != nil { + t.Fatalf("decode response: %v", err) + } + if mustJSON(t, body) != mustJSON(t, map[string]any{"error": map[string]any{ + "code": code, + "message": message, + "requestId": requestID, + }}) { + t.Fatalf("error body = %#v", body) + } +} + +func mustJSON(t *testing.T, value any) string { + t.Helper() + + encoded, err := json.Marshal(value) + if err != nil { + t.Fatalf("marshal JSON: %v", err) + } + return string(encoded) +} diff --git a/backend/internal/httpapi/reference_handler.go b/backend/internal/httpapi/reference_handler.go new file mode 100644 index 0000000..9d1f482 --- /dev/null +++ b/backend/internal/httpapi/reference_handler.go @@ -0,0 +1,245 @@ +package httpapi + +import ( + "context" + "errors" + "net/http" + "net/url" + "strconv" + + "github.com/SourceSenseiTheRealOne/juntly/backend/internal/reference" + "github.com/google/uuid" +) + +type ReferenceService interface { + Categories(context.Context, string) ([]reference.Category, error) + Languages(context.Context, string) ([]reference.Language, error) + Localities(context.Context, string) ([]reference.Locality, error) + NearbyLocalities(context.Context, uuid.UUID, int, string) ([]reference.LocalityDistance, error) +} + +type referenceKind string + +const ( + referenceCategories referenceKind = "categories" + referenceLanguages referenceKind = "languages" + referenceLocalities referenceKind = "localities" +) + +type referenceHandler struct { + service ReferenceService + kind referenceKind +} + +type categoryResponse struct { + ID uuid.UUID `json:"id"` + ParentID *uuid.UUID `json:"parentId"` + Slug string `json:"slug"` + Name string `json:"name"` +} + +type categoriesResponse struct { + Categories []categoryResponse `json:"categories"` +} + +type languageResponse struct { + Code string `json:"code"` + Name string `json:"name"` +} + +type languagesResponse struct { + Languages []languageResponse `json:"languages"` +} + +type localityResponse struct { + ID uuid.UUID `json:"id"` + Slug string `json:"slug"` + Name string `json:"name"` + ParishName string `json:"parishName"` + MunicipalityName string `json:"municipalityName"` + DistrictName string `json:"districtName"` + DistanceMeters *int `json:"distanceMeters,omitempty"` +} + +type attributionResponse struct { + Text string `json:"text"` + URL string `json:"url"` +} + +type localitiesResponse struct { + Localities []localityResponse `json:"localities"` + Attribution attributionResponse `json:"attribution"` +} + +func NewCategoriesHandler(service ReferenceService) http.Handler { + return referenceHandler{service: service, kind: referenceCategories} +} + +func NewLanguagesHandler(service ReferenceService) http.Handler { + return referenceHandler{service: service, kind: referenceLanguages} +} + +func NewLocalitiesHandler(service ReferenceService) http.Handler { + return referenceHandler{service: service, kind: referenceLocalities} +} + +func (h referenceHandler) ServeHTTP(w http.ResponseWriter, request *http.Request) { + if request.Method != http.MethodGet { + w.Header().Set("Allow", http.MethodGet) + http.Error(w, http.StatusText(http.StatusMethodNotAllowed), http.StatusMethodNotAllowed) + return + } + requestID := requestIDFromHeader(request.Header.Get(RequestIDHeader)) + if h.service == nil { + writeAPIError(w, http.StatusServiceUnavailable, "SERVICE_UNAVAILABLE", "Service unavailable", requestID) + return + } + locale, ok := exactQueryValue(request.URL.Query(), "locale") + if !ok || !validReferenceLocale(locale) { + writeAPIError(w, http.StatusBadRequest, "INVALID_REQUEST", "Invalid request", requestID) + return + } + + switch h.kind { + case referenceCategories: + if !exactQueryKeys(request.URL.Query(), "locale") { + writeAPIError(w, http.StatusBadRequest, "INVALID_REQUEST", "Invalid request", requestID) + return + } + values, err := h.service.Categories(request.Context(), locale) + if err != nil { + writeReferenceError(w, err, requestID) + return + } + response := categoriesResponse{Categories: make([]categoryResponse, len(values))} + for index, value := range values { + response.Categories[index] = categoryResponse{ID: value.ID, ParentID: value.ParentID, Slug: value.Slug, Name: value.Name} + } + writeJSON(w, http.StatusOK, response, requestID) + case referenceLanguages: + if !exactQueryKeys(request.URL.Query(), "locale") { + writeAPIError(w, http.StatusBadRequest, "INVALID_REQUEST", "Invalid request", requestID) + return + } + values, err := h.service.Languages(request.Context(), locale) + if err != nil { + writeReferenceError(w, err, requestID) + return + } + response := languagesResponse{Languages: make([]languageResponse, len(values))} + for index, value := range values { + response.Languages[index] = languageResponse{Code: value.Code, Name: value.Name} + } + writeJSON(w, http.StatusOK, response, requestID) + case referenceLocalities: + h.serveLocalities(w, request, locale, requestID) + default: + writeAPIError(w, http.StatusServiceUnavailable, "SERVICE_UNAVAILABLE", "Service unavailable", requestID) + } +} + +func validReferenceLocale(locale string) bool { + switch locale { + case "pt-PT", "en", "es": + return true + default: + return false + } +} + +func (h referenceHandler) serveLocalities(w http.ResponseWriter, request *http.Request, locale, requestID string) { + query := request.URL.Query() + if !exactQueryKeys(query, "locale", "nearLocalityId", "radiusKm") { + writeAPIError(w, http.StatusBadRequest, "INVALID_REQUEST", "Invalid request", requestID) + return + } + nearValue, hasNear := optionalExactQueryValue(query, "nearLocalityId") + radiusValue, hasRadius := optionalExactQueryValue(query, "radiusKm") + if hasNear != hasRadius { + writeAPIError(w, http.StatusBadRequest, "INVALID_REQUEST", "Invalid request", requestID) + return + } + + response := localitiesResponse{Attribution: attributionResponse{Text: reference.AttributionText, URL: reference.AttributionURL}} + if !hasNear { + values, err := h.service.Localities(request.Context(), locale) + if err != nil { + writeReferenceError(w, err, requestID) + return + } + response.Localities = localityResponses(values) + writeJSON(w, http.StatusOK, response, requestID) + return + } + origin, err := uuid.Parse(nearValue) + if err != nil { + writeAPIError(w, http.StatusBadRequest, "INVALID_REQUEST", "Invalid request", requestID) + return + } + radius, err := strconv.Atoi(radiusValue) + if err != nil { + writeAPIError(w, http.StatusBadRequest, "INVALID_REQUEST", "Invalid request", requestID) + return + } + values, err := h.service.NearbyLocalities(request.Context(), origin, radius, locale) + if err != nil { + writeReferenceError(w, err, requestID) + return + } + response.Localities = make([]localityResponse, len(values)) + for index, value := range values { + distance := value.DistanceMeters + response.Localities[index] = localityResponse{ID: value.ID, Slug: value.Slug, Name: value.Name, ParishName: value.ParishName, MunicipalityName: value.MunicipalityName, DistrictName: value.DistrictName, DistanceMeters: &distance} + } + writeJSON(w, http.StatusOK, response, requestID) +} + +func localityResponses(values []reference.Locality) []localityResponse { + responses := make([]localityResponse, len(values)) + for index, value := range values { + responses[index] = localityResponse{ID: value.ID, Slug: value.Slug, Name: value.Name, ParishName: value.ParishName, MunicipalityName: value.MunicipalityName, DistrictName: value.DistrictName} + } + return responses +} + +func writeReferenceError(w http.ResponseWriter, err error, requestID string) { + if errors.Is(err, reference.ErrInvalidRequest) || errors.Is(err, reference.ErrNotFound) { + writeAPIError(w, http.StatusBadRequest, "INVALID_REQUEST", "Invalid request", requestID) + return + } + writeAPIError(w, http.StatusServiceUnavailable, "SERVICE_UNAVAILABLE", "Service unavailable", requestID) +} + +func exactQueryKeys(values url.Values, allowed ...string) bool { + allowlist := make(map[string]struct{}, len(allowed)) + for _, key := range allowed { + allowlist[key] = struct{}{} + } + for key := range values { + if _, ok := allowlist[key]; !ok { + return false + } + } + return true +} + +func exactQueryValue(values url.Values, key string) (string, bool) { + items, ok := values[key] + return firstExact(items, ok) +} + +func optionalExactQueryValue(values url.Values, key string) (string, bool) { + items, ok := values[key] + if !ok { + return "", false + } + return firstExact(items, true) +} + +func firstExact(items []string, present bool) (string, bool) { + returnValue := "" + if !present || len(items) != 1 || items[0] == "" { + return returnValue, false + } + return items[0], true +} diff --git a/backend/internal/httpapi/reference_handler_test.go b/backend/internal/httpapi/reference_handler_test.go new file mode 100644 index 0000000..1cf0d05 --- /dev/null +++ b/backend/internal/httpapi/reference_handler_test.go @@ -0,0 +1,135 @@ +package httpapi_test + +import ( + "context" + "net/http" + "net/http/httptest" + "strings" + "testing" + + "github.com/SourceSenseiTheRealOne/juntly/backend/internal/httpapi" + "github.com/SourceSenseiTheRealOne/juntly/backend/internal/reference" + "github.com/google/uuid" +) + +func TestReferenceHandlersReturnClosedPublicCatalogs(t *testing.T) { + t.Parallel() + + parentID := uuid.MustParse("11111111-1111-4111-8111-111111111111") + categoryID := uuid.MustParse("22222222-2222-4222-8222-222222222222") + localityID := uuid.MustParse("33333333-3333-4333-8333-333333333333") + service := &recordingReferenceService{ + categories: []reference.Category{{ID: categoryID, ParentID: &parentID, Slug: "cleaning", Name: "Limpeza"}}, + languages: []reference.Language{{Code: "pt-PT", Name: "Português"}}, + localities: []reference.Locality{{ID: localityID, Slug: "zebreira", Name: "Zebreira", ParishName: "Zebreira e Segura", MunicipalityName: "Idanha-a-Nova", DistrictName: "Castelo Branco"}}, + } + + for path, handler := range map[string]http.Handler{ + "/api/v1/catalog/categories?locale=pt-PT": httpapi.NewCategoriesHandler(service), + "/api/v1/reference/languages?locale=pt-PT": httpapi.NewLanguagesHandler(service), + "/api/v1/reference/localities?locale=pt-PT": httpapi.NewLocalitiesHandler(service), + } { + response := httptest.NewRecorder() + request := httptest.NewRequest(http.MethodGet, path, nil) + request.Header.Set(httpapi.RequestIDHeader, "req_reference_public") + handler.ServeHTTP(response, request) + if response.Code != http.StatusOK { + t.Fatalf("%s status = %d", path, response.Code) + } + if response.Header().Get(httpapi.RequestIDHeader) != "req_reference_public" { + t.Fatalf("%s request ID mismatch", path) + } + body := response.Body.String() + for _, prohibited := range []string{"latitude", "longitude", "internalUser", "clerk", "phone", "address"} { + if strings.Contains(strings.ToLower(body), strings.ToLower(prohibited)) { + t.Fatalf("%s response exposes %q: %s", path, prohibited, body) + } + } + } +} + +func TestLocalitiesHandlerSupportsPairedRadiusQuery(t *testing.T) { + t.Parallel() + + origin := uuid.MustParse("33333333-3333-4333-8333-333333333333") + service := &recordingReferenceService{nearby: []reference.LocalityDistance{{Locality: reference.Locality{ID: origin, Slug: "zebreira", Name: "Zebreira", ParishName: "Zebreira e Segura", MunicipalityName: "Idanha-a-Nova", DistrictName: "Castelo Branco"}, DistanceMeters: 0}}} + response := httptest.NewRecorder() + request := httptest.NewRequest(http.MethodGet, "/api/v1/reference/localities?locale=pt-PT&nearLocalityId="+origin.String()+"&radiusKm=25", nil) + request.Header.Set(httpapi.RequestIDHeader, "req_reference_radius") + + httpapi.NewLocalitiesHandler(service).ServeHTTP(response, request) + + if response.Code != http.StatusOK || service.nearbyCalls != 1 || service.origin != origin || service.radius != 25 { + t.Fatalf("status/calls/origin/radius = %d %d %s %d", response.Code, service.nearbyCalls, service.origin, service.radius) + } + if !strings.Contains(response.Body.String(), `"distanceMeters":0`) { + t.Fatalf("radius response = %s", response.Body.String()) + } +} + +func TestReferenceHandlersRejectUnknownOrUnpairedQueriesBeforeService(t *testing.T) { + t.Parallel() + + cases := []struct { + path string + handler http.Handler + }{ + {"/api/v1/catalog/categories?locale=pt-PT&admin=true", httpapi.NewCategoriesHandler(&recordingReferenceService{})}, + {"/api/v1/reference/languages?locale=fr", httpapi.NewLanguagesHandler(&recordingReferenceService{})}, + {"/api/v1/reference/localities?locale=pt-PT&radiusKm=25", httpapi.NewLocalitiesHandler(&recordingReferenceService{})}, + {"/api/v1/reference/localities?locale=pt-PT&nearLocalityId=not-a-uuid&radiusKm=25", httpapi.NewLocalitiesHandler(&recordingReferenceService{})}, + } + for _, item := range cases { + response := httptest.NewRecorder() + request := httptest.NewRequest(http.MethodGet, item.path, nil) + request.Header.Set(httpapi.RequestIDHeader, "req_reference_invalid") + item.handler.ServeHTTP(response, request) + if response.Code != http.StatusBadRequest { + t.Fatalf("%s status = %d, want 400", item.path, response.Code) + } + assertErrorResponse(t, response, "INVALID_REQUEST", "Invalid request", "req_reference_invalid") + } +} + +func TestReferenceHandlerReturnsSafeUnavailable(t *testing.T) { + t.Parallel() + + service := &recordingReferenceService{err: reference.ErrUnavailable} + response := httptest.NewRecorder() + request := httptest.NewRequest(http.MethodGet, "/api/v1/catalog/categories?locale=en", nil) + request.Header.Set(httpapi.RequestIDHeader, "req_reference_unavailable") + + httpapi.NewCategoriesHandler(service).ServeHTTP(response, request) + + if response.Code != http.StatusServiceUnavailable { + t.Fatalf("status = %d, want 503", response.Code) + } + assertErrorResponse(t, response, "SERVICE_UNAVAILABLE", "Service unavailable", "req_reference_unavailable") +} + +type recordingReferenceService struct { + categories []reference.Category + languages []reference.Language + localities []reference.Locality + nearby []reference.LocalityDistance + err error + nearbyCalls int + origin uuid.UUID + radius int +} + +func (s *recordingReferenceService) Categories(context.Context, string) ([]reference.Category, error) { + return s.categories, s.err +} +func (s *recordingReferenceService) Languages(context.Context, string) ([]reference.Language, error) { + return s.languages, s.err +} +func (s *recordingReferenceService) Localities(context.Context, string) ([]reference.Locality, error) { + return s.localities, s.err +} +func (s *recordingReferenceService) NearbyLocalities(_ context.Context, origin uuid.UUID, radius int, _ string) ([]reference.LocalityDistance, error) { + s.nearbyCalls++ + s.origin = origin + s.radius = radius + return s.nearby, s.err +} diff --git a/backend/internal/httpapi/review_handler.go b/backend/internal/httpapi/review_handler.go new file mode 100644 index 0000000..65e846d --- /dev/null +++ b/backend/internal/httpapi/review_handler.go @@ -0,0 +1,148 @@ +package httpapi + +import ( + "context" + "encoding/json" + "errors" + "github.com/SourceSenseiTheRealOne/juntly/backend/internal/authn" + "github.com/SourceSenseiTheRealOne/juntly/backend/internal/reviews" + "github.com/SourceSenseiTheRealOne/juntly/backend/internal/users" + "github.com/google/uuid" + "io" + "net/http" + "strings" +) + +const maxReviewRequestBytes = 4 * 1024 + +type ReviewService interface { + Create(context.Context, users.VerifiedIdentity, reviews.CreateReview) (reviews.Review, error) + ListForProvider(context.Context, users.VerifiedIdentity) ([]reviews.Review, error) + Respond(context.Context, users.VerifiedIdentity, uuid.UUID, string) (reviews.Review, error) + Aggregate(context.Context, uuid.UUID) (reviews.Aggregate, error) +} +type reviewHandler struct{ service ReviewService } + +func NewReviewHandler(s ReviewService) http.Handler { return reviewHandler{service: s} } + +type createReviewRequest struct { + BookingID *uuid.UUID `json:"bookingId"` + Rating *int `json:"rating"` + Body *string `json:"body"` +} +type responseRequest struct { + Response *string `json:"response"` +} + +func (h reviewHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { + id := requestIDFromHeader(r.Header.Get(RequestIDHeader)) + if h.service == nil { + writeAPIError(w, 503, "SERVICE_UNAVAILABLE", "Service unavailable", id) + return + } + if strings.HasPrefix(r.URL.Path, "/api/v1/public/providers/") { + parts := strings.Split(strings.TrimPrefix(strings.TrimSuffix(r.URL.Path, "/"), "/api/v1/public/providers/"), "/") + if len(parts) != 2 || parts[1] != "rating" || r.Method != http.MethodGet { + writeAPIError(w, 404, "NOT_FOUND", "Not found", id) + return + } + provider, err := uuid.Parse(parts[0]) + if err != nil { + writeAPIError(w, 400, "INVALID_REQUEST", "Invalid request", id) + return + } + v, err := h.service.Aggregate(r.Context(), provider) + if err != nil { + writeReviewError(w, err, id) + return + } + writeJSON(w, 200, map[string]any{"providerId": v.ProviderID, "averageRating": v.AverageRating, "reviewCount": v.ReviewCount}, id) + return + } + identity, ok := authn.IdentityFromContext(r.Context()) + if !ok { + writeAPIError(w, 401, "UNAUTHORIZED", "Unauthorized", id) + return + } + path := strings.TrimSuffix(r.URL.Path, "/") + if path == "/api/v1/me/reviews" { + if r.Method != http.MethodPost { + http.Error(w, http.StatusText(405), 405) + return + } + var body createReviewRequest + if !decodeReview(r.Body, &body) || body.BookingID == nil || body.Rating == nil || body.Body == nil { + writeAPIError(w, 400, "INVALID_REQUEST", "Invalid request", id) + return + } + v, err := h.service.Create(r.Context(), identity, reviews.CreateReview{BookingID: *body.BookingID, Rating: *body.Rating, Body: *body.Body}) + if err != nil { + writeReviewError(w, err, id) + return + } + writeJSON(w, 201, reviewJSON(v), id) + return + } + if path == "/api/v1/me/reviews/provider" && r.Method == http.MethodGet { + values, err := h.service.ListForProvider(r.Context(), identity) + if err != nil { + writeReviewError(w, err, id) + return + } + out := make([]map[string]any, 0, len(values)) + for _, v := range values { + out = append(out, reviewJSON(v)) + } + writeJSON(w, 200, map[string]any{"reviews": out}, id) + return + } + parts := strings.Split(strings.TrimPrefix(path, "/api/v1/me/reviews/"), "/") + if len(parts) == 2 && parts[1] == "response" && r.Method == http.MethodPut { + reviewID, err := uuid.Parse(parts[0]) + if err != nil { + writeAPIError(w, 400, "INVALID_REQUEST", "Invalid request", id) + return + } + var body responseRequest + if !decodeReview(r.Body, &body) || body.Response == nil { + writeAPIError(w, 400, "INVALID_REQUEST", "Invalid request", id) + return + } + v, err := h.service.Respond(r.Context(), identity, reviewID, *body.Response) + if err != nil { + writeReviewError(w, err, id) + return + } + writeJSON(w, 200, reviewJSON(v), id) + return + } + writeAPIError(w, 404, "NOT_FOUND", "Not found", id) +} +func decodeReview(r io.Reader, target any) bool { + d := json.NewDecoder(io.LimitReader(r, maxReviewRequestBytes+1)) + d.DisallowUnknownFields() + if d.Decode(target) != nil { + return false + } + var extra any + return errors.Is(d.Decode(&extra), io.EOF) +} +func reviewJSON(v reviews.Review) map[string]any { + return map[string]any{"id": v.ID, "bookingId": v.BookingID, "customerId": v.CustomerID, "providerId": v.ProviderID, "rating": v.Rating, "body": v.Body, "providerResponse": v.ProviderResponse, "verifiedBooking": v.VerifiedBooking, "state": v.State, "createdAt": v.CreatedAt, "updatedAt": v.UpdatedAt} +} +func writeReviewError(w http.ResponseWriter, err error, id string) { + switch { + case errors.Is(err, reviews.ErrInvalid): + writeAPIError(w, 400, "INVALID_REQUEST", "Invalid request", id) + case errors.Is(err, reviews.ErrUnauthorized): + writeAPIError(w, 401, "UNAUTHORIZED", "Unauthorized", id) + case errors.Is(err, reviews.ErrForbidden): + writeAPIError(w, 403, "FORBIDDEN", "Forbidden", id) + case errors.Is(err, reviews.ErrConflict): + writeAPIError(w, 409, "CONFLICT", "Conflict", id) + case errors.Is(err, reviews.ErrNotFound): + writeAPIError(w, 404, "NOT_FOUND", "Not found", id) + default: + writeAPIError(w, 503, "SERVICE_UNAVAILABLE", "Service unavailable", id) + } +} diff --git a/backend/internal/httpapi/router_test.go b/backend/internal/httpapi/router_test.go new file mode 100644 index 0000000..54b7593 --- /dev/null +++ b/backend/internal/httpapi/router_test.go @@ -0,0 +1,150 @@ +package httpapi_test + +import ( + "context" + "net/http" + "net/http/httptest" + "testing" + "time" + + "github.com/SourceSenseiTheRealOne/juntly/backend/internal/accounts" + "github.com/SourceSenseiTheRealOne/juntly/backend/internal/contactreveal" + "github.com/SourceSenseiTheRealOne/juntly/backend/internal/discovery" + "github.com/SourceSenseiTheRealOne/juntly/backend/internal/health" + "github.com/SourceSenseiTheRealOne/juntly/backend/internal/httpapi" + "github.com/SourceSenseiTheRealOne/juntly/backend/internal/users" + "github.com/google/uuid" +) + +func TestRouterLeavesHealthPublicAndProtectsReconciliation(t *testing.T) { + t.Parallel() + + verifier := &routerVerifier{identity: users.VerifiedIdentity{Subject: "user_synthetic"}} + reconcileService := &recordingReconcileService{user: users.InternalUser{ + ID: uuid.MustParse("7b7b7d7e-38f9-4f0c-8a10-0fce9cf6f82b"), + CreatedAt: time.Date(2026, 8, 21, 12, 0, 0, 0, time.UTC), + }} + healthService := health.NewService("0.1.0", func() time.Time { + return time.Date(2026, 8, 21, 12, 0, 0, 0, time.UTC) + }) + accountService := &recordingAccountService{account: accounts.Account{ + CustomerEnabled: true, + ProviderEnabled: false, + OnboardingCompletedAt: time.Date(2026, 8, 23, 12, 5, 0, 0, time.UTC), + }} + referenceService := &recordingReferenceService{} + providerProfileService := &recordingProviderProfileService{} + discoveryService := &recordingPublicDiscoveryService{} + contactChannelService := &recordingRouterContactChannelService{} + contactRevealService := &recordingRouterContactRevealService{} + router := httpapi.NewRouter(healthService, nil, verifier, reconcileService, accountService, referenceService, providerProfileService, &recordingListingService{created: sampleListing()}, &recordingModerationReview{listing: sampleListing()}, discoveryService, contactChannelService, contactRevealService, nil, nil, nil, nil, nil, nil, nil) + + healthResponse := httptest.NewRecorder() + router.ServeHTTP(healthResponse, httptest.NewRequest(http.MethodGet, "/api/v1/health", nil)) + if healthResponse.Code != http.StatusOK { + t.Fatalf("health status = %d, want %d", healthResponse.Code, http.StatusOK) + } + if verifier.calls != 0 { + t.Fatalf("verifier calls after health = %d, want 0", verifier.calls) + } + + publicDiscoveryResponse := httptest.NewRecorder() + router.ServeHTTP(publicDiscoveryResponse, httptest.NewRequest(http.MethodGet, "/api/v1/discovery/listings?locale=pt-PT", nil)) + if publicDiscoveryResponse.Code != http.StatusOK || verifier.calls != 0 || discoveryService.calls != 1 { + t.Fatalf("public discovery status/verifier/service = %d/%d/%d", publicDiscoveryResponse.Code, verifier.calls, discoveryService.calls) + } + + unauthorizedResponse := httptest.NewRecorder() + router.ServeHTTP(unauthorizedResponse, httptest.NewRequest(http.MethodPost, "/api/v1/auth/reconcile", nil)) + if unauthorizedResponse.Code != http.StatusUnauthorized { + t.Fatalf("unauthorized reconciliation status = %d, want %d", unauthorizedResponse.Code, http.StatusUnauthorized) + } + if verifier.calls != 0 { + t.Fatalf("verifier calls after missing bearer = %d, want 0", verifier.calls) + } + + authorizedRequest := httptest.NewRequest(http.MethodPost, "/api/v1/auth/reconcile", nil) + authorizedRequest.Header.Set("Authorization", "Bearer synthetic-token") + authorizedResponse := httptest.NewRecorder() + router.ServeHTTP(authorizedResponse, authorizedRequest) + if authorizedResponse.Code != http.StatusOK { + t.Fatalf("authorized reconciliation status = %d, want %d", authorizedResponse.Code, http.StatusOK) + } + if verifier.calls != 1 { + t.Fatalf("verifier calls after valid bearer = %d, want 1", verifier.calls) + } + + unauthorizedAccountResponse := httptest.NewRecorder() + router.ServeHTTP(unauthorizedAccountResponse, httptest.NewRequest(http.MethodGet, "/api/v1/me/account", nil)) + if unauthorizedAccountResponse.Code != http.StatusUnauthorized { + t.Fatalf("unauthorized account status = %d, want %d", unauthorizedAccountResponse.Code, http.StatusUnauthorized) + } + if accountService.calls != 0 { + t.Fatalf("account service calls after missing bearer = %d, want 0", accountService.calls) + } + + authorizedAccountRequest := httptest.NewRequest(http.MethodGet, "/api/v1/me/account", nil) + authorizedAccountRequest.Header.Set("Authorization", "Bearer synthetic-token") + authorizedAccountResponse := httptest.NewRecorder() + router.ServeHTTP(authorizedAccountResponse, authorizedAccountRequest) + if authorizedAccountResponse.Code != http.StatusOK { + t.Fatalf("authorized account status = %d, want %d", authorizedAccountResponse.Code, http.StatusOK) + } + if verifier.calls != 2 { + t.Fatalf("verifier calls after account bearer = %d, want 2", verifier.calls) + } + if accountService.calls != 1 { + t.Fatalf("account service calls = %d, want 1", accountService.calls) + } + + unauthorizedContactResponse := httptest.NewRecorder() + router.ServeHTTP(unauthorizedContactResponse, httptest.NewRequest(http.MethodGet, "/api/v1/me/contact-channels", nil)) + if unauthorizedContactResponse.Code != http.StatusUnauthorized || contactChannelService.calls != 0 { + t.Fatalf("contact status/calls = %d/%d", unauthorizedContactResponse.Code, contactChannelService.calls) + } + + unauthorizedRevealResponse := httptest.NewRecorder() + router.ServeHTTP(unauthorizedRevealResponse, httptest.NewRequest(http.MethodPost, "/api/v1/listings/aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa/contact-reveals", nil)) + if unauthorizedRevealResponse.Code != http.StatusUnauthorized || contactRevealService.calls != 0 { + t.Fatalf("reveal status/calls = %d/%d", unauthorizedRevealResponse.Code, contactRevealService.calls) + } +} + +type recordingRouterContactChannelService struct{ calls int } + +func (s *recordingRouterContactChannelService) Get(context.Context, users.VerifiedIdentity) ([]contactreveal.ChannelStatus, error) { + s.calls++ + return nil, nil +} +func (s *recordingRouterContactChannelService) Put(context.Context, users.VerifiedIdentity, contactreveal.ReplaceChannel) (contactreveal.ChannelStatus, error) { + s.calls++ + return contactreveal.ChannelStatus{}, nil +} + +type recordingRouterContactRevealService struct{ calls int } + +func (s *recordingRouterContactRevealService) Reveal(context.Context, users.VerifiedIdentity, uuid.UUID, contactreveal.Channel) (contactreveal.RevealedContact, error) { + s.calls++ + return contactreveal.RevealedContact{}, nil +} + +type recordingPublicDiscoveryService struct{ calls int } + +func (s *recordingPublicDiscoveryService) Search(context.Context, discovery.Request) ([]discovery.Listing, error) { + s.calls++ + return nil, nil +} + +func (*recordingPublicDiscoveryService) Get(context.Context, string, string) (*discovery.Listing, error) { + return nil, discovery.ErrNotFound +} + +type routerVerifier struct { + identity users.VerifiedIdentity + calls int +} + +func (v *routerVerifier) Verify(context.Context, string) (users.VerifiedIdentity, error) { + v.calls++ + return v.identity, nil +} diff --git a/backend/internal/listingmedia/ent_repository.go b/backend/internal/listingmedia/ent_repository.go new file mode 100644 index 0000000..913afc1 --- /dev/null +++ b/backend/internal/listingmedia/ent_repository.go @@ -0,0 +1,24 @@ +package listingmedia + +import ( + "context" + "errors" + + jent "github.com/SourceSenseiTheRealOne/juntly/backend/ent" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/listing" + entlistingmedia "github.com/SourceSenseiTheRealOne/juntly/backend/ent/listingmedia" + "github.com/google/uuid" +) + +type entRepository struct{ client *jent.Client } + +func NewEntRepository(client *jent.Client) Repository { return entRepository{client: client} } +func (r entRepository) ReservePending(ctx context.Context, owner, listingID, mediaID uuid.UUID, request UploadRequest, objectReference string) error { + if r.client == nil || owner == uuid.Nil || listingID == uuid.Nil || mediaID == uuid.Nil || objectReference == "" { + return errors.New("listing media persistence unavailable") + } + if _, err := r.client.Listing.Query().Where(listing.IDEQ(listingID), listing.InternalUserIDEQ(owner), listing.StateIn(listing.StateDraft, listing.StateRejected)).Only(ctx); err != nil { + return err + } + return r.client.ListingMedia.Create().SetID(mediaID).SetListingID(listingID).SetOrdinal(request.Ordinal).SetContentType(request.ContentType).SetByteSize(request.ByteSize).SetChecksumSha256(request.ChecksumSHA256).SetObjectReference(objectReference).SetState(entlistingmedia.StatePendingUpload).Exec(ctx) +} diff --git a/backend/internal/listingmedia/model.go b/backend/internal/listingmedia/model.go new file mode 100644 index 0000000..862f1ed --- /dev/null +++ b/backend/internal/listingmedia/model.go @@ -0,0 +1,32 @@ +package listingmedia + +import ( + "errors" + + "github.com/google/uuid" +) + +var ( + ErrInvalidUpload = errors.New("invalid listing media upload") + ErrUnavailable = errors.New("listing media unavailable") +) + +type UploadRequest struct { + Ordinal int + ContentType string + ByteSize int64 + ChecksumSHA256 string +} +type UploadCapability struct { + URL string + Method string + Headers map[string]string +} +type StorageReservation struct { + ObjectReference string + Capability UploadCapability +} +type UploadIntent struct { + MediaID uuid.UUID + Capability UploadCapability +} diff --git a/backend/internal/listingmedia/repository.go b/backend/internal/listingmedia/repository.go new file mode 100644 index 0000000..a6375c8 --- /dev/null +++ b/backend/internal/listingmedia/repository.go @@ -0,0 +1,18 @@ +package listingmedia + +import ( + "context" + + "github.com/SourceSenseiTheRealOne/juntly/backend/internal/users" + "github.com/google/uuid" +) + +type ProviderAuthorizer interface { + RequireProvider(context.Context, users.VerifiedIdentity) (users.InternalUser, error) +} +type Repository interface { + ReservePending(context.Context, uuid.UUID, uuid.UUID, uuid.UUID, UploadRequest, string) error +} +type Storage interface { + CreateUploadReservation(context.Context, uuid.UUID, UploadRequest) (StorageReservation, error) +} diff --git a/backend/internal/listingmedia/service.go b/backend/internal/listingmedia/service.go new file mode 100644 index 0000000..b6cd5d6 --- /dev/null +++ b/backend/internal/listingmedia/service.go @@ -0,0 +1,54 @@ +package listingmedia + +import ( + "context" + "strings" + + "github.com/SourceSenseiTheRealOne/juntly/backend/internal/users" + "github.com/google/uuid" +) + +type Service interface { + CreateUploadIntent(context.Context, users.VerifiedIdentity, uuid.UUID, UploadRequest) (UploadIntent, error) +} +type service struct { + authorizer ProviderAuthorizer + repository Repository + storage Storage +} + +func NewService(authorizer ProviderAuthorizer, repository Repository, storage Storage) Service { + return service{authorizer: authorizer, repository: repository, storage: storage} +} +func (s service) CreateUploadIntent(ctx context.Context, identity users.VerifiedIdentity, listingID uuid.UUID, request UploadRequest) (UploadIntent, error) { + if s.authorizer == nil || s.repository == nil || s.storage == nil || listingID == uuid.Nil || !validUploadRequest(request) { + return UploadIntent{}, ErrInvalidUpload + } + owner, err := s.authorizer.RequireProvider(ctx, identity) + if err != nil { + return UploadIntent{}, err + } + mediaID := uuid.New() + reservation, err := s.storage.CreateUploadReservation(ctx, mediaID, request) + if err != nil { + return UploadIntent{}, ErrUnavailable + } + if reservation.ObjectReference == "" || reservation.Capability.URL == "" || reservation.Capability.Method == "" { + return UploadIntent{}, ErrUnavailable + } + if err := s.repository.ReservePending(ctx, owner.ID, listingID, mediaID, request, reservation.ObjectReference); err != nil { + return UploadIntent{}, ErrUnavailable + } + return UploadIntent{MediaID: mediaID, Capability: reservation.Capability}, nil +} +func validUploadRequest(request UploadRequest) bool { + if request.Ordinal < 1 || request.Ordinal > 10 || request.ByteSize < 1 || request.ByteSize > 10485760 || len(request.ChecksumSHA256) != 64 { + return false + } + for _, r := range request.ChecksumSHA256 { + if !(r >= '0' && r <= '9' || r >= 'a' && r <= 'f') { + return false + } + } + return strings.EqualFold(request.ContentType, "image/jpeg") || strings.EqualFold(request.ContentType, "image/png") || strings.EqualFold(request.ContentType, "image/webp") +} diff --git a/backend/internal/listingmedia/service_test.go b/backend/internal/listingmedia/service_test.go new file mode 100644 index 0000000..1a080be --- /dev/null +++ b/backend/internal/listingmedia/service_test.go @@ -0,0 +1,78 @@ +package listingmedia + +import ( + "context" + "errors" + "testing" + + "github.com/SourceSenseiTheRealOne/juntly/backend/internal/provideraccess" + "github.com/SourceSenseiTheRealOne/juntly/backend/internal/users" + "github.com/google/uuid" +) + +func TestServiceCreatesOwnerUploadIntentWithoutStorageReference(t *testing.T) { + t.Parallel() + owner := users.InternalUser{ID: uuid.MustParse("aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa")} + listingID := uuid.MustParse("bbbbbbbb-bbbb-4bbb-8bbb-bbbbbbbbbbbb") + storage := &recordingStorage{reservation: StorageReservation{ObjectReference: "private-object-reference", Capability: UploadCapability{URL: "https://upload.example.invalid/capability", Method: "PUT", Headers: map[string]string{"Content-Type": "image/webp"}}}} + repository := &recordingRepository{} + intent, err := NewService(&recordingAuthorizer{owner: owner}, repository, storage).CreateUploadIntent(context.Background(), users.VerifiedIdentity{Subject: "provider"}, listingID, UploadRequest{Ordinal: 1, ContentType: "image/webp", ByteSize: 1024, ChecksumSHA256: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"}) + if err != nil || intent.MediaID == uuid.Nil || intent.Capability.URL == "" || repository.owner != owner.ID || repository.objectReference == "" || intent.Capability.URL == repository.objectReference { + t.Fatalf("intent/err/repository = %#v/%v/%#v", intent, err, repository) + } +} + +func TestServiceRejectsInvalidUploadOrUnauthorizedProviderBeforeStorage(t *testing.T) { + t.Parallel() + for _, test := range []struct { + err error + request UploadRequest + want error + }{ + {err: provideraccess.ErrForbidden, request: validRequest(), want: provideraccess.ErrForbidden}, + {request: UploadRequest{Ordinal: 11}, want: ErrInvalidUpload}, + } { + repository := &recordingRepository{} + storage := &recordingStorage{} + _, err := NewService(&recordingAuthorizer{err: test.err}, repository, storage).CreateUploadIntent(context.Background(), users.VerifiedIdentity{}, uuid.New(), test.request) + if !errors.Is(err, test.want) || repository.calls != 0 || storage.calls != 0 { + t.Fatalf("error/calls = %v/%d/%d", err, repository.calls, storage.calls) + } + } +} + +func validRequest() UploadRequest { + return UploadRequest{Ordinal: 1, ContentType: "image/webp", ByteSize: 1024, ChecksumSHA256: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"} +} + +type recordingAuthorizer struct { + owner users.InternalUser + err error +} + +func (a *recordingAuthorizer) RequireProvider(context.Context, users.VerifiedIdentity) (users.InternalUser, error) { + return a.owner, a.err +} + +type recordingRepository struct { + owner uuid.UUID + objectReference string + calls int +} + +func (r *recordingRepository) ReservePending(_ context.Context, owner, listingID, mediaID uuid.UUID, request UploadRequest, objectReference string) error { + r.calls++ + r.owner = owner + r.objectReference = objectReference + return nil +} + +type recordingStorage struct { + reservation StorageReservation + calls int +} + +func (s *recordingStorage) CreateUploadReservation(context.Context, uuid.UUID, UploadRequest) (StorageReservation, error) { + s.calls++ + return s.reservation, nil +} diff --git a/backend/internal/listingmedia/storage.go b/backend/internal/listingmedia/storage.go new file mode 100644 index 0000000..cfff653 --- /dev/null +++ b/backend/internal/listingmedia/storage.go @@ -0,0 +1,15 @@ +package listingmedia + +import ( + "context" + "errors" + + "github.com/google/uuid" +) + +type unavailableStorage struct{} + +func NewUnavailableStorage() Storage { return unavailableStorage{} } +func (unavailableStorage) CreateUploadReservation(context.Context, uuid.UUID, UploadRequest) (StorageReservation, error) { + return StorageReservation{}, errors.New("listing media storage unavailable") +} diff --git a/backend/internal/listings/ent_repository.go b/backend/internal/listings/ent_repository.go new file mode 100644 index 0000000..6d082ae --- /dev/null +++ b/backend/internal/listings/ent_repository.go @@ -0,0 +1,289 @@ +package listings + +import ( + "context" + "errors" + "time" + + jent "github.com/SourceSenseiTheRealOne/juntly/backend/ent" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/listing" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/listingevent" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/locality" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/providerprofile" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/servicecategory" + "github.com/google/uuid" +) + +type entRepository struct{ client *jent.Client } + +func NewEntRepository(client *jent.Client) *entRepository { return &entRepository{client: client} } + +func (r entRepository) Create(ctx context.Context, owner uuid.UUID, input CreateListing) (Listing, error) { + if r.client == nil { + return Listing{}, errors.New("Ent client is nil") + } + tx, err := r.client.Tx(ctx) + if err != nil { + return Listing{}, err + } + committed := false + defer func() { + if !committed { + _ = tx.Rollback() + } + }() + client := tx.Client() + if err := validateOwnerReferences(ctx, client, owner, input); err != nil { + return Listing{}, err + } + entity, err := client.Listing.Create(). + SetInternalUserID(owner). + SetCategoryID(input.CategoryID). + SetPrimaryLocalityID(input.PrimaryLocalityID). + SetTitle(input.Title). + SetDescription(input.Description). + SetPriceType(listing.PriceType(input.PriceType)). + SetNillablePriceMinor(input.PriceMinor). + SetCurrency(input.Currency). + SetTravelsToCustomer(input.TravelsToCustomer). + SetReceivesCustomer(input.ReceivesCustomer). + SetRemoteServices(input.RemoteServices). + SetState(listing.StateDraft). + SetRevision(1). + Save(ctx) + if err != nil { + return Listing{}, err + } + if err := client.ListingEvent.Create(). + SetListingID(entity.ID). + SetActorInternalUserID(owner). + SetEventType(listingevent.EventTypeCreated). + SetToState(string(StateDraft)). + SetRevision(1). + Exec(ctx); err != nil { + return Listing{}, err + } + if err := tx.Commit(); err != nil { + return Listing{}, err + } + committed = true + return listingFromEnt(entity), nil +} + +func (r entRepository) ReplaceDraft(ctx context.Context, owner, id uuid.UUID, revision int, input CreateListing) (Listing, error) { + if r.client == nil { + return Listing{}, errors.New("Ent client is nil") + } + tx, err := r.client.Tx(ctx) + if err != nil { + return Listing{}, err + } + committed := false + defer func() { + if !committed { + _ = tx.Rollback() + } + }() + client := tx.Client() + if err := validateOwnerReferences(ctx, client, owner, input); err != nil { + return Listing{}, err + } + affected, err := client.Listing.Update(). + Where(listing.IDEQ(id), listing.InternalUserIDEQ(owner), listing.StateEQ(listing.StateDraft), listing.RevisionEQ(revision)). + SetCategoryID(input.CategoryID). + SetPrimaryLocalityID(input.PrimaryLocalityID). + SetTitle(input.Title). + SetDescription(input.Description). + SetPriceType(listing.PriceType(input.PriceType)). + SetNillablePriceMinor(input.PriceMinor). + SetTravelsToCustomer(input.TravelsToCustomer). + SetReceivesCustomer(input.ReceivesCustomer). + SetRemoteServices(input.RemoteServices). + SetRevision(revision + 1). + Save(ctx) + if err != nil { + return Listing{}, err + } + if affected != 1 { + return Listing{}, ErrConflict + } + if err := client.ListingEvent.Create(). + SetListingID(id). + SetActorInternalUserID(owner). + SetEventType(listingevent.EventTypeUpdated). + SetFromState(string(StateDraft)). + SetToState(string(StateDraft)). + SetRevision(revision + 1). + Exec(ctx); err != nil { + return Listing{}, err + } + entity, err := client.Listing.Query().Where(listing.IDEQ(id), listing.InternalUserIDEQ(owner)).Only(ctx) + if err != nil { + return Listing{}, err + } + if err := tx.Commit(); err != nil { + return Listing{}, err + } + committed = true + return listingFromEnt(entity), nil +} + +func (r entRepository) TransitionOwned(ctx context.Context, owner, id uuid.UUID, from, to State, revision int, reason *string) (Listing, error) { + return r.transition(ctx, owner, id, from, to, revision, reason, true) +} + +func (r entRepository) TransitionModerated(ctx context.Context, moderator, id uuid.UUID, from, to State, revision int, reason *string) (Listing, error) { + return r.transition(ctx, moderator, id, from, to, revision, reason, false) +} + +func (r entRepository) transition(ctx context.Context, actor, id uuid.UUID, from, to State, revision int, reason *string, ownerScoped bool) (Listing, error) { + if r.client == nil || actor == uuid.Nil || id == uuid.Nil || revision < 1 { + return Listing{}, ErrInvalidListing + } + eventType, ok := transitionEventType(from, to) + if !ok { + return Listing{}, ErrInvalidListing + } + tx, err := r.client.Tx(ctx) + if err != nil { + return Listing{}, err + } + committed := false + defer func() { + if !committed { + _ = tx.Rollback() + } + }() + client := tx.Client() + update := client.Listing.Update().Where( + listing.IDEQ(id), listing.StateEQ(listing.State(from)), listing.RevisionEQ(revision), + ) + if ownerScoped { + update.Where(listing.InternalUserIDEQ(actor)) + } + affected, err := update.SetState(listing.State(to)).SetRevision(revision + 1).Save(ctx) + if err != nil { + return Listing{}, err + } + if affected != 1 { + return Listing{}, ErrConflict + } + if err := client.ListingEvent.Create().SetListingID(id).SetActorInternalUserID(actor). + SetEventType(listingevent.EventType(eventType)).SetFromState(string(from)).SetToState(string(to)). + SetRevision(revision + 1).SetNillableReason(reason).Exec(ctx); err != nil { + return Listing{}, err + } + entity, err := client.Listing.Get(ctx, id) + if err != nil { + return Listing{}, err + } + if err := tx.Commit(); err != nil { + return Listing{}, err + } + committed = true + return listingFromEnt(entity), nil +} + +func (r entRepository) FindByOwner(ctx context.Context, owner, id uuid.UUID) (*Listing, error) { + if r.client == nil { + return nil, errors.New("Ent client is nil") + } + entity, err := r.client.Listing.Query().Where(listing.IDEQ(id), listing.InternalUserIDEQ(owner)).Only(ctx) + if jent.IsNotFound(err) { + return nil, nil + } + if err != nil { + return nil, err + } + value := listingFromEnt(entity) + return &value, nil +} + +func (r entRepository) ListByOwner(ctx context.Context, owner uuid.UUID) ([]Listing, error) { + if r.client == nil { + return nil, errors.New("Ent client is nil") + } + entities, err := r.client.Listing.Query().Where(listing.InternalUserIDEQ(owner)).Order(jent.Desc(listing.FieldUpdatedAt), jent.Asc(listing.FieldID)).All(ctx) + if err != nil { + return nil, err + } + values := make([]Listing, 0, len(entities)) + for _, entity := range entities { + values = append(values, listingFromEnt(entity)) + } + return values, nil +} + +func (r entRepository) ListPending(ctx context.Context) ([]Listing, error) { + if r.client == nil { + return nil, errors.New("Ent client is nil") + } + entities, err := r.client.Listing.Query().Where(listing.StateEQ(listing.StatePendingReview)).Order(jent.Asc(listing.FieldUpdatedAt), jent.Asc(listing.FieldID)).All(ctx) + if err != nil { + return nil, err + } + values := make([]Listing, 0, len(entities)) + for _, entity := range entities { + values = append(values, listingFromEnt(entity)) + } + return values, nil +} + +func validateOwnerReferences(ctx context.Context, client *jent.Client, owner uuid.UUID, input CreateListing) error { + if _, err := client.ServiceCategory.Query().Where(servicecategory.IDEQ(input.CategoryID), servicecategory.ActiveEQ(true)).Only(ctx); err != nil { + if jent.IsNotFound(err) { + return ErrInvalidListing + } + return err + } + if _, err := client.Locality.Query().Where(locality.IDEQ(input.PrimaryLocalityID), locality.ActiveEQ(true)).Only(ctx); err != nil { + if jent.IsNotFound(err) { + return ErrInvalidListing + } + return err + } + profile, err := client.ProviderProfile.Query().Where(providerprofile.IDEQ(owner)).WithServiceLocalities().Only(ctx) + if err != nil { + if jent.IsNotFound(err) { + return ErrInvalidListing + } + return err + } + if (input.TravelsToCustomer && !profile.TravelsToCustomer) || (input.ReceivesCustomer && !profile.ReceivesCustomer) || (input.RemoteServices && !profile.RemoteServices) { + return ErrInvalidListing + } + for _, serviceLocality := range profile.Edges.ServiceLocalities { + if serviceLocality.ID == input.PrimaryLocalityID { + return nil + } + } + return ErrInvalidListing +} + +func listingFromEnt(entity *jent.Listing) Listing { + var price *int + if entity.PriceMinor != nil { + value := *entity.PriceMinor + price = &value + } + return Listing{ID: entity.ID, CreateListing: CreateListing{CategoryID: entity.CategoryID, PrimaryLocalityID: entity.PrimaryLocalityID, Title: entity.Title, Description: entity.Description, PriceType: PriceType(entity.PriceType), PriceMinor: price, Currency: entity.Currency, TravelsToCustomer: entity.TravelsToCustomer, ReceivesCustomer: entity.ReceivesCustomer, RemoteServices: entity.RemoteServices}, State: State(entity.State), Revision: entity.Revision, CreatedAt: normalizeTime(entity.CreatedAt), UpdatedAt: normalizeTime(entity.UpdatedAt)} +} + +func normalizeTime(value time.Time) time.Time { return value.UTC().Truncate(time.Microsecond) } + +func transitionEventType(from, to State) (string, bool) { + switch { + case from == StateDraft && to == StatePendingReview: + return "submitted", true + case from == StatePendingReview && to == StateActive: + return "approved", true + case from == StatePendingReview && to == StateRejected: + return "rejected", true + case from == StateActive && to == StatePaused: + return "paused", true + case to == StateArchived && from != StateArchived: + return "archived", true + default: + return "", false + } +} diff --git a/backend/internal/listings/ent_repository_test.go b/backend/internal/listings/ent_repository_test.go new file mode 100644 index 0000000..b90176a --- /dev/null +++ b/backend/internal/listings/ent_repository_test.go @@ -0,0 +1,251 @@ +package listings + +import ( + "context" + "database/sql" + "errors" + "os" + "sync" + "testing" + + "entgo.io/ent/dialect" + entsql "entgo.io/ent/dialect/sql" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/listing" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/listingevent" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/locality" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/servicecategory" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/spokenlanguage" + "github.com/SourceSenseiTheRealOne/juntly/backend/internal/accounts" + "github.com/SourceSenseiTheRealOne/juntly/backend/internal/providers" + "github.com/SourceSenseiTheRealOne/juntly/backend/internal/users" + "github.com/google/uuid" + _ "github.com/jackc/pgx/v5/stdlib" +) + +func TestEntRepositoryCreatesDraftAndAtomicCreatedEvent(t *testing.T) { + client := openListingClient(t) + ctx := context.Background() + owner, localityIDs, categoryID := createListingProvider(t, client) + repository := NewEntRepository(client) + input := integrationCreate(categoryID, localityIDs[0]) + + created, err := repository.Create(ctx, owner.ID, input) + if err != nil { + t.Fatalf("create draft: %v", err) + } + if created.ID == uuid.Nil || created.State != StateDraft || created.Revision != 1 || created.CreatedAt.IsZero() || created.UpdatedAt.IsZero() { + t.Fatalf("created listing = %#v", created) + } + found, err := repository.FindByOwner(ctx, owner.ID, created.ID) + if err != nil || found == nil || found.ID != created.ID || found.State != StateDraft { + t.Fatalf("find owner listing = %#v, err = %v", found, err) + } + events, err := client.ListingEvent.Query().Where(listingevent.ListingIDEQ(created.ID)).All(ctx) + if err != nil || len(events) != 1 || string(events[0].EventType) != "created" || events[0].ToState != "draft" || events[0].Revision != 1 { + t.Fatalf("created events = %#v, err = %v", events, err) + } + other, _, _ := createListingProvider(t, client) + missing, err := repository.FindByOwner(ctx, other.ID, created.ID) + if err != nil || missing != nil { + t.Fatalf("cross-owner listing = %#v, err = %v", missing, err) + } +} + +func TestEntRepositoryRejectsLocalityOutsideProviderServiceAreasWithoutEvent(t *testing.T) { + client := openListingClient(t) + ctx := context.Background() + owner, localityIDs, categoryID := createListingProvider(t, client) + repository := NewEntRepository(client) + input := integrationCreate(categoryID, localityIDs[1]) + if _, err := repository.Create(ctx, owner.ID, input); err == nil { + t.Fatal("out-of-service-area listing error = nil") + } + count, err := client.Listing.Query().Where(listing.InternalUserIDEQ(owner.ID)).Count(ctx) + if err != nil || count != 0 { + t.Fatalf("listing count = %d, err = %v", count, err) + } +} + +func TestEntRepositoryReplacesDraftWithCASAndOneUpdatedEvent(t *testing.T) { + client := openListingClient(t) + ctx := context.Background() + owner, localityIDs, categoryID := createListingProvider(t, client) + repository := NewEntRepository(client) + created, err := repository.Create(ctx, owner.ID, integrationCreate(categoryID, localityIDs[0])) + if err != nil { + t.Fatalf("create draft: %v", err) + } + updatedInput := integrationCreate(categoryID, localityIDs[0]) + updatedInput.Title = "Canalização local atualizada" + updated, err := repository.ReplaceDraft(ctx, owner.ID, created.ID, created.Revision, updatedInput) + if err != nil || updated.Revision != 2 || updated.Title != updatedInput.Title || updated.CreatedAt != created.CreatedAt { + t.Fatalf("updated = %#v, err = %v", updated, err) + } + if _, err := repository.ReplaceDraft(ctx, owner.ID, created.ID, created.Revision, updatedInput); !errors.Is(err, ErrConflict) { + t.Fatalf("stale update error = %v, want ErrConflict", err) + } + events, err := client.ListingEvent.Query().Where(listingevent.ListingIDEQ(created.ID)).Order(ent.Asc(listingevent.FieldRevision)).All(ctx) + if err != nil || len(events) != 2 || string(events[1].EventType) != "updated" || events[1].Revision != 2 { + t.Fatalf("events = %#v, err = %v", events, err) + } +} + +func TestEntRepositoryTransitionsWithCASAndOneEvent(t *testing.T) { + client := openListingClient(t) + ctx := context.Background() + owner, localityIDs, categoryID := createListingProvider(t, client) + repository := NewEntRepository(client) + created, err := repository.Create(ctx, owner.ID, integrationCreate(categoryID, localityIDs[0])) + if err != nil { + t.Fatalf("create draft: %v", err) + } + transitioned, err := repository.TransitionOwned(ctx, owner.ID, created.ID, StateDraft, StatePendingReview, created.Revision, nil) + if err != nil || transitioned.State != StatePendingReview || transitioned.Revision != 2 { + t.Fatalf("transitioned = %#v, err = %v", transitioned, err) + } + if _, err := repository.TransitionOwned(ctx, owner.ID, created.ID, StateDraft, StatePendingReview, created.Revision, nil); !errors.Is(err, ErrConflict) { + t.Fatalf("stale transition error = %v, want ErrConflict", err) + } + events, err := client.ListingEvent.Query().Where(listingevent.ListingIDEQ(created.ID)).Order(ent.Asc(listingevent.FieldRevision)).All(ctx) + if err != nil || len(events) != 2 || string(events[1].EventType) != "submitted" || events[1].Revision != 2 { + t.Fatalf("events = %#v, err = %v", events, err) + } +} + +func TestEntRepositoryOwnerTransitionRejectsAnotherProvidersDraft(t *testing.T) { + client := openListingClient(t) + ctx := context.Background() + owner, localityIDs, categoryID := createListingProvider(t, client) + other, _, _ := createListingProvider(t, client) + repository := NewEntRepository(client) + created, err := repository.Create(ctx, owner.ID, integrationCreate(categoryID, localityIDs[0])) + if err != nil { + t.Fatalf("create draft: %v", err) + } + if _, err := repository.TransitionOwned(ctx, other.ID, created.ID, StateDraft, StatePendingReview, created.Revision, nil); !errors.Is(err, ErrConflict) { + t.Fatalf("cross-owner transition error = %v, want ErrConflict", err) + } + found, err := repository.FindByOwner(ctx, owner.ID, created.ID) + if err != nil || found == nil || found.State != StateDraft || found.Revision != created.Revision { + t.Fatalf("listing after cross-owner transition = %#v, err = %v", found, err) + } +} + +func TestEntRepositoryConcurrentModeratorOutcomesHaveOneWinner(t *testing.T) { + client := openListingClient(t) + ctx := context.Background() + owner, localityIDs, categoryID := createListingProvider(t, client) + other, _, _ := createListingProvider(t, client) + repository := NewEntRepository(client) + created, err := repository.Create(ctx, owner.ID, integrationCreate(categoryID, localityIDs[0])) + if err != nil { + t.Fatalf("create: %v", err) + } + pending, err := repository.TransitionOwned(ctx, owner.ID, created.ID, StateDraft, StatePendingReview, created.Revision, nil) + if err != nil { + t.Fatalf("submit: %v", err) + } + start := make(chan struct{}) + results := make(chan error, 2) + var workers sync.WaitGroup + for _, outcome := range []State{StateActive, StateRejected} { + actor, outcome := owner.ID, outcome + if outcome == StateRejected { + actor = other.ID + } + workers.Add(1) + go func() { + defer workers.Done() + <-start + _, err := repository.TransitionModerated(ctx, actor, created.ID, StatePendingReview, outcome, pending.Revision, nil) + results <- err + }() + } + close(start) + workers.Wait() + close(results) + successes := 0 + for err := range results { + if err == nil { + successes++ + } else if !errors.Is(err, ErrConflict) { + t.Fatalf("moderation outcome: %v", err) + } + } + if successes != 1 { + t.Fatalf("successful outcomes = %d, want 1", successes) + } + events, err := client.ListingEvent.Query().Where(listingevent.ListingIDEQ(created.ID)).All(ctx) + if err != nil || len(events) != 3 { + t.Fatalf("events = %#v, err = %v", events, err) + } +} + +func openListingClient(t *testing.T) *ent.Client { + t.Helper() + url := os.Getenv("TEST_DATABASE_URL") + if url == "" { + t.Skip("TEST_DATABASE_URL is required for PostgreSQL integration tests") + } + database, err := sql.Open("pgx", url) + if err != nil { + t.Fatalf("open database: %v", err) + } + client := ent.NewClient(ent.Driver(entsql.OpenDB(dialect.Postgres, database))) + t.Cleanup(func() { + if err := client.Close(); err != nil { + t.Errorf("close client: %v", err) + } + }) + return client +} + +func createListingProvider(t *testing.T, client *ent.Client) (users.InternalUser, []uuid.UUID, uuid.UUID) { + t.Helper() + ctx := context.Background() + owner, _, err := users.NewService(users.NewEntRepository(client)).Reconcile(ctx, users.VerifiedIdentity{Subject: "listing_provider_" + uuid.NewString()}) + if err != nil { + t.Fatalf("reconcile provider: %v", err) + } + accountsRepo := accounts.NewEntRepository(client) + if _, err := accountsRepo.Create(ctx, owner.ID); err != nil { + t.Fatalf("create account: %v", err) + } + if _, err := accountsRepo.SetProviderEnabled(ctx, owner.ID, true); err != nil { + t.Fatalf("enable provider: %v", err) + } + localityIDs, err := client.Locality.Query().Where(locality.ActiveEQ(true)).Order(ent.Asc(locality.FieldID)).IDs(ctx) + if err != nil || len(localityIDs) < 2 { + t.Fatalf("localities = %v, err = %v", localityIDs, err) + } + languageIDs, err := client.SpokenLanguage.Query().Where(spokenlanguage.ActiveEQ(true)).Limit(1).IDs(ctx) + if err != nil || len(languageIDs) != 1 { + t.Fatalf("languages = %v, err = %v", languageIDs, err) + } + categoryIDs, err := client.ServiceCategory.Query().Where(servicecategory.ActiveEQ(true)).Limit(1).IDs(ctx) + if err != nil || len(categoryIDs) != 1 { + t.Fatalf("categories = %v, err = %v", categoryIDs, err) + } + _, err = providers.NewEntRepository(client).Replace(ctx, owner.ID, providers.ReplaceProfile{DisplayName: "Listing provider", ProviderType: providers.ProviderTypeProfessional, Bio: "Synthetic provider profile for listing integration.", PrimaryLocalityID: localityIDs[0], ServiceLocalityIDs: []uuid.UUID{localityIDs[0]}, MaxTravelDistanceKM: 25, TravelsToCustomer: true, LanguageCodes: languageIDs}) + if err != nil { + t.Fatalf("create provider profile: %v", err) + } + t.Cleanup(func() { + if _, err := client.ListingEvent.Delete().Where(listingevent.ActorInternalUserIDEQ(owner.ID)).Exec(ctx); err != nil { + t.Errorf("cleanup actor events: %v", err) + } + if _, err := client.Listing.Delete().Where(listing.InternalUserIDEQ(owner.ID)).Exec(ctx); err != nil { + t.Errorf("cleanup listings: %v", err) + } + if err := client.InternalUser.DeleteOneID(owner.ID).Exec(ctx); err != nil { + t.Errorf("cleanup owner: %v", err) + } + }) + return owner, localityIDs, categoryIDs[0] +} + +func integrationCreate(categoryID, localityID uuid.UUID) CreateListing { + price := 5000 + return CreateListing{CategoryID: categoryID, PrimaryLocalityID: localityID, Title: "Canalização local", Description: "Serviço de canalização local para pequenas reparações domésticas e manutenção.", PriceType: PriceTypeFixed, PriceMinor: &price, Currency: "EUR", TravelsToCustomer: true} +} diff --git a/backend/internal/listings/lifecycle_service.go b/backend/internal/listings/lifecycle_service.go new file mode 100644 index 0000000..53c17a3 --- /dev/null +++ b/backend/internal/listings/lifecycle_service.go @@ -0,0 +1,100 @@ +package listings + +import ( + "context" + "strings" + "unicode/utf8" + + "github.com/SourceSenseiTheRealOne/juntly/backend/internal/users" + "github.com/google/uuid" +) + +type ModeratorAuthorizer interface { + RequireModerator(context.Context, users.VerifiedIdentity) (users.InternalUser, error) +} +type LifecycleRepository interface { + TransitionOwned(context.Context, uuid.UUID, uuid.UUID, State, State, int, *string) (Listing, error) + TransitionModerated(context.Context, uuid.UUID, uuid.UUID, State, State, int, *string) (Listing, error) +} +type LifecycleService interface { + Submit(context.Context, users.VerifiedIdentity, uuid.UUID, int) (Listing, error) + Approve(context.Context, users.VerifiedIdentity, uuid.UUID, int) (Listing, error) + Reject(context.Context, users.VerifiedIdentity, uuid.UUID, int, string) (Listing, error) + Pause(context.Context, users.VerifiedIdentity, uuid.UUID, int) (Listing, error) + Archive(context.Context, users.VerifiedIdentity, uuid.UUID, State, int) (Listing, error) +} +type lifecycleService struct { + providers ProviderAuthorizer + moderators ModeratorAuthorizer + repository LifecycleRepository +} + +func NewLifecycleService(providers ProviderAuthorizer, moderators ModeratorAuthorizer, repository LifecycleRepository) LifecycleService { + return lifecycleService{providers: providers, moderators: moderators, repository: repository} +} +func (s lifecycleService) Submit(ctx context.Context, identity users.VerifiedIdentity, id uuid.UUID, revision int) (Listing, error) { + if s.providers == nil || s.repository == nil || id == uuid.Nil || revision < 1 { + return Listing{}, ErrInvalidListing + } + owner, err := s.providers.RequireProvider(ctx, identity) + if err != nil { + return Listing{}, err + } + return s.repository.TransitionOwned(ctx, owner.ID, id, StateDraft, StatePendingReview, revision, nil) +} +func (s lifecycleService) Approve(ctx context.Context, identity users.VerifiedIdentity, id uuid.UUID, revision int) (Listing, error) { + if s.moderators == nil || s.repository == nil || id == uuid.Nil || revision < 1 { + return Listing{}, ErrInvalidListing + } + moderator, err := s.moderators.RequireModerator(ctx, identity) + if err != nil { + return Listing{}, err + } + return s.repository.TransitionModerated(ctx, moderator.ID, id, StatePendingReview, StateActive, revision, nil) +} + +func (s lifecycleService) Reject(ctx context.Context, identity users.VerifiedIdentity, id uuid.UUID, revision int, reason string) (Listing, error) { + if s.moderators == nil || s.repository == nil || id == uuid.Nil || revision < 1 { + return Listing{}, ErrInvalidListing + } + reason = strings.TrimSpace(reason) + if utf8.RuneCountInString(reason) < 1 || utf8.RuneCountInString(reason) > 500 { + return Listing{}, ErrInvalidListing + } + moderator, err := s.moderators.RequireModerator(ctx, identity) + if err != nil { + return Listing{}, err + } + return s.repository.TransitionModerated(ctx, moderator.ID, id, StatePendingReview, StateRejected, revision, &reason) +} + +func (s lifecycleService) Pause(ctx context.Context, identity users.VerifiedIdentity, id uuid.UUID, revision int) (Listing, error) { + if s.providers == nil || s.repository == nil || id == uuid.Nil || revision < 1 { + return Listing{}, ErrInvalidListing + } + owner, err := s.providers.RequireProvider(ctx, identity) + if err != nil { + return Listing{}, err + } + return s.repository.TransitionOwned(ctx, owner.ID, id, StateActive, StatePaused, revision, nil) +} + +func (s lifecycleService) Archive(ctx context.Context, identity users.VerifiedIdentity, id uuid.UUID, from State, revision int) (Listing, error) { + if s.providers == nil || s.repository == nil || id == uuid.Nil || revision < 1 || !archivableState(from) { + return Listing{}, ErrInvalidListing + } + owner, err := s.providers.RequireProvider(ctx, identity) + if err != nil { + return Listing{}, err + } + return s.repository.TransitionOwned(ctx, owner.ID, id, from, StateArchived, revision, nil) +} + +func archivableState(state State) bool { + switch state { + case StateDraft, StateRejected, StateActive, StatePaused: + return true + default: + return false + } +} diff --git a/backend/internal/listings/lifecycle_service_test.go b/backend/internal/listings/lifecycle_service_test.go new file mode 100644 index 0000000..9c95a20 --- /dev/null +++ b/backend/internal/listings/lifecycle_service_test.go @@ -0,0 +1,121 @@ +package listings + +import ( + "context" + "errors" + "testing" + + "github.com/SourceSenseiTheRealOne/juntly/backend/internal/provideraccess" + "github.com/SourceSenseiTheRealOne/juntly/backend/internal/users" + "github.com/google/uuid" +) + +func TestLifecycleServiceScopesOwnerAndModeratorTransitions(t *testing.T) { + t.Parallel() + owner := users.InternalUser{ID: uuid.MustParse("aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa")} + moderator := users.InternalUser{ID: uuid.MustParse("bbbbbbbb-bbbb-4bbb-8bbb-bbbbbbbbbbbb")} + listingID := uuid.MustParse("cccccccc-cccc-4ccc-8ccc-cccccccccccc") + repository := &recordingLifecycleRepository{} + service := NewLifecycleService(&recordingAuthorizer{owner: owner}, &recordingModeratorAuthorizer{user: moderator}, repository) + + if _, err := service.Submit(context.Background(), users.VerifiedIdentity{Subject: "provider"}, listingID, 1); err != nil { + t.Fatalf("submit: %v", err) + } + if repository.actor != owner.ID || repository.from != StateDraft || repository.to != StatePendingReview || repository.revision != 1 { + t.Fatalf("submit transition = %#v", repository) + } + if _, err := service.Approve(context.Background(), users.VerifiedIdentity{Subject: "moderator"}, listingID, 2); err != nil { + t.Fatalf("approve: %v", err) + } + if repository.actor != moderator.ID || repository.from != StatePendingReview || repository.to != StateActive || repository.revision != 2 { + t.Fatalf("approve transition = %#v", repository) + } +} + +func TestLifecycleServiceShortCircuitsUnauthorizedOwnersAndModerators(t *testing.T) { + t.Parallel() + listingID := uuid.New() + moderatorForbidden := errors.New("moderator forbidden") + for _, test := range []struct { + providerErr, moderatorErr, want error + action string + }{ + {providerErr: provideraccess.ErrForbidden, want: provideraccess.ErrForbidden, action: "submit"}, + {moderatorErr: moderatorForbidden, want: moderatorForbidden, action: "approve"}, + } { + repository := &recordingLifecycleRepository{} + service := NewLifecycleService(&recordingAuthorizer{err: test.providerErr}, &recordingModeratorAuthorizer{err: test.moderatorErr}, repository) + var err error + if test.action == "submit" { + _, err = service.Submit(context.Background(), users.VerifiedIdentity{}, listingID, 1) + } else { + _, err = service.Approve(context.Background(), users.VerifiedIdentity{}, listingID, 1) + } + if !errors.Is(err, test.want) || repository.calls != 0 { + t.Fatalf("error/calls = %v/%d", err, repository.calls) + } + } +} + +func TestLifecycleServiceRejectsPendingReviewWithBoundedModeratorReason(t *testing.T) { + t.Parallel() + moderator := users.InternalUser{ID: uuid.MustParse("dddddddd-dddd-4ddd-8ddd-dddddddddddd")} + repository := &recordingLifecycleRepository{} + service := NewLifecycleService(&recordingAuthorizer{}, &recordingModeratorAuthorizer{user: moderator}, repository) + listingID := uuid.MustParse("eeeeeeee-eeee-4eee-8eee-eeeeeeeeeeee") + + if _, err := service.Reject(context.Background(), users.VerifiedIdentity{Subject: "moderator"}, listingID, 3, "Needs clearer scope"); err != nil { + t.Fatalf("reject: %v", err) + } + if repository.actor != moderator.ID || repository.from != StatePendingReview || repository.to != StateRejected || repository.revision != 3 { + t.Fatalf("reject transition = %#v", repository) + } +} + +func TestLifecycleServicePausesAndArchivesOnlyThroughOwnerScope(t *testing.T) { + t.Parallel() + owner := users.InternalUser{ID: uuid.MustParse("ffffffff-ffff-4fff-8fff-ffffffffffff")} + repository := &recordingLifecycleRepository{} + service := NewLifecycleService(&recordingAuthorizer{owner: owner}, &recordingModeratorAuthorizer{}, repository) + listingID := uuid.MustParse("99999999-9999-4999-8999-999999999999") + if _, err := service.Pause(context.Background(), users.VerifiedIdentity{Subject: "provider"}, listingID, 4); err != nil { + t.Fatalf("pause: %v", err) + } + if repository.actor != owner.ID || repository.from != StateActive || repository.to != StatePaused { + t.Fatalf("pause transition = %#v", repository) + } + if _, err := service.Archive(context.Background(), users.VerifiedIdentity{Subject: "provider"}, listingID, StatePaused, 5); err != nil { + t.Fatalf("archive: %v", err) + } + if repository.actor != owner.ID || repository.from != StatePaused || repository.to != StateArchived { + t.Fatalf("archive transition = %#v", repository) + } +} + +type recordingModeratorAuthorizer struct { + user users.InternalUser + err error +} + +func (a *recordingModeratorAuthorizer) RequireModerator(context.Context, users.VerifiedIdentity) (users.InternalUser, error) { + return a.user, a.err +} + +type recordingLifecycleRepository struct { + actor uuid.UUID + from State + to State + revision int + calls int +} + +func (r *recordingLifecycleRepository) TransitionOwned(_ context.Context, actor, id uuid.UUID, from, to State, revision int, reason *string) (Listing, error) { + r.calls++ + r.actor, r.from, r.to, r.revision = actor, from, to, revision + return Listing{ID: id, State: to, Revision: revision + 1}, nil +} +func (r *recordingLifecycleRepository) TransitionModerated(_ context.Context, actor, id uuid.UUID, from, to State, revision int, reason *string) (Listing, error) { + r.calls++ + r.actor, r.from, r.to, r.revision = actor, from, to, revision + return Listing{ID: id, State: to, Revision: revision + 1}, nil +} diff --git a/backend/internal/listings/listingmedia_repository_test.go b/backend/internal/listings/listingmedia_repository_test.go new file mode 100644 index 0000000..905c1e6 --- /dev/null +++ b/backend/internal/listings/listingmedia_repository_test.go @@ -0,0 +1,39 @@ +package listings + +import ( + "context" + "testing" + + entlistingmedia "github.com/SourceSenseiTheRealOne/juntly/backend/ent/listingmedia" + "github.com/SourceSenseiTheRealOne/juntly/backend/internal/listingmedia" + "github.com/google/uuid" +) + +func TestListingMediaEntRepositoryReservesOwnerPendingMediaWithoutPublicReference(t *testing.T) { + client := openListingClient(t) + ctx := context.Background() + owner, localityIDs, categoryID := createListingProvider(t, client) + other, _, _ := createListingProvider(t, client) + listing, err := NewEntRepository(client).Create(ctx, owner.ID, integrationCreate(categoryID, localityIDs[0])) + if err != nil { + t.Fatalf("create listing: %v", err) + } + mediaID := uuid.New() + request := listingmedia.UploadRequest{Ordinal: 1, ContentType: "image/webp", ByteSize: 1024, ChecksumSHA256: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"} + privateReference := "storage-internal/media/" + mediaID.String() + repository := listingmedia.NewEntRepository(client) + if err := repository.ReservePending(ctx, owner.ID, listing.ID, mediaID, request, privateReference); err != nil { + t.Fatalf("reserve: %v", err) + } + stored, err := client.ListingMedia.Get(ctx, mediaID) + if err != nil || stored.ListingID != listing.ID || stored.ObjectReference != privateReference || string(stored.State) != "pending_upload" { + t.Fatalf("stored=%#v err=%v", stored, err) + } + if err := repository.ReservePending(ctx, other.ID, listing.ID, uuid.New(), request, "storage-internal/media/other"); err == nil { + t.Fatal("cross-owner reservation error=nil") + } + count, err := client.ListingMedia.Query().Where(entlistingmedia.ListingIDEQ(listing.ID)).Count(ctx) + if err != nil || count != 1 { + t.Fatalf("count=%d err=%v", count, err) + } +} diff --git a/backend/internal/listings/model.go b/backend/internal/listings/model.go new file mode 100644 index 0000000..60aad54 --- /dev/null +++ b/backend/internal/listings/model.go @@ -0,0 +1,57 @@ +package listings + +import ( + "errors" + "time" + + "github.com/google/uuid" +) + +var ( + ErrInvalidListing = errors.New("invalid listing") + ErrConflict = errors.New("listing revision or state conflict") + ErrUnavailable = errors.New("listing persistence unavailable") +) + +type PriceType string + +const ( + PriceTypeFixed PriceType = "fixed" + PriceTypeHourly PriceType = "hourly" + PriceTypeDaily PriceType = "daily" + PriceTypeQuote PriceType = "quote" + PriceTypeNegotiable PriceType = "negotiable" +) + +type State string + +const ( + StateDraft State = "draft" + StatePendingReview State = "pending_review" + StateActive State = "active" + StateRejected State = "rejected" + StatePaused State = "paused" + StateArchived State = "archived" +) + +type CreateListing struct { + CategoryID uuid.UUID + PrimaryLocalityID uuid.UUID + Title string + Description string + PriceType PriceType + PriceMinor *int + Currency string + TravelsToCustomer bool + ReceivesCustomer bool + RemoteServices bool +} + +type Listing struct { + ID uuid.UUID + CreateListing + State State + Revision int + CreatedAt time.Time + UpdatedAt time.Time +} diff --git a/backend/internal/listings/owner_service.go b/backend/internal/listings/owner_service.go new file mode 100644 index 0000000..2e52b80 --- /dev/null +++ b/backend/internal/listings/owner_service.go @@ -0,0 +1,77 @@ +package listings + +import ( + "context" + + "github.com/SourceSenseiTheRealOne/juntly/backend/internal/listingmedia" + "github.com/SourceSenseiTheRealOne/juntly/backend/internal/users" + "github.com/google/uuid" +) + +type OwnerService interface { + Create(context.Context, users.VerifiedIdentity, CreateListing) (Listing, error) + ReplaceDraft(context.Context, users.VerifiedIdentity, uuid.UUID, int, CreateListing) (Listing, error) + Get(context.Context, users.VerifiedIdentity, uuid.UUID) (*Listing, error) + List(context.Context, users.VerifiedIdentity) ([]Listing, error) + Submit(context.Context, users.VerifiedIdentity, uuid.UUID, int) (Listing, error) + Pause(context.Context, users.VerifiedIdentity, uuid.UUID, int) (Listing, error) + Archive(context.Context, users.VerifiedIdentity, uuid.UUID, State, int) (Listing, error) + CreateUploadIntent(context.Context, users.VerifiedIdentity, uuid.UUID, listingmedia.UploadRequest) (listingmedia.UploadIntent, error) +} +type ownerService struct { + drafts Service + lifecycle LifecycleService + media listingmedia.Service +} + +func NewOwnerService(drafts Service, lifecycle LifecycleService, media listingmedia.Service) OwnerService { + return ownerService{drafts: drafts, lifecycle: lifecycle, media: media} +} +func (s ownerService) Create(ctx context.Context, i users.VerifiedIdentity, v CreateListing) (Listing, error) { + if s.drafts == nil { + return Listing{}, ErrUnavailable + } + return s.drafts.Create(ctx, i, v) +} +func (s ownerService) ReplaceDraft(ctx context.Context, i users.VerifiedIdentity, id uuid.UUID, r int, v CreateListing) (Listing, error) { + if s.drafts == nil { + return Listing{}, ErrUnavailable + } + return s.drafts.ReplaceDraft(ctx, i, id, r, v) +} +func (s ownerService) Get(ctx context.Context, i users.VerifiedIdentity, id uuid.UUID) (*Listing, error) { + if s.drafts == nil { + return nil, ErrUnavailable + } + return s.drafts.Get(ctx, i, id) +} +func (s ownerService) List(ctx context.Context, i users.VerifiedIdentity) ([]Listing, error) { + if s.drafts == nil { + return nil, ErrUnavailable + } + return s.drafts.List(ctx, i) +} +func (s ownerService) Submit(ctx context.Context, i users.VerifiedIdentity, id uuid.UUID, r int) (Listing, error) { + if s.lifecycle == nil { + return Listing{}, ErrUnavailable + } + return s.lifecycle.Submit(ctx, i, id, r) +} +func (s ownerService) Pause(ctx context.Context, i users.VerifiedIdentity, id uuid.UUID, r int) (Listing, error) { + if s.lifecycle == nil { + return Listing{}, ErrUnavailable + } + return s.lifecycle.Pause(ctx, i, id, r) +} +func (s ownerService) Archive(ctx context.Context, i users.VerifiedIdentity, id uuid.UUID, from State, r int) (Listing, error) { + if s.lifecycle == nil { + return Listing{}, ErrUnavailable + } + return s.lifecycle.Archive(ctx, i, id, from, r) +} +func (s ownerService) CreateUploadIntent(ctx context.Context, i users.VerifiedIdentity, id uuid.UUID, r listingmedia.UploadRequest) (listingmedia.UploadIntent, error) { + if s.media == nil { + return listingmedia.UploadIntent{}, listingmedia.ErrUnavailable + } + return s.media.CreateUploadIntent(ctx, i, id, r) +} diff --git a/backend/internal/listings/owner_service_test.go b/backend/internal/listings/owner_service_test.go new file mode 100644 index 0000000..ac19947 --- /dev/null +++ b/backend/internal/listings/owner_service_test.go @@ -0,0 +1,87 @@ +package listings + +import ( + "context" + "testing" + + "github.com/SourceSenseiTheRealOne/juntly/backend/internal/listingmedia" + "github.com/SourceSenseiTheRealOne/juntly/backend/internal/users" + "github.com/google/uuid" +) + +func TestOwnerServiceDelegatesOnlyToServerSideDomains(t *testing.T) { + t.Parallel() + listingID := uuid.MustParse("aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa") + identity := users.VerifiedIdentity{Subject: "provider"} + draft := &recordingDraftService{listing: sampleOwnerListing()} + lifecycle := &recordingLifecycleService{listing: sampleOwnerListing()} + media := &recordingMediaService{intent: listingmedia.UploadIntent{MediaID: uuid.MustParse("bbbbbbbb-bbbb-4bbb-8bbb-bbbbbbbbbbbb")}} + service := NewOwnerService(draft, lifecycle, media) + if _, err := service.Submit(context.Background(), identity, listingID, 1); err != nil { + t.Fatalf("submit: %v", err) + } + if _, err := service.Pause(context.Background(), identity, listingID, 2); err != nil { + t.Fatalf("pause: %v", err) + } + if _, err := service.Archive(context.Background(), identity, listingID, StatePaused, 3); err != nil { + t.Fatalf("archive: %v", err) + } + if _, err := service.CreateUploadIntent(context.Background(), identity, listingID, listingmedia.UploadRequest{Ordinal: 1, ContentType: "image/webp", ByteSize: 1, ChecksumSHA256: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"}); err != nil { + t.Fatalf("media: %v", err) + } + if lifecycle.calls != 3 || media.calls != 1 { + t.Fatalf("calls lifecycle/media=%d/%d", lifecycle.calls, media.calls) + } +} + +type recordingDraftService struct{ listing Listing } + +func (s *recordingDraftService) Create(context.Context, users.VerifiedIdentity, CreateListing) (Listing, error) { + return s.listing, nil +} +func (s *recordingDraftService) ReplaceDraft(context.Context, users.VerifiedIdentity, uuid.UUID, int, CreateListing) (Listing, error) { + return s.listing, nil +} +func (s *recordingDraftService) Get(context.Context, users.VerifiedIdentity, uuid.UUID) (*Listing, error) { + return &s.listing, nil +} +func (s *recordingDraftService) List(context.Context, users.VerifiedIdentity) ([]Listing, error) { + return []Listing{s.listing}, nil +} + +type recordingLifecycleService struct { + listing Listing + calls int +} + +func (s *recordingLifecycleService) Submit(context.Context, users.VerifiedIdentity, uuid.UUID, int) (Listing, error) { + s.calls++ + return s.listing, nil +} +func (s *recordingLifecycleService) Approve(context.Context, users.VerifiedIdentity, uuid.UUID, int) (Listing, error) { + return s.listing, nil +} +func (s *recordingLifecycleService) Reject(context.Context, users.VerifiedIdentity, uuid.UUID, int, string) (Listing, error) { + return s.listing, nil +} +func (s *recordingLifecycleService) Pause(context.Context, users.VerifiedIdentity, uuid.UUID, int) (Listing, error) { + s.calls++ + return s.listing, nil +} +func (s *recordingLifecycleService) Archive(context.Context, users.VerifiedIdentity, uuid.UUID, State, int) (Listing, error) { + s.calls++ + return s.listing, nil +} + +type recordingMediaService struct { + intent listingmedia.UploadIntent + calls int +} + +func (s *recordingMediaService) CreateUploadIntent(context.Context, users.VerifiedIdentity, uuid.UUID, listingmedia.UploadRequest) (listingmedia.UploadIntent, error) { + s.calls++ + return s.intent, nil +} +func sampleOwnerListing() Listing { + return Listing{ID: uuid.MustParse("aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa"), State: StateDraft, Revision: 1} +} diff --git a/backend/internal/listings/repository.go b/backend/internal/listings/repository.go new file mode 100644 index 0000000..5fcbf0d --- /dev/null +++ b/backend/internal/listings/repository.go @@ -0,0 +1,16 @@ +package listings + +import ( + "context" + + "github.com/google/uuid" +) + +type Repository interface { + Create(context.Context, uuid.UUID, CreateListing) (Listing, error) + FindByOwner(context.Context, uuid.UUID, uuid.UUID) (*Listing, error) + ListByOwner(context.Context, uuid.UUID) ([]Listing, error) + ReplaceDraft(context.Context, uuid.UUID, uuid.UUID, int, CreateListing) (Listing, error) + TransitionOwned(context.Context, uuid.UUID, uuid.UUID, State, State, int, *string) (Listing, error) + TransitionModerated(context.Context, uuid.UUID, uuid.UUID, State, State, int, *string) (Listing, error) +} diff --git a/backend/internal/listings/service.go b/backend/internal/listings/service.go new file mode 100644 index 0000000..5920223 --- /dev/null +++ b/backend/internal/listings/service.go @@ -0,0 +1,134 @@ +package listings + +import ( + "context" + "errors" + "strings" + "unicode/utf8" + + "github.com/SourceSenseiTheRealOne/juntly/backend/internal/users" + "github.com/google/uuid" +) + +type ProviderAuthorizer interface { + RequireProvider(context.Context, users.VerifiedIdentity) (users.InternalUser, error) +} + +type Service interface { + Create(context.Context, users.VerifiedIdentity, CreateListing) (Listing, error) + ReplaceDraft(context.Context, users.VerifiedIdentity, uuid.UUID, int, CreateListing) (Listing, error) + Get(context.Context, users.VerifiedIdentity, uuid.UUID) (*Listing, error) + List(context.Context, users.VerifiedIdentity) ([]Listing, error) +} + +type service struct { + authorizer ProviderAuthorizer + repository Repository +} + +func NewService(authorizer ProviderAuthorizer, repository Repository) Service { + return service{authorizer: authorizer, repository: repository} +} + +func (s service) Create(ctx context.Context, identity users.VerifiedIdentity, input CreateListing) (Listing, error) { + owner, err := s.requireOwner(ctx, identity) + if err != nil { + return Listing{}, err + } + input, valid := normalizeCreate(input) + if !valid { + return Listing{}, ErrInvalidListing + } + listing, err := s.repository.Create(ctx, owner.ID, input) + if err != nil { + return Listing{}, publicRepositoryError(err) + } + return listing, nil +} + +func (s service) ReplaceDraft(ctx context.Context, identity users.VerifiedIdentity, id uuid.UUID, revision int, input CreateListing) (Listing, error) { + owner, err := s.requireOwner(ctx, identity) + if err != nil { + return Listing{}, err + } + input, valid := normalizeCreate(input) + if id == uuid.Nil || revision < 1 || !valid { + return Listing{}, ErrInvalidListing + } + updated, err := s.repository.ReplaceDraft(ctx, owner.ID, id, revision, input) + if err != nil { + return Listing{}, publicRepositoryError(err) + } + return updated, nil +} + +func (s service) Get(ctx context.Context, identity users.VerifiedIdentity, id uuid.UUID) (*Listing, error) { + owner, err := s.requireOwner(ctx, identity) + if err != nil { + return nil, err + } + if id == uuid.Nil { + return nil, ErrInvalidListing + } + listing, err := s.repository.FindByOwner(ctx, owner.ID, id) + if err != nil { + return nil, ErrUnavailable + } + return listing, nil +} + +func (s service) List(ctx context.Context, identity users.VerifiedIdentity) ([]Listing, error) { + owner, err := s.requireOwner(ctx, identity) + if err != nil { + return nil, err + } + listings, err := s.repository.ListByOwner(ctx, owner.ID) + if err != nil { + return nil, ErrUnavailable + } + return listings, nil +} + +func (s service) requireOwner(ctx context.Context, identity users.VerifiedIdentity) (users.InternalUser, error) { + if s.authorizer == nil || s.repository == nil { + return users.InternalUser{}, ErrUnavailable + } + owner, err := s.authorizer.RequireProvider(ctx, identity) + if err != nil { + return users.InternalUser{}, err + } + return owner, nil +} + +func normalizeCreate(input CreateListing) (CreateListing, bool) { + input.Title = strings.TrimSpace(input.Title) + input.Description = strings.TrimSpace(input.Description) + input.Currency = strings.TrimSpace(input.Currency) + if input.CategoryID == uuid.Nil || input.PrimaryLocalityID == uuid.Nil || utf8.RuneCountInString(input.Title) < 2 || utf8.RuneCountInString(input.Title) > 140 || utf8.RuneCountInString(input.Description) < 20 || utf8.RuneCountInString(input.Description) > 4000 || input.Currency != "EUR" || (!input.TravelsToCustomer && !input.ReceivesCustomer && !input.RemoteServices) { + return CreateListing{}, false + } + switch input.PriceType { + case PriceTypeFixed, PriceTypeHourly, PriceTypeDaily: + if input.PriceMinor == nil || *input.PriceMinor <= 0 { + return CreateListing{}, false + } + case PriceTypeQuote, PriceTypeNegotiable: + if input.PriceMinor != nil { + return CreateListing{}, false + } + default: + return CreateListing{}, false + } + return input, true +} + +func publicRepositoryError(err error) error { + switch { + case errors.Is(err, ErrInvalidListing): + return ErrInvalidListing + case errors.Is(err, ErrConflict): + return ErrConflict + default: + return ErrUnavailable + } +} diff --git a/backend/internal/listings/service_test.go b/backend/internal/listings/service_test.go new file mode 100644 index 0000000..e38fdb0 --- /dev/null +++ b/backend/internal/listings/service_test.go @@ -0,0 +1,181 @@ +package listings + +import ( + "context" + "errors" + "reflect" + "testing" + "time" + + "github.com/SourceSenseiTheRealOne/juntly/backend/internal/provideraccess" + "github.com/SourceSenseiTheRealOne/juntly/backend/internal/users" + "github.com/google/uuid" +) + +func TestServiceRejectsUnauthorizedProviderBeforeRepository(t *testing.T) { + t.Parallel() + for _, authorizationError := range []error{provideraccess.ErrUnauthorized, provideraccess.ErrForbidden, provideraccess.ErrUnavailable} { + repository := &recordingRepository{} + _, err := NewService(&recordingAuthorizer{err: authorizationError}, repository).Create(context.Background(), users.VerifiedIdentity{}, validCreate()) + if !errors.Is(err, authorizationError) { + t.Fatalf("error = %v, want %v", err, authorizationError) + } + if repository.calls != 0 { + t.Fatalf("repository calls = %d, want 0", repository.calls) + } + } +} + +func TestServiceRejectsInvalidDraftBeforeRepository(t *testing.T) { + t.Parallel() + valid := validCreate() + for name, input := range map[string]CreateListing{ + "short title": replaceCreate(valid, func(value *CreateListing) { value.Title = "A" }), + "short description": replaceCreate(valid, func(value *CreateListing) { value.Description = "short" }), + "missing category": replaceCreate(valid, func(value *CreateListing) { value.CategoryID = uuid.Nil }), + "missing locality": replaceCreate(valid, func(value *CreateListing) { value.PrimaryLocalityID = uuid.Nil }), + "unsupported price type": replaceCreate(valid, func(value *CreateListing) { value.PriceType = "subscription" }), + "missing fixed price": replaceCreate(valid, func(value *CreateListing) { value.PriceMinor = nil }), + "price on quote": replaceCreate(valid, func(value *CreateListing) { value.PriceType = PriceTypeQuote }), + "wrong currency": replaceCreate(valid, func(value *CreateListing) { value.Currency = "USD" }), + "no service mode": replaceCreate(valid, func(value *CreateListing) { value.TravelsToCustomer = false }), + } { + name, input := name, input + t.Run(name, func(t *testing.T) { + t.Parallel() + repository := &recordingRepository{} + _, err := NewService(&recordingAuthorizer{owner: owner()}, repository).Create(context.Background(), users.VerifiedIdentity{Subject: "provider"}, input) + if !errors.Is(err, ErrInvalidListing) { + t.Fatalf("error = %v, want ErrInvalidListing", err) + } + if repository.calls != 0 { + t.Fatalf("repository calls = %d, want 0", repository.calls) + } + }) + } +} + +func TestServiceCreatesAndReadsOwnerDraft(t *testing.T) { + t.Parallel() + input := validCreate() + stored := listingFromCreate(input) + repository := &recordingRepository{created: stored, found: &stored} + service := NewService(&recordingAuthorizer{owner: owner()}, repository) + + created, err := service.Create(context.Background(), users.VerifiedIdentity{Subject: "provider"}, input) + if err != nil || !reflect.DeepEqual(created, stored) { + t.Fatalf("created = %#v, err = %v", created, err) + } + if repository.owner != owner().ID || !reflect.DeepEqual(repository.input, input) { + t.Fatalf("repository owner/input = %s/%#v", repository.owner, repository.input) + } + found, err := service.Get(context.Background(), users.VerifiedIdentity{Subject: "provider"}, stored.ID) + if err != nil || !reflect.DeepEqual(found, &stored) { + t.Fatalf("found = %#v, err = %v", found, err) + } +} + +func TestServiceMapsRepositoryFailureToUnavailable(t *testing.T) { + t.Parallel() + _, err := NewService(&recordingAuthorizer{owner: owner()}, &recordingRepository{err: errors.New("private database detail")}).List(context.Background(), users.VerifiedIdentity{Subject: "provider"}) + if !errors.Is(err, ErrUnavailable) { + t.Fatalf("error = %v, want ErrUnavailable", err) + } +} + +func TestServiceReplacesAuthorizedDraftWithExpectedRevision(t *testing.T) { + t.Parallel() + input := validCreate() + stored := listingFromCreate(input) + stored.Revision = 2 + repository := &recordingRepository{updated: stored} + + updated, err := NewService(&recordingAuthorizer{owner: owner()}, repository).ReplaceDraft( + context.Background(), users.VerifiedIdentity{Subject: "provider"}, stored.ID, 1, input, + ) + if err != nil || !reflect.DeepEqual(updated, stored) { + t.Fatalf("updated = %#v, err = %v", updated, err) + } + if repository.owner != owner().ID || repository.id != stored.ID || repository.revision != 1 || !reflect.DeepEqual(repository.input, input) { + t.Fatalf("repository owner/id/revision/input = %s/%s/%d/%#v", repository.owner, repository.id, repository.revision, repository.input) + } +} + +func validCreate() CreateListing { + price := 5000 + return CreateListing{ + CategoryID: uuid.MustParse("aaaaaaaa-aaaa-daaa-0aaa-aaaaaaaaaaaa"), + PrimaryLocalityID: uuid.MustParse("bbbbbbbb-bbbb-dbbb-0bbb-bbbbbbbbbbbb"), + Title: "Reparação local de canalização", + Description: "Serviço local para pequenas reparações de canalização e manutenção doméstica.", + PriceType: PriceTypeFixed, + PriceMinor: &price, + Currency: "EUR", + TravelsToCustomer: true, + ReceivesCustomer: false, + RemoteServices: false, + } +} +func replaceCreate(value CreateListing, mutate func(*CreateListing)) CreateListing { + mutate(&value) + return value +} +func owner() users.InternalUser { + return users.InternalUser{ID: uuid.MustParse("cccccccc-cccc-dccc-0ccc-cccccccccccc")} +} +func listingFromCreate(value CreateListing) Listing { + return Listing{ID: uuid.MustParse("dddddddd-dddd-dddd-0ddd-dddddddddddd"), CreateListing: value, State: StateDraft, Revision: 1, CreatedAt: time.Date(2026, 8, 24, 11, 0, 0, 0, time.UTC), UpdatedAt: time.Date(2026, 8, 24, 11, 0, 0, 0, time.UTC)} +} + +type recordingAuthorizer struct { + owner users.InternalUser + err error +} + +func (a *recordingAuthorizer) RequireProvider(context.Context, users.VerifiedIdentity) (users.InternalUser, error) { + return a.owner, a.err +} + +type recordingRepository struct { + owner uuid.UUID + id uuid.UUID + revision int + input CreateListing + created Listing + updated Listing + found *Listing + err error + calls int +} + +func (r *recordingRepository) Create(_ context.Context, owner uuid.UUID, input CreateListing) (Listing, error) { + r.calls++ + r.owner = owner + r.input = input + return r.created, r.err +} +func (r *recordingRepository) FindByOwner(_ context.Context, owner, id uuid.UUID) (*Listing, error) { + r.calls++ + r.owner = owner + return r.found, r.err +} +func (r *recordingRepository) ListByOwner(_ context.Context, owner uuid.UUID) ([]Listing, error) { + r.calls++ + r.owner = owner + return nil, r.err +} +func (r *recordingRepository) ReplaceDraft(_ context.Context, owner, id uuid.UUID, revision int, input CreateListing) (Listing, error) { + r.calls++ + r.owner, r.id, r.revision, r.input = owner, id, revision, input + return r.updated, r.err +} +func (r *recordingRepository) TransitionOwned(_ context.Context, actor, id uuid.UUID, from, to State, revision int, reason *string) (Listing, error) { + r.calls++ + r.owner, r.id, r.revision = actor, id, revision + return Listing{ID: id, State: to, Revision: revision + 1}, r.err +} +func (r *recordingRepository) TransitionModerated(_ context.Context, actor, id uuid.UUID, from, to State, revision int, reason *string) (Listing, error) { + r.calls++ + r.owner, r.id, r.revision = actor, id, revision + return Listing{ID: id, State: to, Revision: revision + 1}, r.err +} diff --git a/backend/internal/messaging/model.go b/backend/internal/messaging/model.go new file mode 100644 index 0000000..ef72e87 --- /dev/null +++ b/backend/internal/messaging/model.go @@ -0,0 +1,46 @@ +package messaging + +import ( + "errors" + "time" + + "github.com/google/uuid" +) + +var ( + ErrInvalid = errors.New("messaging invalid request") + ErrUnauthorized = errors.New("messaging unauthorized") + ErrForbidden = errors.New("messaging forbidden") + ErrNotFound = errors.New("messaging not found") + ErrUnavailable = errors.New("messaging unavailable") +) + +type Conversation struct { + ID uuid.UUID + ListingID *uuid.UUID + CustomerID uuid.UUID + ProviderID uuid.UUID + BlockedBy *uuid.UUID + CreatedAt time.Time + UpdatedAt time.Time +} + +type Message struct { + ID uuid.UUID + ConversationID uuid.UUID + SenderID uuid.UUID + Body string + CreatedAt time.Time +} + +type NotificationPreferences struct { + InAppEnabled bool + EmailEnabled bool +} + +type Notification struct { + ID uuid.UUID + Kind string + ReadAt *time.Time + CreatedAt time.Time +} diff --git a/backend/internal/messaging/service.go b/backend/internal/messaging/service.go new file mode 100644 index 0000000..6bbc154 --- /dev/null +++ b/backend/internal/messaging/service.go @@ -0,0 +1,176 @@ +package messaging + +import ( + "context" + "errors" + "strings" + "unicode/utf8" + + "github.com/SourceSenseiTheRealOne/juntly/backend/internal/users" + "github.com/google/uuid" +) + +type IdentityReconciler interface { + Reconcile(context.Context, users.VerifiedIdentity) (users.InternalUser, bool, error) +} + +type Store interface { + Start(context.Context, uuid.UUID, uuid.UUID) (Conversation, error) + List(context.Context, uuid.UUID) ([]Conversation, error) + ListMessages(context.Context, uuid.UUID, uuid.UUID) ([]Message, error) + Send(context.Context, uuid.UUID, uuid.UUID, string) (Message, error) + SetBlocked(context.Context, uuid.UUID, uuid.UUID, bool) error + Report(context.Context, uuid.UUID, uuid.UUID, *uuid.UUID, string) error + Preferences(context.Context, uuid.UUID) (NotificationPreferences, error) + ReplacePreferences(context.Context, uuid.UUID, NotificationPreferences) (NotificationPreferences, error) + Notifications(context.Context, uuid.UUID) ([]Notification, error) + MarkNotificationRead(context.Context, uuid.UUID, uuid.UUID) error +} + +type Service interface { + Start(context.Context, users.VerifiedIdentity, uuid.UUID) (Conversation, error) + List(context.Context, users.VerifiedIdentity) ([]Conversation, error) + ListMessages(context.Context, users.VerifiedIdentity, uuid.UUID) ([]Message, error) + Send(context.Context, users.VerifiedIdentity, uuid.UUID, string) (Message, error) + SetBlocked(context.Context, users.VerifiedIdentity, uuid.UUID, bool) error + Report(context.Context, users.VerifiedIdentity, uuid.UUID, *uuid.UUID, string) error + Preferences(context.Context, users.VerifiedIdentity) (NotificationPreferences, error) + ReplacePreferences(context.Context, users.VerifiedIdentity, NotificationPreferences) (NotificationPreferences, error) + Notifications(context.Context, users.VerifiedIdentity) ([]Notification, error) + MarkNotificationRead(context.Context, users.VerifiedIdentity, uuid.UUID) error +} + +type service struct { + identities IdentityReconciler + store Store +} + +func NewService(identities IdentityReconciler, store Store) Service { + return service{identities: identities, store: store} +} + +func (s service) Start(ctx context.Context, identity users.VerifiedIdentity, listingID uuid.UUID) (Conversation, error) { + if listingID == uuid.Nil { + return Conversation{}, ErrInvalid + } + actor, err := s.actor(ctx, identity) + if err != nil { + return Conversation{}, err + } + value, err := s.store.Start(ctx, actor, listingID) + return value, normalize(err) +} +func (s service) List(ctx context.Context, identity users.VerifiedIdentity) ([]Conversation, error) { + actor, err := s.actor(ctx, identity) + if err != nil { + return nil, err + } + value, err := s.store.List(ctx, actor) + return value, normalize(err) +} +func (s service) ListMessages(ctx context.Context, identity users.VerifiedIdentity, conversationID uuid.UUID) ([]Message, error) { + if conversationID == uuid.Nil { + return nil, ErrInvalid + } + actor, err := s.actor(ctx, identity) + if err != nil { + return nil, err + } + value, err := s.store.ListMessages(ctx, actor, conversationID) + return value, normalize(err) +} +func (s service) Send(ctx context.Context, identity users.VerifiedIdentity, conversationID uuid.UUID, body string) (Message, error) { + body = strings.TrimSpace(body) + if conversationID == uuid.Nil || body == "" || utf8.RuneCountInString(body) > 4000 { + return Message{}, ErrInvalid + } + actor, err := s.actor(ctx, identity) + if err != nil { + return Message{}, err + } + value, err := s.store.Send(ctx, actor, conversationID, body) + return value, normalize(err) +} +func (s service) SetBlocked(ctx context.Context, identity users.VerifiedIdentity, conversationID uuid.UUID, blocked bool) error { + if conversationID == uuid.Nil { + return ErrInvalid + } + actor, err := s.actor(ctx, identity) + if err != nil { + return err + } + return normalize(s.store.SetBlocked(ctx, actor, conversationID, blocked)) +} +func (s service) Report(ctx context.Context, identity users.VerifiedIdentity, conversationID uuid.UUID, messageID *uuid.UUID, reason string) error { + reason = strings.TrimSpace(reason) + if conversationID == uuid.Nil || utf8.RuneCountInString(reason) < 5 || utf8.RuneCountInString(reason) > 500 { + return ErrInvalid + } + if messageID != nil && *messageID == uuid.Nil { + return ErrInvalid + } + actor, err := s.actor(ctx, identity) + if err != nil { + return err + } + return normalize(s.store.Report(ctx, actor, conversationID, messageID, reason)) +} +func (s service) Preferences(ctx context.Context, identity users.VerifiedIdentity) (NotificationPreferences, error) { + actor, err := s.actor(ctx, identity) + if err != nil { + return NotificationPreferences{}, err + } + value, err := s.store.Preferences(ctx, actor) + return value, normalize(err) +} +func (s service) ReplacePreferences(ctx context.Context, identity users.VerifiedIdentity, value NotificationPreferences) (NotificationPreferences, error) { + actor, err := s.actor(ctx, identity) + if err != nil { + return NotificationPreferences{}, err + } + updated, err := s.store.ReplacePreferences(ctx, actor, value) + return updated, normalize(err) +} +func (s service) Notifications(ctx context.Context, identity users.VerifiedIdentity) ([]Notification, error) { + actor, err := s.actor(ctx, identity) + if err != nil { + return nil, err + } + items, err := s.store.Notifications(ctx, actor) + return items, normalize(err) +} +func (s service) MarkNotificationRead(ctx context.Context, identity users.VerifiedIdentity, notificationID uuid.UUID) error { + if notificationID == uuid.Nil { + return ErrInvalid + } + actor, err := s.actor(ctx, identity) + if err != nil { + return err + } + return normalize(s.store.MarkNotificationRead(ctx, actor, notificationID)) +} +func (s service) actor(ctx context.Context, identity users.VerifiedIdentity) (uuid.UUID, error) { + if s.identities == nil || s.store == nil { + return uuid.Nil, ErrUnavailable + } + user, _, err := s.identities.Reconcile(ctx, identity) + if err != nil { + if errors.Is(err, users.ErrInvalidIdentity) { + return uuid.Nil, ErrUnauthorized + } + return uuid.Nil, ErrUnavailable + } + if user.ID == uuid.Nil { + return uuid.Nil, ErrUnauthorized + } + return user.ID, nil +} +func normalize(err error) error { + if err == nil { + return nil + } + if errors.Is(err, ErrInvalid) || errors.Is(err, ErrUnauthorized) || errors.Is(err, ErrForbidden) || errors.Is(err, ErrNotFound) { + return err + } + return ErrUnavailable +} diff --git a/backend/internal/messaging/service_test.go b/backend/internal/messaging/service_test.go new file mode 100644 index 0000000..3e6fd30 --- /dev/null +++ b/backend/internal/messaging/service_test.go @@ -0,0 +1,119 @@ +package messaging + +import ( + "context" + "errors" + "testing" + "time" + + "github.com/SourceSenseiTheRealOne/juntly/backend/internal/users" + "github.com/google/uuid" +) + +func TestServiceStartsListingConversationAndCreatesNotification(t *testing.T) { + t.Parallel() + actorID := uuid.MustParse("11111111-1111-4111-8111-111111111111") + listingID := uuid.MustParse("22222222-2222-4222-8222-222222222222") + store := &recordingStore{conversation: Conversation{ + ID: uuid.MustParse("33333333-3333-4333-8333-333333333333"), ListingID: &listingID, + CustomerID: actorID, ProviderID: uuid.MustParse("44444444-4444-4444-8444-444444444444"), + CreatedAt: time.Date(2026, 9, 1, 12, 0, 0, 0, time.UTC), + }} + service := NewService(staticIdentity{user: users.InternalUser{ID: actorID}}, store) + + conversation, err := service.Start(context.Background(), users.VerifiedIdentity{Subject: "user_customer"}, listingID) + if err != nil { + t.Fatalf("Start() error = %v", err) + } + if conversation.ID != store.conversation.ID || store.startedBy != actorID || store.listingID != listingID { + t.Fatalf("Start() conversation/store = %#v/%s/%s", conversation, store.startedBy, store.listingID) + } +} + +func TestServiceRejectsBlankMessageBeforeStore(t *testing.T) { + t.Parallel() + store := &recordingStore{} + service := NewService(staticIdentity{user: users.InternalUser{ID: uuid.New()}}, store) + + _, err := service.Send(context.Background(), users.VerifiedIdentity{Subject: "user_customer"}, uuid.New(), " ") + if !errors.Is(err, ErrInvalid) { + t.Fatalf("Send() error = %v, want ErrInvalid", err) + } + if store.sentBody != "" { + t.Fatalf("store called with %q", store.sentBody) + } +} + +func TestServiceMapsParticipantDenialToForbidden(t *testing.T) { + t.Parallel() + store := &recordingStore{sendErr: ErrForbidden} + service := NewService(staticIdentity{user: users.InternalUser{ID: uuid.New()}}, store) + + _, err := service.Send(context.Background(), users.VerifiedIdentity{Subject: "user_customer"}, uuid.New(), "Preciso de ajuda amanhã.") + if !errors.Is(err, ErrForbidden) { + t.Fatalf("Send() error = %v, want ErrForbidden", err) + } +} + +func TestServiceListsOnlyReconciledParticipants(t *testing.T) { + t.Parallel() + actorID := uuid.New() + store := &recordingStore{conversations: []Conversation{{ID: uuid.New(), CustomerID: actorID, ProviderID: uuid.New()}}} + service := NewService(staticIdentity{user: users.InternalUser{ID: actorID}}, store) + + items, err := service.List(context.Background(), users.VerifiedIdentity{Subject: "user_customer"}) + if err != nil || len(items) != 1 || store.listedBy != actorID { + t.Fatalf("List() items/error/actor = %#v/%v/%s", items, err, store.listedBy) + } +} + +type staticIdentity struct { + user users.InternalUser + err error +} + +func (s staticIdentity) Reconcile(context.Context, users.VerifiedIdentity) (users.InternalUser, bool, error) { + return s.user, false, s.err +} + +type recordingStore struct { + conversation Conversation + conversations []Conversation + startedBy uuid.UUID + listingID uuid.UUID + listedBy uuid.UUID + sentBody string + sendErr error +} + +func (s *recordingStore) Start(_ context.Context, actorID, listingID uuid.UUID) (Conversation, error) { + s.startedBy, s.listingID = actorID, listingID + return s.conversation, nil +} +func (s *recordingStore) List(_ context.Context, actorID uuid.UUID) ([]Conversation, error) { + s.listedBy = actorID + return s.conversations, nil +} +func (s *recordingStore) ListMessages(context.Context, uuid.UUID, uuid.UUID) ([]Message, error) { + return nil, nil +} +func (s *recordingStore) Send(_ context.Context, _ uuid.UUID, _ uuid.UUID, body string) (Message, error) { + s.sentBody = body + return Message{ID: uuid.New(), Body: body}, s.sendErr +} +func (s *recordingStore) SetBlocked(context.Context, uuid.UUID, uuid.UUID, bool) error { return nil } +func (s *recordingStore) Report(context.Context, uuid.UUID, uuid.UUID, *uuid.UUID, string) error { + return nil +} +func (s *recordingStore) Preferences(context.Context, uuid.UUID) (NotificationPreferences, error) { + return NotificationPreferences{}, nil +} +func (s *recordingStore) ReplacePreferences(context.Context, uuid.UUID, NotificationPreferences) (NotificationPreferences, error) { + return NotificationPreferences{}, nil +} +func (s *recordingStore) Notifications(context.Context, uuid.UUID) ([]Notification, error) { + return nil, nil +} +func (s *recordingStore) MarkNotificationRead(context.Context, uuid.UUID, uuid.UUID) error { + return nil +} diff --git a/backend/internal/messaging/sql_store.go b/backend/internal/messaging/sql_store.go new file mode 100644 index 0000000..e8c1457 --- /dev/null +++ b/backend/internal/messaging/sql_store.go @@ -0,0 +1,193 @@ +package messaging + +import ( + "context" + "database/sql" + "errors" + "time" + + "github.com/google/uuid" +) + +type sqlStore struct{ database *sql.DB } + +func NewSQLStore(database *sql.DB) Store { return sqlStore{database: database} } + +func (s sqlStore) Start(ctx context.Context, actorID, listingID uuid.UUID) (Conversation, error) { + tx, err := s.database.BeginTx(ctx, nil) + if err != nil { + return Conversation{}, err + } + defer tx.Rollback() + var providerID uuid.UUID + if err := tx.QueryRowContext(ctx, `select internal_user_id from public.listings where id=$1 and state='active'`, listingID).Scan(&providerID); err != nil { + if errors.Is(err, sql.ErrNoRows) { + return Conversation{}, ErrNotFound + } + return Conversation{}, err + } + if providerID == actorID { + return Conversation{}, ErrForbidden + } + var value Conversation + err = tx.QueryRowContext(ctx, `insert into public.conversations (listing_id,customer_internal_user_id,provider_internal_user_id) values ($1,$2,$3) + on conflict (listing_id,customer_internal_user_id,provider_internal_user_id) do update set updated_at=public.conversations.updated_at + returning id,listing_id,customer_internal_user_id,provider_internal_user_id,blocked_by_internal_user_id,created_at,updated_at`, listingID, actorID, providerID). + Scan(&value.ID, &value.ListingID, &value.CustomerID, &value.ProviderID, &value.BlockedBy, &value.CreatedAt, &value.UpdatedAt) + if err != nil { + return Conversation{}, err + } + if _, err = tx.ExecContext(ctx, `insert into public.notifications (recipient_internal_user_id,kind,resource_id,in_app_visible) + select $1,'conversation_started',$2,coalesce((select in_app_enabled from public.notification_preferences where internal_user_id=$1),true) + where coalesce((select in_app_enabled or email_enabled from public.notification_preferences where internal_user_id=$1),true) + and not exists (select 1 from public.notifications where recipient_internal_user_id=$1 and kind='conversation_started' and resource_id=$2)`, providerID, value.ID); err != nil { + return Conversation{}, err + } + if _, err = tx.ExecContext(ctx, `insert into public.notification_email_outbox (notification_id,recipient_internal_user_id) + select id,recipient_internal_user_id from public.notifications where recipient_internal_user_id=$1 and kind='conversation_started' and resource_id=$2 + and coalesce((select email_enabled from public.notification_preferences where internal_user_id=$1),true) + on conflict (notification_id) do nothing`, providerID, value.ID); err != nil { + return Conversation{}, err + } + if err = tx.Commit(); err != nil { + return Conversation{}, err + } + return value, nil +} +func (s sqlStore) List(ctx context.Context, actorID uuid.UUID) ([]Conversation, error) { + rows, err := s.database.QueryContext(ctx, `select id,listing_id,customer_internal_user_id,provider_internal_user_id,blocked_by_internal_user_id,created_at,updated_at from public.conversations where customer_internal_user_id=$1 or provider_internal_user_id=$1 order by updated_at desc,id`, actorID) + if err != nil { + return nil, err + } + defer rows.Close() + values := []Conversation{} + for rows.Next() { + var value Conversation + if err := rows.Scan(&value.ID, &value.ListingID, &value.CustomerID, &value.ProviderID, &value.BlockedBy, &value.CreatedAt, &value.UpdatedAt); err != nil { + return nil, err + } + values = append(values, value) + } + return values, rows.Err() +} +func (s sqlStore) ListMessages(ctx context.Context, actorID, conversationID uuid.UUID) ([]Message, error) { + rows, err := s.database.QueryContext(ctx, `select m.id,m.conversation_id,m.sender_internal_user_id,m.body,m.created_at from public.conversation_messages m join public.conversations c on c.id=m.conversation_id where c.id=$1 and ($2=c.customer_internal_user_id or $2=c.provider_internal_user_id) order by m.created_at,m.id`, conversationID, actorID) + if err != nil { + return nil, err + } + defer rows.Close() + values := []Message{} + for rows.Next() { + var value Message + if err := rows.Scan(&value.ID, &value.ConversationID, &value.SenderID, &value.Body, &value.CreatedAt); err != nil { + return nil, err + } + values = append(values, value) + } + return values, rows.Err() +} +func (s sqlStore) Send(ctx context.Context, actorID, conversationID uuid.UUID, body string) (Message, error) { + tx, err := s.database.BeginTx(ctx, nil) + if err != nil { + return Message{}, err + } + defer tx.Rollback() + var customerID, providerID uuid.UUID + var blockedBy *uuid.UUID + if err = tx.QueryRowContext(ctx, `select customer_internal_user_id,provider_internal_user_id,blocked_by_internal_user_id from public.conversations where id=$1 for update`, conversationID).Scan(&customerID, &providerID, &blockedBy); err != nil { + if errors.Is(err, sql.ErrNoRows) { + return Message{}, ErrNotFound + } + return Message{}, err + } + if actorID != customerID && actorID != providerID { + return Message{}, ErrForbidden + } + if blockedBy != nil { + return Message{}, ErrForbidden + } + var value Message + err = tx.QueryRowContext(ctx, `insert into public.conversation_messages (conversation_id,sender_internal_user_id,body) values ($1,$2,$3) returning id,conversation_id,sender_internal_user_id,body,created_at`, conversationID, actorID, body).Scan(&value.ID, &value.ConversationID, &value.SenderID, &value.Body, &value.CreatedAt) + if err != nil { + return Message{}, err + } + recipient := customerID + if actorID == customerID { + recipient = providerID + } + if _, err = tx.ExecContext(ctx, `insert into public.notifications (recipient_internal_user_id,kind,resource_id,in_app_visible) + select $1,'message_received',$2,coalesce((select in_app_enabled from public.notification_preferences where internal_user_id=$1),true) + where coalesce((select in_app_enabled or email_enabled from public.notification_preferences where internal_user_id=$1),true)`, recipient, value.ID); err != nil { + return Message{}, err + } + if _, err = tx.ExecContext(ctx, `insert into public.notification_email_outbox (notification_id,recipient_internal_user_id) + select id,recipient_internal_user_id from public.notifications where recipient_internal_user_id=$1 and kind='message_received' and resource_id=$2 + and coalesce((select email_enabled from public.notification_preferences where internal_user_id=$1),true)`, recipient, value.ID); err != nil { + return Message{}, err + } + if _, err = tx.ExecContext(ctx, `update public.conversations set updated_at=timezone('utc',now()) where id=$1`, conversationID); err != nil { + return Message{}, err + } + if err = tx.Commit(); err != nil { + return Message{}, err + } + return value, nil +} +func (s sqlStore) SetBlocked(ctx context.Context, actorID, conversationID uuid.UUID, blocked bool) error { + var result sql.Result + var err error + if blocked { + result, err = s.database.ExecContext(ctx, `update public.conversations set blocked_by_internal_user_id=$1,updated_at=timezone('utc',now()) where id=$2 and ($1=customer_internal_user_id or $1=provider_internal_user_id)`, actorID, conversationID) + } else { + result, err = s.database.ExecContext(ctx, `update public.conversations set blocked_by_internal_user_id=null,updated_at=timezone('utc',now()) where id=$2 and blocked_by_internal_user_id=$1`, actorID, conversationID) + } + return affected(result, err) +} +func (s sqlStore) Report(ctx context.Context, actorID, conversationID uuid.UUID, messageID *uuid.UUID, reason string) error { + result, err := s.database.ExecContext(ctx, `insert into public.conversation_reports (conversation_id,message_id,reporter_internal_user_id,reason) select $1,$2,$3,$4 where exists (select 1 from public.conversations where id=$1 and ($3=customer_internal_user_id or $3=provider_internal_user_id)) and ($2::uuid is null or exists(select 1 from public.conversation_messages where id=$2 and conversation_id=$1))`, conversationID, messageID, actorID, reason) + return affected(result, err) +} +func (s sqlStore) Preferences(ctx context.Context, actorID uuid.UUID) (NotificationPreferences, error) { + var v NotificationPreferences + err := s.database.QueryRowContext(ctx, `select coalesce((select in_app_enabled from public.notification_preferences where internal_user_id=$1),true),coalesce((select email_enabled from public.notification_preferences where internal_user_id=$1),true)`, actorID).Scan(&v.InAppEnabled, &v.EmailEnabled) + return v, err +} +func (s sqlStore) ReplacePreferences(ctx context.Context, actorID uuid.UUID, v NotificationPreferences) (NotificationPreferences, error) { + err := s.database.QueryRowContext(ctx, `insert into public.notification_preferences (internal_user_id,in_app_enabled,email_enabled) values($1,$2,$3) on conflict(internal_user_id) do update set in_app_enabled=excluded.in_app_enabled,email_enabled=excluded.email_enabled,updated_at=timezone('utc',now()) returning in_app_enabled,email_enabled`, actorID, v.InAppEnabled, v.EmailEnabled).Scan(&v.InAppEnabled, &v.EmailEnabled) + return v, err +} +func (s sqlStore) Notifications(ctx context.Context, actorID uuid.UUID) ([]Notification, error) { + rows, err := s.database.QueryContext(ctx, `select id,kind,read_at,created_at from public.notifications where recipient_internal_user_id=$1 and in_app_visible order by created_at desc,id limit 100`, actorID) + if err != nil { + return nil, err + } + defer rows.Close() + values := []Notification{} + for rows.Next() { + var v Notification + if err := rows.Scan(&v.ID, &v.Kind, &v.ReadAt, &v.CreatedAt); err != nil { + return nil, err + } + values = append(values, v) + } + return values, rows.Err() +} +func (s sqlStore) MarkNotificationRead(ctx context.Context, actorID, notificationID uuid.UUID) error { + result, err := s.database.ExecContext(ctx, `update public.notifications set read_at=coalesce(read_at,timezone('utc',now())) where id=$1 and recipient_internal_user_id=$2`, notificationID, actorID) + return affected(result, err) +} +func affected(result sql.Result, err error) error { + if err != nil { + return err + } + count, err := result.RowsAffected() + if err != nil { + return err + } + if count == 0 { + return ErrForbidden + } + return nil +} + +var _ = time.Time{} diff --git a/backend/internal/moderation/ent_repository_test.go b/backend/internal/moderation/ent_repository_test.go new file mode 100644 index 0000000..958df9d --- /dev/null +++ b/backend/internal/moderation/ent_repository_test.go @@ -0,0 +1,54 @@ +package moderation + +import ( + "context" + "database/sql" + "os" + "testing" + + "entgo.io/ent/dialect" + entsql "entgo.io/ent/dialect/sql" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/platformrole" + "github.com/SourceSenseiTheRealOne/juntly/backend/internal/users" + "github.com/google/uuid" + _ "github.com/jackc/pgx/v5/stdlib" +) + +func TestEntRepositoryReadsOnlyPersistedModeratorGrant(t *testing.T) { + url := os.Getenv("TEST_DATABASE_URL") + if url == "" { + t.Skip("TEST_DATABASE_URL is required for PostgreSQL integration tests") + } + database, err := sql.Open("pgx", url) + if err != nil { + t.Fatalf("open database: %v", err) + } + client := ent.NewClient(ent.Driver(entsql.OpenDB(dialect.Postgres, database))) + t.Cleanup(func() { _ = client.Close() }) + ctx := context.Background() + user, _, err := users.NewService(users.NewEntRepository(client)).Reconcile(ctx, users.VerifiedIdentity{Subject: "moderation_user_" + uuid.NewString()}) + if err != nil { + t.Fatalf("reconcile user: %v", err) + } + t.Cleanup(func() { + if _, err := client.PlatformRole.Delete().Where(platformrole.InternalUserIDEQ(user.ID)).Exec(ctx); err != nil { + t.Errorf("cleanup grant: %v", err) + } + if err := client.InternalUser.DeleteOneID(user.ID).Exec(ctx); err != nil { + t.Errorf("cleanup user: %v", err) + } + }) + repository := NewEntRepository(client) + granted, err := repository.HasModeratorGrant(ctx, user.ID) + if err != nil || granted { + t.Fatalf("pre-grant = %v, err = %v", granted, err) + } + if err := client.PlatformRole.Create().SetInternalUserID(user.ID).SetRole("moderator").Exec(ctx); err != nil { + t.Fatalf("grant moderator: %v", err) + } + granted, err = repository.HasModeratorGrant(ctx, user.ID) + if err != nil || !granted { + t.Fatalf("moderator grant = %v, err = %v", granted, err) + } +} diff --git a/backend/internal/moderation/queue_service.go b/backend/internal/moderation/queue_service.go new file mode 100644 index 0000000..bb095aa --- /dev/null +++ b/backend/internal/moderation/queue_service.go @@ -0,0 +1,36 @@ +package moderation + +import ( + "context" + + "github.com/SourceSenseiTheRealOne/juntly/backend/internal/listings" + "github.com/SourceSenseiTheRealOne/juntly/backend/internal/users" +) + +type PendingListingReader interface { + ListPending(context.Context) ([]listings.Listing, error) +} +type QueueService interface { + ListPending(context.Context, users.VerifiedIdentity) ([]listings.Listing, error) +} +type queueService struct { + moderators Service + repository PendingListingReader +} + +func NewQueueService(moderators Service, repository PendingListingReader) QueueService { + return queueService{moderators: moderators, repository: repository} +} +func (s queueService) ListPending(ctx context.Context, identity users.VerifiedIdentity) ([]listings.Listing, error) { + if s.moderators == nil || s.repository == nil { + return nil, ErrUnavailable + } + if _, err := s.moderators.RequireModerator(ctx, identity); err != nil { + return nil, err + } + values, err := s.repository.ListPending(ctx) + if err != nil { + return nil, ErrUnavailable + } + return values, nil +} diff --git a/backend/internal/moderation/queue_service_test.go b/backend/internal/moderation/queue_service_test.go new file mode 100644 index 0000000..97d78be --- /dev/null +++ b/backend/internal/moderation/queue_service_test.go @@ -0,0 +1,44 @@ +package moderation + +import ( + "context" + "errors" + "testing" + + "github.com/SourceSenseiTheRealOne/juntly/backend/internal/listings" + "github.com/SourceSenseiTheRealOne/juntly/backend/internal/users" +) + +func TestQueueServiceRequiresModeratorBeforePendingListingRead(t *testing.T) { + t.Parallel() + authorizer := &recordingModeratorAuthorizer{user: users.InternalUser{}} + repository := &recordingPendingRepository{listings: []listings.Listing{{State: listings.StatePendingReview}}} + values, err := NewQueueService(authorizer, repository).ListPending(context.Background(), users.VerifiedIdentity{Subject: "moderator"}) + if err != nil || len(values) != 1 || repository.calls != 1 { + t.Fatalf("values/err/calls=%#v/%v/%d", values, err, repository.calls) + } + _, err = NewQueueService(&recordingModeratorAuthorizer{err: ErrForbidden}, repository).ListPending(context.Background(), users.VerifiedIdentity{}) + if !errors.Is(err, ErrForbidden) || repository.calls != 1 { + t.Fatalf("forbidden/calls=%v/%d", err, repository.calls) + } +} + +type recordingModeratorAuthorizer struct { + user users.InternalUser + err error +} + +func (a *recordingModeratorAuthorizer) RequireModerator(context.Context, users.VerifiedIdentity) (users.InternalUser, error) { + return a.user, a.err +} + +type recordingPendingRepository struct { + listings []listings.Listing + calls int + err error +} + +func (r *recordingPendingRepository) ListPending(context.Context) ([]listings.Listing, error) { + r.calls++ + return r.listings, r.err +} diff --git a/backend/internal/moderation/repository.go b/backend/internal/moderation/repository.go new file mode 100644 index 0000000..913c14b --- /dev/null +++ b/backend/internal/moderation/repository.go @@ -0,0 +1,24 @@ +package moderation + +import ( + "context" + "errors" + + jent "github.com/SourceSenseiTheRealOne/juntly/backend/ent" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/platformrole" + "github.com/google/uuid" +) + +type entRepository struct{ client *jent.Client } + +func NewEntRepository(client *jent.Client) Repository { return entRepository{client: client} } + +func (r entRepository) HasModeratorGrant(ctx context.Context, internalUserID uuid.UUID) (bool, error) { + if r.client == nil || internalUserID == uuid.Nil { + return false, errors.New("platform role persistence unavailable") + } + return r.client.PlatformRole.Query().Where( + platformrole.InternalUserIDEQ(internalUserID), + platformrole.RoleEQ("moderator"), + ).Exist(ctx) +} diff --git a/backend/internal/moderation/review_service.go b/backend/internal/moderation/review_service.go new file mode 100644 index 0000000..7a0b59e --- /dev/null +++ b/backend/internal/moderation/review_service.go @@ -0,0 +1,48 @@ +package moderation + +import ( + "context" + + "github.com/SourceSenseiTheRealOne/juntly/backend/internal/listings" + "github.com/SourceSenseiTheRealOne/juntly/backend/internal/users" + "github.com/google/uuid" +) + +type ListingQueue interface { + ListPending(context.Context, users.VerifiedIdentity) ([]listings.Listing, error) +} +type ModeratedLifecycle interface { + Approve(context.Context, users.VerifiedIdentity, uuid.UUID, int) (listings.Listing, error) + Reject(context.Context, users.VerifiedIdentity, uuid.UUID, int, string) (listings.Listing, error) +} +type ReviewService interface { + ListPending(context.Context, users.VerifiedIdentity) ([]listings.Listing, error) + Approve(context.Context, users.VerifiedIdentity, uuid.UUID, int) (listings.Listing, error) + Reject(context.Context, users.VerifiedIdentity, uuid.UUID, int, string) (listings.Listing, error) +} +type reviewService struct { + queue ListingQueue + lifecycle ModeratedLifecycle +} + +func NewReviewService(queue ListingQueue, lifecycle ModeratedLifecycle) ReviewService { + return reviewService{queue: queue, lifecycle: lifecycle} +} +func (s reviewService) ListPending(ctx context.Context, i users.VerifiedIdentity) ([]listings.Listing, error) { + if s.queue == nil { + return nil, ErrUnavailable + } + return s.queue.ListPending(ctx, i) +} +func (s reviewService) Approve(ctx context.Context, i users.VerifiedIdentity, id uuid.UUID, r int) (listings.Listing, error) { + if s.lifecycle == nil { + return listings.Listing{}, ErrUnavailable + } + return s.lifecycle.Approve(ctx, i, id, r) +} +func (s reviewService) Reject(ctx context.Context, i users.VerifiedIdentity, id uuid.UUID, r int, reason string) (listings.Listing, error) { + if s.lifecycle == nil { + return listings.Listing{}, ErrUnavailable + } + return s.lifecycle.Reject(ctx, i, id, r, reason) +} diff --git a/backend/internal/moderation/review_service_test.go b/backend/internal/moderation/review_service_test.go new file mode 100644 index 0000000..9e96633 --- /dev/null +++ b/backend/internal/moderation/review_service_test.go @@ -0,0 +1,49 @@ +package moderation + +import ( + "context" + "testing" + + "github.com/SourceSenseiTheRealOne/juntly/backend/internal/listings" + "github.com/SourceSenseiTheRealOne/juntly/backend/internal/users" + "github.com/google/uuid" +) + +func TestReviewServiceDelegatesQueueAndModeratedTransitions(t *testing.T) { + t.Parallel() + queue := &recordingQueue{listings: []listings.Listing{{State: listings.StatePendingReview}}} + lifecycle := &recordingReviewLifecycle{listing: listings.Listing{State: listings.StateActive}} + service := NewReviewService(queue, lifecycle) + if values, err := service.ListPending(context.Background(), users.VerifiedIdentity{Subject: "moderator"}); err != nil || len(values) != 1 { + t.Fatalf("queue=%#v err=%v", values, err) + } + if _, err := service.Approve(context.Background(), users.VerifiedIdentity{Subject: "moderator"}, uuid.New(), 1); err != nil { + t.Fatalf("approve=%v", err) + } + if _, err := service.Reject(context.Background(), users.VerifiedIdentity{Subject: "moderator"}, uuid.New(), 2, "Needs scope"); err != nil { + t.Fatalf("reject=%v", err) + } + if lifecycle.approvals != 1 || lifecycle.rejections != 1 { + t.Fatalf("lifecycle=%#v", lifecycle) + } +} + +type recordingQueue struct{ listings []listings.Listing } + +func (q *recordingQueue) ListPending(context.Context, users.VerifiedIdentity) ([]listings.Listing, error) { + return q.listings, nil +} + +type recordingReviewLifecycle struct { + listing listings.Listing + approvals, rejections int +} + +func (s *recordingReviewLifecycle) Approve(context.Context, users.VerifiedIdentity, uuid.UUID, int) (listings.Listing, error) { + s.approvals++ + return s.listing, nil +} +func (s *recordingReviewLifecycle) Reject(context.Context, users.VerifiedIdentity, uuid.UUID, int, string) (listings.Listing, error) { + s.rejections++ + return s.listing, nil +} diff --git a/backend/internal/moderation/service.go b/backend/internal/moderation/service.go new file mode 100644 index 0000000..6b95a9d --- /dev/null +++ b/backend/internal/moderation/service.go @@ -0,0 +1,53 @@ +package moderation + +import ( + "context" + "errors" + + "github.com/SourceSenseiTheRealOne/juntly/backend/internal/users" + "github.com/google/uuid" +) + +var ( + ErrUnauthorized = errors.New("moderator access unauthorized") + ErrForbidden = errors.New("moderator access forbidden") + ErrUnavailable = errors.New("moderator access unavailable") +) + +type InternalUserReconciler interface { + Reconcile(context.Context, users.VerifiedIdentity) (users.InternalUser, bool, error) +} +type Repository interface { + HasModeratorGrant(context.Context, uuid.UUID) (bool, error) +} +type Service interface { + RequireModerator(context.Context, users.VerifiedIdentity) (users.InternalUser, error) +} +type service struct { + identities InternalUserReconciler + repository Repository +} + +func NewService(identities InternalUserReconciler, repository Repository) Service { + return service{identities: identities, repository: repository} +} +func (s service) RequireModerator(ctx context.Context, identity users.VerifiedIdentity) (users.InternalUser, error) { + if s.identities == nil || s.repository == nil { + return users.InternalUser{}, ErrUnavailable + } + user, _, err := s.identities.Reconcile(ctx, identity) + if err != nil { + if errors.Is(err, users.ErrInvalidIdentity) { + return users.InternalUser{}, ErrUnauthorized + } + return users.InternalUser{}, ErrUnavailable + } + granted, err := s.repository.HasModeratorGrant(ctx, user.ID) + if err != nil { + return users.InternalUser{}, ErrUnavailable + } + if !granted { + return users.InternalUser{}, ErrForbidden + } + return user, nil +} diff --git a/backend/internal/moderation/service_test.go b/backend/internal/moderation/service_test.go new file mode 100644 index 0000000..c14c506 --- /dev/null +++ b/backend/internal/moderation/service_test.go @@ -0,0 +1,64 @@ +package moderation + +import ( + "context" + "errors" + "testing" + + "github.com/SourceSenseiTheRealOne/juntly/backend/internal/users" + "github.com/google/uuid" +) + +func TestServiceRequiresPersistedModeratorGrant(t *testing.T) { + t.Parallel() + moderator := users.InternalUser{ID: uuid.MustParse("eeeeeeee-eeee-deee-0eee-eeeeeeeeeeee")} + repository := &recordingRepository{granted: true} + resolved, err := NewService(&recordingReconciler{user: moderator}, repository).RequireModerator(context.Background(), users.VerifiedIdentity{Subject: "moderator"}) + if err != nil || resolved != moderator || repository.owner != moderator.ID { + t.Fatalf("resolved/err/owner = %#v/%v/%s", resolved, err, repository.owner) + } +} + +func TestServiceRejectsMissingOrInvalidModeratorBeforeLifecycle(t *testing.T) { + t.Parallel() + for _, test := range []struct { + reconcilerErr error + granted bool + want error + }{ + {reconcilerErr: users.ErrInvalidIdentity, want: ErrUnauthorized}, + {granted: false, want: ErrForbidden}, + {reconcilerErr: errors.New("database private detail"), want: ErrUnavailable}, + } { + repository := &recordingRepository{granted: test.granted} + _, err := NewService(&recordingReconciler{err: test.reconcilerErr}, repository).RequireModerator(context.Background(), users.VerifiedIdentity{}) + if !errors.Is(err, test.want) { + t.Fatalf("error = %v, want %v", err, test.want) + } + if test.reconcilerErr != nil && repository.calls != 0 { + t.Fatalf("role repository calls = %d, want 0", repository.calls) + } + } +} + +type recordingReconciler struct { + user users.InternalUser + err error +} + +func (r *recordingReconciler) Reconcile(context.Context, users.VerifiedIdentity) (users.InternalUser, bool, error) { + return r.user, false, r.err +} + +type recordingRepository struct { + owner uuid.UUID + granted bool + calls int + err error +} + +func (r *recordingRepository) HasModeratorGrant(_ context.Context, owner uuid.UUID) (bool, error) { + r.calls++ + r.owner = owner + return r.granted, r.err +} diff --git a/backend/internal/payments/migration_contract_test.go b/backend/internal/payments/migration_contract_test.go new file mode 100644 index 0000000..644fea9 --- /dev/null +++ b/backend/internal/payments/migration_contract_test.go @@ -0,0 +1,57 @@ +package payments + +import ( + "os" + "path/filepath" + "strings" + "testing" +) + +func TestPaymentMigrationDefinesDurableMoneyAndWebhookInvariants(t *testing.T) { + migration := paymentMigration(t) + for _, requirement := range []string{ + "create table public.provider_payment_accounts", + "stripe_account_id text not null unique", + "create table public.payment_orders", + "gross_minor integer not null", + "platform_fee_minor integer not null", + "provider_net_minor integer not null", + "unique(booking_id)", + "stripe_checkout_session_id text unique", + "stripe_payment_intent_id text unique", + "stripe_invoice_id text", + "create table public.payment_events", + "create table public.stripe_webhook_receipts", + "stripe_event_id text primary key", + "create table public.payment_disputes", + } { + if !strings.Contains(migration, requirement) { + t.Errorf("payment migration does not contain %q", requirement) + } + } + for _, prohibited := range []string{"card_number", "bank_account", "raw_payload", "webhook_signature"} { + if strings.Contains(migration, prohibited) { + t.Errorf("payment migration must not persist %q", prohibited) + } + } +} + +func paymentMigration(t *testing.T) string { + t.Helper() + directory := filepath.Join("..", "..", "..", "supabase", "migrations") + entries, err := os.ReadDir(directory) + if err != nil { + t.Fatalf("read migrations: %v", err) + } + for _, entry := range entries { + if strings.HasSuffix(entry.Name(), "_create_payments.sql") { + contents, err := os.ReadFile(filepath.Join(directory, entry.Name())) + if err != nil { + t.Fatalf("read payment migration: %v", err) + } + return strings.ToLower(string(contents)) + } + } + t.Fatal("payment migration not found") + return "" +} diff --git a/backend/internal/payments/model.go b/backend/internal/payments/model.go new file mode 100644 index 0000000..64b756f --- /dev/null +++ b/backend/internal/payments/model.go @@ -0,0 +1,118 @@ +package payments + +import ( + "errors" + "time" +) + +var ( + ErrInvalid = errors.New("payment invalid request") + ErrUnauthorized = errors.New("payment unauthorized") + ErrForbidden = errors.New("payment forbidden") + ErrNotFound = errors.New("payment not found") + ErrConflict = errors.New("payment conflict") + ErrUnavailable = errors.New("payment unavailable") +) + +type State string + +const ( + StatePendingCheckout State = "pending_checkout" + StateCheckoutCreated State = "checkout_created" + StateProcessing State = "processing" + StatePaid State = "paid" + StateFailed State = "failed" + StateRefundPending State = "refund_pending" + StateRefunded State = "refunded" + StateDisputed State = "disputed" + StateDisputeWon State = "dispute_won" + StateDisputeLost State = "dispute_lost" + StateCancelled State = "cancelled" +) + +type EventKind string + +const ( + EventProcessing EventKind = "processing" + EventPaid EventKind = "paid" + EventFailed EventKind = "failed" + EventRefunded EventKind = "refunded" + EventDisputeOpened EventKind = "dispute_opened" + EventDisputeWon EventKind = "dispute_won" + EventDisputeLost EventKind = "dispute_lost" + EventAccountUpdate EventKind = "account_updated" +) + +type Order struct { + ID string `json:"id"` + BookingID string `json:"bookingId"` + CustomerID string `json:"customerId"` + ProviderID string `json:"providerId"` + State State `json:"state"` + GrossMinor int64 `json:"grossMinor"` + PlatformFeeMinor int64 `json:"platformFeeMinor"` + ProviderNetMinor int64 `json:"providerNetMinor"` + Currency string `json:"currency"` + CheckoutSessionID string `json:"-"` + PaymentIntentID string `json:"-"` + InvoiceID string `json:"-"` + RefundID string `json:"-"` + CreatedAt time.Time `json:"createdAt"` + UpdatedAt time.Time `json:"updatedAt"` +} + +type ProviderAccount struct { + InternalUserID string `json:"-"` + StripeAccountID string `json:"-"` + DetailsSubmitted bool `json:"detailsSubmitted"` + ChargesEnabled bool `json:"chargesEnabled"` + PayoutsEnabled bool `json:"payoutsEnabled"` + UpdatedAt time.Time `json:"updatedAt"` +} + +type CheckoutRequest struct { + OrderID string + BookingID string + ConnectedAccountID string + GrossMinor int64 + FeeMinor int64 + Locale string + IdempotencyKey string +} + +type CheckoutSession struct { + ID string + URL string +} + +type ConnectedAccount struct { + ID string + DetailsSubmitted bool + ChargesEnabled bool + PayoutsEnabled bool +} + +type AccountLink struct{ URL string } + +type RefundResult struct{ ID string } + +type ProviderEvent struct { + ID string + Kind EventKind + ProviderObjectID string + OrderID string + PaymentIntentID string + InvoiceID string + RefundID string + AccountID string + ChargeID string + DisputeID string + DisputeState string + DisputeReason string + AmountMinor int64 + Currency string + OccurredAt time.Time + DetailsSubmitted bool + ChargesEnabled bool + PayoutsEnabled bool +} diff --git a/backend/internal/payments/service.go b/backend/internal/payments/service.go new file mode 100644 index 0000000..aff657f --- /dev/null +++ b/backend/internal/payments/service.go @@ -0,0 +1,248 @@ +package payments + +import ( + "context" + "errors" + "regexp" + "strings" + + "github.com/SourceSenseiTheRealOne/juntly/backend/internal/users" + "github.com/google/uuid" +) + +var paymentIdempotencyPattern = regexp.MustCompile(`^[A-Za-z0-9._:-]{8,128}$`) + +type IdentityReconciler interface { + Reconcile(context.Context, users.VerifiedIdentity) (users.InternalUser, bool, error) +} + +type ModeratorAuthorizer interface { + RequireModerator(context.Context, users.VerifiedIdentity) (users.InternalUser, error) +} + +type Store interface { + PrepareCheckout(context.Context, uuid.UUID, uuid.UUID, string, int) (Order, ProviderAccount, error) + AttachCheckout(context.Context, uuid.UUID, uuid.UUID, CheckoutSession) (Order, error) + ListOrders(context.Context, uuid.UUID) ([]Order, error) + ListAdminOrders(context.Context, uuid.UUID) ([]Order, error) + GetProviderAccount(context.Context, uuid.UUID) (ProviderAccount, error) + SaveProviderAccount(context.Context, uuid.UUID, ConnectedAccount) (ProviderAccount, error) + ApplyProviderEvent(context.Context, ProviderEvent) error + PrepareRefund(context.Context, uuid.UUID, uuid.UUID) (Order, error) + AttachRefund(context.Context, uuid.UUID, uuid.UUID, RefundResult) (Order, error) +} + +type Gateway interface { + CreateCheckout(context.Context, CheckoutRequest) (CheckoutSession, error) + CreateConnectedAccount(context.Context, string) (ConnectedAccount, error) + CreateAccountLink(context.Context, string, string) (AccountLink, error) + GetConnectedAccount(context.Context, string) (ConnectedAccount, error) + CreateRefund(context.Context, string, string) (RefundResult, error) + VerifyWebhook([]byte, string) (ProviderEvent, error) +} + +type CheckoutResult struct { + Order Order `json:"order"` + URL string `json:"url"` +} + +type PayoutOnboardingResult struct { + Account ProviderAccount `json:"account"` + URL string `json:"url"` +} + +type Service interface { + BeginCheckout(context.Context, users.VerifiedIdentity, uuid.UUID, string, string) (CheckoutResult, error) + ListOrders(context.Context, users.VerifiedIdentity) ([]Order, error) + ListAdminOrders(context.Context, users.VerifiedIdentity) ([]Order, error) + BeginPayoutOnboarding(context.Context, users.VerifiedIdentity, string) (PayoutOnboardingResult, error) + PayoutStatus(context.Context, users.VerifiedIdentity) (ProviderAccount, error) + HandleWebhook(context.Context, []byte, string) error + Refund(context.Context, users.VerifiedIdentity, uuid.UUID, string) (Order, error) +} + +type service struct { + identities IdentityReconciler + moderators ModeratorAuthorizer + store Store + gateway Gateway + feeBPS int +} + +func NewService(identities IdentityReconciler, moderators ModeratorAuthorizer, store Store, gateway Gateway, feeBPS int) Service { + return service{identities: identities, moderators: moderators, store: store, gateway: gateway, feeBPS: feeBPS} +} + +func (s service) BeginCheckout(ctx context.Context, identity users.VerifiedIdentity, bookingID uuid.UUID, idempotencyKey, locale string) (CheckoutResult, error) { + idempotencyKey = strings.TrimSpace(idempotencyKey) + if bookingID == uuid.Nil || !paymentIdempotencyPattern.MatchString(idempotencyKey) || s.feeBPS < 0 || s.feeBPS >= 10_000 { + return CheckoutResult{}, ErrInvalid + } + actor, err := s.actor(ctx, identity) + if err != nil { + return CheckoutResult{}, err + } + if s.gateway == nil { + return CheckoutResult{}, ErrUnavailable + } + order, account, err := s.store.PrepareCheckout(ctx, actor.ID, bookingID, idempotencyKey, s.feeBPS) + if err != nil { + return CheckoutResult{}, normalize(err) + } + if !account.DetailsSubmitted || !account.ChargesEnabled || !account.PayoutsEnabled { + return CheckoutResult{}, ErrForbidden + } + session, err := s.gateway.CreateCheckout(ctx, CheckoutRequest{OrderID: order.ID, BookingID: order.BookingID, ConnectedAccountID: account.StripeAccountID, GrossMinor: order.GrossMinor, FeeMinor: order.PlatformFeeMinor, Locale: locale, IdempotencyKey: "checkout-" + order.ID}) + if err != nil { + return CheckoutResult{}, normalize(err) + } + orderID, err := uuid.Parse(order.ID) + if err != nil { + return CheckoutResult{}, ErrUnavailable + } + attached, err := s.store.AttachCheckout(ctx, actor.ID, orderID, session) + if err != nil { + return CheckoutResult{}, normalize(err) + } + return CheckoutResult{Order: attached, URL: session.URL}, nil +} + +func (s service) ListOrders(ctx context.Context, identity users.VerifiedIdentity) ([]Order, error) { + actor, err := s.actor(ctx, identity) + if err != nil { + return nil, err + } + orders, err := s.store.ListOrders(ctx, actor.ID) + return orders, normalize(err) +} + +func (s service) ListAdminOrders(ctx context.Context, identity users.VerifiedIdentity) ([]Order, error) { + if s.moderators == nil || s.store == nil { + return nil, ErrUnavailable + } + moderator, err := s.moderators.RequireModerator(ctx, identity) + if err != nil { + return nil, normalize(err) + } + orders, err := s.store.ListAdminOrders(ctx, moderator.ID) + return orders, normalize(err) +} + +func (s service) BeginPayoutOnboarding(ctx context.Context, identity users.VerifiedIdentity, locale string) (PayoutOnboardingResult, error) { + actor, err := s.actor(ctx, identity) + if err != nil { + return PayoutOnboardingResult{}, err + } + if s.gateway == nil { + return PayoutOnboardingResult{}, ErrUnavailable + } + account, err := s.store.GetProviderAccount(ctx, actor.ID) + if errors.Is(err, ErrNotFound) { + created, createErr := s.gateway.CreateConnectedAccount(ctx, "connect-"+actor.ID.String()) + if createErr != nil { + return PayoutOnboardingResult{}, normalize(createErr) + } + account, err = s.store.SaveProviderAccount(ctx, actor.ID, created) + } else if err == nil { + refreshed, refreshErr := s.gateway.GetConnectedAccount(ctx, account.StripeAccountID) + if refreshErr != nil { + return PayoutOnboardingResult{}, normalize(refreshErr) + } + account, err = s.store.SaveProviderAccount(ctx, actor.ID, refreshed) + } + if err != nil { + return PayoutOnboardingResult{}, normalize(err) + } + link, err := s.gateway.CreateAccountLink(ctx, account.StripeAccountID, supportedLocale(locale)) + if err != nil { + return PayoutOnboardingResult{}, normalize(err) + } + return PayoutOnboardingResult{Account: account, URL: link.URL}, nil +} + +func (s service) PayoutStatus(ctx context.Context, identity users.VerifiedIdentity) (ProviderAccount, error) { + actor, err := s.actor(ctx, identity) + if err != nil { + return ProviderAccount{}, err + } + account, err := s.store.GetProviderAccount(ctx, actor.ID) + if err != nil { + return ProviderAccount{}, normalize(err) + } + if s.gateway == nil { + return ProviderAccount{}, ErrUnavailable + } + refreshed, err := s.gateway.GetConnectedAccount(ctx, account.StripeAccountID) + if err != nil { + return ProviderAccount{}, normalize(err) + } + account, err = s.store.SaveProviderAccount(ctx, actor.ID, refreshed) + return account, normalize(err) +} + +func (s service) HandleWebhook(ctx context.Context, payload []byte, signature string) error { + if s.gateway == nil || s.store == nil { + return ErrUnavailable + } + event, err := s.gateway.VerifyWebhook(payload, signature) + if err != nil { + return normalize(err) + } + return normalize(s.store.ApplyProviderEvent(ctx, event)) +} + +func (s service) Refund(ctx context.Context, identity users.VerifiedIdentity, orderID uuid.UUID, idempotencyKey string) (Order, error) { + if orderID == uuid.Nil || !paymentIdempotencyPattern.MatchString(idempotencyKey) || s.moderators == nil || s.gateway == nil || s.store == nil { + return Order{}, ErrInvalid + } + moderator, err := s.moderators.RequireModerator(ctx, identity) + if err != nil { + return Order{}, normalize(err) + } + order, err := s.store.PrepareRefund(ctx, moderator.ID, orderID) + if err != nil { + return Order{}, normalize(err) + } + if order.State == StateRefundPending { + return order, nil + } + refund, err := s.gateway.CreateRefund(ctx, order.PaymentIntentID, "refund-"+order.ID) + if err != nil { + return Order{}, normalize(err) + } + order, err = s.store.AttachRefund(ctx, moderator.ID, orderID, refund) + return order, normalize(err) +} + +func (s service) actor(ctx context.Context, identity users.VerifiedIdentity) (users.InternalUser, error) { + if s.identities == nil || s.store == nil { + return users.InternalUser{}, ErrUnavailable + } + actor, _, err := s.identities.Reconcile(ctx, identity) + if err != nil || actor.ID == uuid.Nil { + if errors.Is(err, users.ErrInvalidIdentity) { + return users.InternalUser{}, ErrUnauthorized + } + return users.InternalUser{}, ErrUnavailable + } + return actor, nil +} + +func supportedLocale(locale string) string { + if locale == "en" || locale == "es" || locale == "pt-PT" { + return locale + } + return "pt-PT" +} + +func normalize(err error) error { + if err == nil { + return nil + } + for _, known := range []error{ErrInvalid, ErrUnauthorized, ErrForbidden, ErrNotFound, ErrConflict} { + if errors.Is(err, known) { + return known + } + } + return ErrUnavailable +} diff --git a/backend/internal/payments/service_test.go b/backend/internal/payments/service_test.go new file mode 100644 index 0000000..a9f7315 --- /dev/null +++ b/backend/internal/payments/service_test.go @@ -0,0 +1,160 @@ +package payments + +import ( + "context" + "testing" + + "github.com/SourceSenseiTheRealOne/juntly/backend/internal/users" + "github.com/google/uuid" +) + +func TestServiceBeginsCheckoutFromDurableServerAmounts(t *testing.T) { + actor := users.InternalUser{ID: uuid.MustParse("aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa")} + store := &recordingStore{ + order: Order{ID: "bbbbbbbb-bbbb-4bbb-8bbb-bbbbbbbbbbbb", BookingID: "cccccccc-cccc-4ccc-8ccc-cccccccccccc", CustomerID: actor.ID.String(), ProviderID: "dddddddd-dddd-4ddd-8ddd-dddddddddddd", State: StatePendingCheckout, GrossMinor: 12500, PlatformFeeMinor: 1250, ProviderNetMinor: 11250, Currency: "EUR"}, + account: ProviderAccount{StripeAccountID: "acct_provider", DetailsSubmitted: true, ChargesEnabled: true, PayoutsEnabled: true}, + } + gateway := &recordingGateway{checkout: CheckoutSession{ID: "cs_test_checkout", URL: "https://checkout.stripe.test/session"}} + service := NewService(staticIdentity{user: actor}, nil, store, gateway, 1000) + + result, err := service.BeginCheckout(context.Background(), users.VerifiedIdentity{Subject: "customer"}, uuid.MustParse(store.order.BookingID), "checkout-key-123", "pt-PT") + if err != nil { + t.Fatalf("begin checkout: %v", err) + } + if result.URL != gateway.checkout.URL || gateway.checkoutInput.GrossMinor != 12500 || gateway.checkoutInput.FeeMinor != 1250 || gateway.checkoutInput.ConnectedAccountID != "acct_provider" { + t.Fatalf("checkout result=%#v input=%#v", result, gateway.checkoutInput) + } + if store.attachedSession.ID != "cs_test_checkout" { + t.Fatalf("session not attached: %#v", store.attachedSession) + } +} + +func TestServiceVerifiesWebhookBeforeStoreMutation(t *testing.T) { + store := &recordingStore{} + gateway := &recordingGateway{event: ProviderEvent{ID: "evt_paid", Kind: EventPaid, OrderID: "bbbbbbbb-bbbb-4bbb-8bbb-bbbbbbbbbbbb", ProviderObjectID: "cs_paid"}} + service := NewService(staticIdentity{}, nil, store, gateway, 1000) + if err := service.HandleWebhook(context.Background(), []byte("signed"), "t=1,v1=synthetic"); err != nil { + t.Fatalf("webhook: %v", err) + } + if store.event.ID != "evt_paid" || string(gateway.payload) != "signed" { + t.Fatalf("event=%#v payload=%q", store.event, gateway.payload) + } +} + +func TestServiceListsAllOrdersOnlyThroughModeratorBoundary(t *testing.T) { + moderator := users.InternalUser{ID: uuid.MustParse("aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa")} + store := &recordingStore{order: Order{ID: "bbbbbbbb-bbbb-4bbb-8bbb-bbbbbbbbbbbb", State: StateDisputed}} + service := NewService(staticIdentity{}, staticModerator{user: moderator}, store, nil, 1000) + orders, err := service.ListAdminOrders(context.Background(), users.VerifiedIdentity{Subject: "moderator"}) + if err != nil || len(orders) != 1 || store.adminActor != moderator.ID { + t.Fatalf("orders=%#v actor=%s err=%v", orders, store.adminActor, err) + } +} + +func TestServiceUsesOneOrderOwnedRefundKeyAndSkipsPendingReplay(t *testing.T) { + moderator := users.InternalUser{ID: uuid.MustParse("aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa")} + orderID := "bbbbbbbb-bbbb-4bbb-8bbb-bbbbbbbbbbbb" + store := &recordingStore{order: Order{ID: orderID, State: StatePaid, PaymentIntentID: "pi_refund"}} + gateway := &recordingGateway{} + service := NewService(staticIdentity{}, staticModerator{user: moderator}, store, gateway, 1000) + if _, err := service.Refund(context.Background(), users.VerifiedIdentity{Subject: "moderator"}, uuid.MustParse(orderID), "browser-key-one"); err != nil { + t.Fatalf("refund: %v", err) + } + if gateway.refundKey != "refund-"+orderID || gateway.refundCalls != 1 { + t.Fatalf("refund key=%q calls=%d", gateway.refundKey, gateway.refundCalls) + } + if _, err := service.Refund(context.Background(), users.VerifiedIdentity{Subject: "moderator"}, uuid.MustParse(orderID), "browser-key-two"); err != nil { + t.Fatalf("refund replay: %v", err) + } + if gateway.refundCalls != 1 { + t.Fatalf("pending refund called provider again: %d", gateway.refundCalls) + } +} + +type staticIdentity struct{ user users.InternalUser } + +func (s staticIdentity) Reconcile(context.Context, users.VerifiedIdentity) (users.InternalUser, bool, error) { + return s.user, false, nil +} + +type staticModerator struct{ user users.InternalUser } + +func (s staticModerator) RequireModerator(context.Context, users.VerifiedIdentity) (users.InternalUser, error) { + return s.user, nil +} + +type recordingStore struct { + order Order + account ProviderAccount + attachedSession CheckoutSession + event ProviderEvent + adminActor uuid.UUID +} + +func (s *recordingStore) PrepareCheckout(context.Context, uuid.UUID, uuid.UUID, string, int) (Order, ProviderAccount, error) { + return s.order, s.account, nil +} +func (s *recordingStore) AttachCheckout(_ context.Context, _ uuid.UUID, _ uuid.UUID, session CheckoutSession) (Order, error) { + s.attachedSession = session + s.order.CheckoutSessionID = session.ID + s.order.State = StateCheckoutCreated + return s.order, nil +} +func (s *recordingStore) ListOrders(context.Context, uuid.UUID) ([]Order, error) { + return []Order{s.order}, nil +} +func (s *recordingStore) ListAdminOrders(_ context.Context, actor uuid.UUID) ([]Order, error) { + s.adminActor = actor + return []Order{s.order}, nil +} +func (s *recordingStore) GetProviderAccount(context.Context, uuid.UUID) (ProviderAccount, error) { + return s.account, nil +} +func (s *recordingStore) SaveProviderAccount(_ context.Context, _ uuid.UUID, account ConnectedAccount) (ProviderAccount, error) { + s.account = ProviderAccount{StripeAccountID: account.ID, DetailsSubmitted: account.DetailsSubmitted, ChargesEnabled: account.ChargesEnabled, PayoutsEnabled: account.PayoutsEnabled} + return s.account, nil +} +func (s *recordingStore) ApplyProviderEvent(_ context.Context, event ProviderEvent) error { + s.event = event + return nil +} +func (s *recordingStore) PrepareRefund(context.Context, uuid.UUID, uuid.UUID) (Order, error) { + return s.order, nil +} +func (s *recordingStore) AttachRefund(_ context.Context, _ uuid.UUID, _ uuid.UUID, refund RefundResult) (Order, error) { + s.order.RefundID = refund.ID + s.order.State = StateRefundPending + return s.order, nil +} + +type recordingGateway struct { + checkout CheckoutSession + checkoutInput CheckoutRequest + event ProviderEvent + payload []byte + refundKey string + refundCalls int +} + +func (g *recordingGateway) CreateCheckout(_ context.Context, input CheckoutRequest) (CheckoutSession, error) { + g.checkoutInput = input + return g.checkout, nil +} +func (g *recordingGateway) CreateConnectedAccount(context.Context, string) (ConnectedAccount, error) { + return ConnectedAccount{ID: "acct_provider"}, nil +} +func (g *recordingGateway) CreateAccountLink(context.Context, string, string) (AccountLink, error) { + return AccountLink{URL: "https://connect.stripe.test/onboarding"}, nil +} +func (g *recordingGateway) GetConnectedAccount(context.Context, string) (ConnectedAccount, error) { + return ConnectedAccount{ID: "acct_provider"}, nil +} +func (g *recordingGateway) CreateRefund(_ context.Context, _ string, key string) (RefundResult, error) { + g.refundKey = key + g.refundCalls++ + return RefundResult{ID: "re_test"}, nil +} +func (g *recordingGateway) VerifyWebhook(payload []byte, _ string) (ProviderEvent, error) { + g.payload = append([]byte(nil), payload...) + return g.event, nil +} diff --git a/backend/internal/payments/sql_store.go b/backend/internal/payments/sql_store.go new file mode 100644 index 0000000..a5fde40 --- /dev/null +++ b/backend/internal/payments/sql_store.go @@ -0,0 +1,401 @@ +package payments + +import ( + "context" + "database/sql" + "errors" + "fmt" + "time" + + "github.com/google/uuid" +) + +type sqlStore struct{ database *sql.DB } + +func NewSQLStore(database *sql.DB) Store { return sqlStore{database: database} } + +func (s sqlStore) PrepareCheckout(ctx context.Context, actor, bookingID uuid.UUID, key string, feeBPS int) (Order, ProviderAccount, error) { + if s.database == nil { + return Order{}, ProviderAccount{}, ErrUnavailable + } + tx, err := s.database.BeginTx(ctx, nil) + if err != nil { + return Order{}, ProviderAccount{}, err + } + defer func() { _ = tx.Rollback() }() + var customer, provider uuid.UUID + var bookingState string + var gross int64 + var currency string + var account ProviderAccount + err = tx.QueryRowContext(ctx, `select b.customer_internal_user_id,b.provider_internal_user_id,b.state,b.agreed_price_minor,b.currency,coalesce(a.stripe_account_id,''),coalesce(a.details_submitted,false),coalesce(a.charges_enabled,false),coalesce(a.payouts_enabled,false),coalesce(a.updated_at,timezone('utc',now())) from public.bookings b left join public.provider_payment_accounts a on a.internal_user_id=b.provider_internal_user_id where b.id=$1 for update of b`, bookingID).Scan(&customer, &provider, &bookingState, &gross, ¤cy, &account.StripeAccountID, &account.DetailsSubmitted, &account.ChargesEnabled, &account.PayoutsEnabled, &account.UpdatedAt) + if errors.Is(err, sql.ErrNoRows) { + return Order{}, ProviderAccount{}, ErrNotFound + } + if err != nil { + return Order{}, ProviderAccount{}, err + } + if customer != actor { + return Order{}, ProviderAccount{}, ErrForbidden + } + if bookingState != "confirmed" && bookingState != "scheduled" { + return Order{}, ProviderAccount{}, ErrConflict + } + account.InternalUserID = provider.String() + if account.StripeAccountID == "" { + return Order{}, ProviderAccount{}, ErrForbidden + } + if existing, found, loadErr := loadOrderByBooking(ctx, tx, bookingID); loadErr != nil { + return Order{}, ProviderAccount{}, loadErr + } else if found { + var existingKey string + if err := tx.QueryRowContext(ctx, `select idempotency_key from public.payment_orders where id=$1`, existing.ID).Scan(&existingKey); err != nil { + return Order{}, ProviderAccount{}, err + } + if existingKey != key { + return Order{}, ProviderAccount{}, ErrConflict + } + if err := tx.Commit(); err != nil { + return Order{}, ProviderAccount{}, err + } + return existing, account, nil + } + fee := gross * int64(feeBPS) / 10_000 + if fee < 0 || fee >= gross || currency != "EUR" { + return Order{}, ProviderAccount{}, ErrInvalid + } + id := uuid.New() + var order Order + err = tx.QueryRowContext(ctx, `insert into public.payment_orders(id,booking_id,customer_internal_user_id,provider_internal_user_id,idempotency_key,gross_minor,platform_fee_minor,provider_net_minor,currency) values($1,$2,$3,$4,$5,$6,$7,$8,$9) returning id,booking_id,customer_internal_user_id,provider_internal_user_id,state,gross_minor,platform_fee_minor,provider_net_minor,currency,coalesce(stripe_checkout_session_id,''),coalesce(stripe_payment_intent_id,''),coalesce(stripe_invoice_id,''),coalesce(stripe_refund_id,''),created_at,updated_at`, id, bookingID, customer, provider, key, gross, fee, gross-fee, currency).Scan(orderScan(&order)...) + if err != nil { + return Order{}, ProviderAccount{}, err + } + if err := tx.Commit(); err != nil { + return Order{}, ProviderAccount{}, err + } + return order, account, nil +} + +func (s sqlStore) AttachCheckout(ctx context.Context, actor, orderID uuid.UUID, session CheckoutSession) (Order, error) { + tx, err := s.database.BeginTx(ctx, nil) + if err != nil { + return Order{}, err + } + defer func() { _ = tx.Rollback() }() + order, found, err := loadOrderByID(ctx, tx, orderID, true) + if err != nil || !found { + if !found && err == nil { + err = ErrNotFound + } + return Order{}, err + } + if order.CustomerID != actor.String() { + return Order{}, ErrForbidden + } + if order.State == StateCheckoutCreated && order.CheckoutSessionID == session.ID { + if err := tx.Commit(); err != nil { + return Order{}, err + } + return order, nil + } + if order.State != StatePendingCheckout { + return Order{}, ErrConflict + } + result, err := tx.ExecContext(ctx, `update public.payment_orders set state='checkout_created',stripe_checkout_session_id=$1,updated_at=timezone('utc',now()) where id=$2 and state='pending_checkout'`, session.ID, orderID) + if err != nil { + return Order{}, err + } + if rows, _ := result.RowsAffected(); rows != 1 { + return Order{}, ErrConflict + } + if _, err := tx.ExecContext(ctx, `insert into public.payment_events(payment_order_id,event_type,from_state,to_state,provider_object_id) values($1,'checkout_created','pending_checkout','checkout_created',$2)`, orderID, session.ID); err != nil { + return Order{}, err + } + order, _, err = loadOrderByID(ctx, tx, orderID, false) + if err != nil { + return Order{}, err + } + if err := tx.Commit(); err != nil { + return Order{}, err + } + return order, nil +} + +func (s sqlStore) ListOrders(ctx context.Context, actor uuid.UUID) ([]Order, error) { + rows, err := s.database.QueryContext(ctx, `select id,booking_id,customer_internal_user_id,provider_internal_user_id,state,gross_minor,platform_fee_minor,provider_net_minor,currency,coalesce(stripe_checkout_session_id,''),coalesce(stripe_payment_intent_id,''),coalesce(stripe_invoice_id,''),coalesce(stripe_refund_id,''),created_at,updated_at from public.payment_orders where customer_internal_user_id=$1 or provider_internal_user_id=$1 order by updated_at desc,id`, actor) + if err != nil { + return nil, err + } + defer rows.Close() + orders := []Order{} + for rows.Next() { + var order Order + if err := rows.Scan(orderScan(&order)...); err != nil { + return nil, err + } + orders = append(orders, order) + } + return orders, rows.Err() +} + +func (s sqlStore) ListAdminOrders(ctx context.Context, actor uuid.UUID) ([]Order, error) { + var authorized bool + if err := s.database.QueryRowContext(ctx, `select exists(select 1 from public.platform_roles where internal_user_id=$1 and role in('moderator','administrator'))`, actor).Scan(&authorized); err != nil { + return nil, err + } + if !authorized { + return nil, ErrForbidden + } + rows, err := s.database.QueryContext(ctx, orderSelect+` order by updated_at desc,id limit 100`) + if err != nil { + return nil, err + } + defer rows.Close() + orders := []Order{} + for rows.Next() { + var order Order + if err := rows.Scan(orderScan(&order)...); err != nil { + return nil, err + } + orders = append(orders, order) + } + return orders, rows.Err() +} + +func (s sqlStore) GetProviderAccount(ctx context.Context, actor uuid.UUID) (ProviderAccount, error) { + var account ProviderAccount + err := s.database.QueryRowContext(ctx, `select internal_user_id,stripe_account_id,details_submitted,charges_enabled,payouts_enabled,updated_at from public.provider_payment_accounts where internal_user_id=$1`, actor).Scan(&account.InternalUserID, &account.StripeAccountID, &account.DetailsSubmitted, &account.ChargesEnabled, &account.PayoutsEnabled, &account.UpdatedAt) + if errors.Is(err, sql.ErrNoRows) { + return ProviderAccount{}, ErrNotFound + } + return account, err +} + +func (s sqlStore) SaveProviderAccount(ctx context.Context, actor uuid.UUID, value ConnectedAccount) (ProviderAccount, error) { + var providerExists bool + if err := s.database.QueryRowContext(ctx, `select exists(select 1 from public.provider_profiles where internal_user_id=$1)`, actor).Scan(&providerExists); err != nil { + return ProviderAccount{}, err + } + if !providerExists { + return ProviderAccount{}, ErrForbidden + } + var account ProviderAccount + err := s.database.QueryRowContext(ctx, `insert into public.provider_payment_accounts(internal_user_id,stripe_account_id,details_submitted,charges_enabled,payouts_enabled) values($1,$2,$3,$4,$5) on conflict(internal_user_id) do update set details_submitted=excluded.details_submitted,charges_enabled=excluded.charges_enabled,payouts_enabled=excluded.payouts_enabled,updated_at=timezone('utc',now()) where provider_payment_accounts.stripe_account_id=excluded.stripe_account_id returning internal_user_id,stripe_account_id,details_submitted,charges_enabled,payouts_enabled,updated_at`, actor, value.ID, value.DetailsSubmitted, value.ChargesEnabled, value.PayoutsEnabled).Scan(&account.InternalUserID, &account.StripeAccountID, &account.DetailsSubmitted, &account.ChargesEnabled, &account.PayoutsEnabled, &account.UpdatedAt) + if errors.Is(err, sql.ErrNoRows) { + return ProviderAccount{}, ErrConflict + } + return account, err +} + +func (s sqlStore) ApplyProviderEvent(ctx context.Context, event ProviderEvent) error { + tx, err := s.database.BeginTx(ctx, nil) + if err != nil { + return err + } + defer func() { _ = tx.Rollback() }() + result, err := tx.ExecContext(ctx, `insert into public.stripe_webhook_receipts(stripe_event_id,event_type,provider_object_id,outcome) values($1,$2,$3,'processing') on conflict do nothing`, event.ID, event.Kind, event.ProviderObjectID) + if err != nil { + return err + } + if rows, _ := result.RowsAffected(); rows == 0 { + return tx.Commit() + } + if event.Kind == EventAccountUpdate { + result, err = tx.ExecContext(ctx, `update public.provider_payment_accounts set details_submitted=$1,charges_enabled=$2,payouts_enabled=$3,updated_at=timezone('utc',now()) where stripe_account_id=$4`, event.DetailsSubmitted, event.ChargesEnabled, event.PayoutsEnabled, event.AccountID) + if err != nil { + return err + } + if rows, _ := result.RowsAffected(); rows != 1 { + return ErrNotFound + } + _, err = tx.ExecContext(ctx, `update public.stripe_webhook_receipts set outcome='account_updated',processed_at=timezone('utc',now()) where stripe_event_id=$1`, event.ID) + if err != nil { + return err + } + return tx.Commit() + } + order, err := loadOrderForEvent(ctx, tx, event) + if err != nil { + return err + } + from := order.State + to, eventType, err := eventTransition(from, event.Kind) + if err != nil { + return err + } + _, err = tx.ExecContext(ctx, `update public.payment_orders set state=$1,stripe_checkout_session_id=coalesce(nullif($2,''),stripe_checkout_session_id),stripe_payment_intent_id=coalesce(nullif($3,''),stripe_payment_intent_id),stripe_invoice_id=coalesce(nullif($4,''),stripe_invoice_id),paid_at=case when $1='paid' then coalesce(paid_at,timezone('utc',now())) else paid_at end,refunded_at=case when $1='refunded' then coalesce(refunded_at,timezone('utc',now())) else refunded_at end,updated_at=timezone('utc',now()) where id=$5`, to, checkoutID(event), event.PaymentIntentID, event.InvoiceID, order.ID) + if err != nil { + return err + } + if _, err := tx.ExecContext(ctx, `insert into public.payment_events(payment_order_id,event_type,from_state,to_state,provider_object_id) values($1,$2,$3,$4,$5)`, order.ID, eventType, from, to, event.ProviderObjectID); err != nil { + return err + } + if event.Kind == EventDisputeOpened || event.Kind == EventDisputeWon || event.Kind == EventDisputeLost { + closed := any(nil) + if event.Kind == EventDisputeWon || event.Kind == EventDisputeLost { + closed = time.Now().UTC() + } + _, err = tx.ExecContext(ctx, `insert into public.payment_disputes(stripe_dispute_id,payment_order_id,stripe_charge_id,amount_minor,currency,state,reason,opened_at,closed_at) values($1,$2,$3,$4,$5,$6,$7,$8,$9) on conflict(stripe_dispute_id) do update set state=excluded.state,reason=excluded.reason,closed_at=excluded.closed_at,updated_at=timezone('utc',now())`, event.DisputeID, order.ID, event.ChargeID, event.AmountMinor, event.Currency, event.DisputeState, event.DisputeReason, event.OccurredAt, closed) + if err != nil { + return err + } + } + if _, err := tx.ExecContext(ctx, `update public.stripe_webhook_receipts set outcome=$1,processed_at=timezone('utc',now()) where stripe_event_id=$2`, eventType, event.ID); err != nil { + return err + } + return tx.Commit() +} + +func (s sqlStore) PrepareRefund(ctx context.Context, actor, orderID uuid.UUID) (Order, error) { + tx, err := s.database.BeginTx(ctx, nil) + if err != nil { + return Order{}, err + } + defer func() { _ = tx.Rollback() }() + var moderator bool + if err := tx.QueryRowContext(ctx, `select exists(select 1 from public.platform_roles where internal_user_id=$1 and role in('moderator','administrator'))`, actor).Scan(&moderator); err != nil { + return Order{}, err + } + if !moderator { + return Order{}, ErrForbidden + } + order, found, err := loadOrderByID(ctx, tx, orderID, true) + if err != nil || !found { + if !found && err == nil { + err = ErrNotFound + } + return Order{}, err + } + if order.State == StateRefundPending { + if err := tx.Commit(); err != nil { + return Order{}, err + } + return order, nil + } + if order.State != StatePaid && order.State != StateDisputeWon || order.PaymentIntentID == "" { + return Order{}, ErrConflict + } + return order, tx.Commit() +} + +func (s sqlStore) AttachRefund(ctx context.Context, actor, orderID uuid.UUID, refund RefundResult) (Order, error) { + tx, err := s.database.BeginTx(ctx, nil) + if err != nil { + return Order{}, err + } + defer func() { _ = tx.Rollback() }() + var from State + if err := tx.QueryRowContext(ctx, `select state from public.payment_orders where id=$1 for update`, orderID).Scan(&from); errors.Is(err, sql.ErrNoRows) { + return Order{}, ErrNotFound + } else if err != nil { + return Order{}, err + } + result, err := tx.ExecContext(ctx, `update public.payment_orders set state='refund_pending',stripe_refund_id=$1,updated_at=timezone('utc',now()) where id=$2 and state=$3`, refund.ID, orderID, from) + if err != nil { + return Order{}, err + } + if rows, _ := result.RowsAffected(); rows != 1 { + return Order{}, ErrConflict + } + if _, err := tx.ExecContext(ctx, `insert into public.payment_events(payment_order_id,event_type,from_state,to_state,provider_object_id) values($1,'refund_requested',$2,'refund_pending',$3)`, orderID, from, refund.ID); err != nil { + return Order{}, err + } + order, _, err := loadOrderByID(ctx, tx, orderID, false) + if err != nil { + return Order{}, err + } + if err := tx.Commit(); err != nil { + return Order{}, err + } + return order, nil +} + +type queryer interface { + QueryRowContext(context.Context, string, ...any) *sql.Row +} + +func loadOrderByBooking(ctx context.Context, q queryer, bookingID uuid.UUID) (Order, bool, error) { + var order Order + err := q.QueryRowContext(ctx, orderSelect+` where booking_id=$1`, bookingID).Scan(orderScan(&order)...) + if errors.Is(err, sql.ErrNoRows) { + return Order{}, false, nil + } + return order, err == nil, err +} +func loadOrderByID(ctx context.Context, q queryer, orderID uuid.UUID, lock bool) (Order, bool, error) { + suffix := ` where id=$1` + if lock { + suffix += ` for update` + } + var order Order + err := q.QueryRowContext(ctx, orderSelect+suffix, orderID).Scan(orderScan(&order)...) + if errors.Is(err, sql.ErrNoRows) { + return Order{}, false, nil + } + return order, err == nil, err +} + +const orderSelect = `select id,booking_id,customer_internal_user_id,provider_internal_user_id,state,gross_minor,platform_fee_minor,provider_net_minor,currency,coalesce(stripe_checkout_session_id,''),coalesce(stripe_payment_intent_id,''),coalesce(stripe_invoice_id,''),coalesce(stripe_refund_id,''),created_at,updated_at from public.payment_orders` + +func orderScan(order *Order) []any { + return []any{&order.ID, &order.BookingID, &order.CustomerID, &order.ProviderID, &order.State, &order.GrossMinor, &order.PlatformFeeMinor, &order.ProviderNetMinor, &order.Currency, &order.CheckoutSessionID, &order.PaymentIntentID, &order.InvoiceID, &order.RefundID, &order.CreatedAt, &order.UpdatedAt} +} +func loadOrderForEvent(ctx context.Context, tx *sql.Tx, event ProviderEvent) (Order, error) { + var order Order + var row *sql.Row + if event.OrderID != "" { + row = tx.QueryRowContext(ctx, orderSelect+` where id=$1 for update`, event.OrderID) + } else if event.PaymentIntentID != "" { + row = tx.QueryRowContext(ctx, orderSelect+` where stripe_payment_intent_id=$1 for update`, event.PaymentIntentID) + } else { + return Order{}, ErrInvalid + } + if err := row.Scan(orderScan(&order)...); errors.Is(err, sql.ErrNoRows) { + return Order{}, ErrNotFound + } else if err != nil { + return Order{}, err + } + return order, nil +} +func eventTransition(from State, kind EventKind) (State, string, error) { + switch kind { + case EventProcessing: + if from == StateCheckoutCreated { + return StateProcessing, "processing", nil + } + case EventPaid: + if from == StateCheckoutCreated || from == StateProcessing { + return StatePaid, "paid", nil + } + if from == StatePaid { + return from, "paid", nil + } + case EventFailed: + if from == StateCheckoutCreated || from == StateProcessing { + return StateFailed, "failed", nil + } + case EventRefunded: + if from == StateRefundPending || from == StatePaid || from == StateDisputed || from == StateDisputeLost { + return StateRefunded, "refunded", nil + } + case EventDisputeOpened: + if from == StatePaid { + return StateDisputed, "dispute_opened", nil + } + case EventDisputeWon: + if from == StateDisputed { + return StateDisputeWon, "dispute_won", nil + } + case EventDisputeLost: + if from == StateDisputed { + return StateDisputeLost, "dispute_lost", nil + } + } + return "", "", fmt.Errorf("%w: transition %s from %s", ErrConflict, kind, from) +} +func checkoutID(event ProviderEvent) string { + if len(event.ProviderObjectID) > 3 && event.ProviderObjectID[:3] == "cs_" { + return event.ProviderObjectID + } + return "" +} diff --git a/backend/internal/payments/sql_store_integration_test.go b/backend/internal/payments/sql_store_integration_test.go new file mode 100644 index 0000000..5c1d6b0 --- /dev/null +++ b/backend/internal/payments/sql_store_integration_test.go @@ -0,0 +1,98 @@ +package payments + +import ( + "context" + "database/sql" + "os" + "testing" + "time" + + "github.com/google/uuid" + _ "github.com/jackc/pgx/v5/stdlib" +) + +func TestSQLStorePersistsCheckoutAndIdempotentPaidWebhook(t *testing.T) { + url := os.Getenv("TEST_DATABASE_URL") + if url == "" { + t.Skip("TEST_DATABASE_URL is required") + } + database, err := sql.Open("pgx", url) + if err != nil { + t.Fatal(err) + } + defer database.Close() + ctx := context.Background() + customer, provider, booking := uuid.New(), uuid.New(), uuid.New() + cleanup := func() { + _, _ = database.ExecContext(ctx, `delete from public.payment_events where payment_order_id in(select id from public.payment_orders where booking_id=$1)`, booking) + _, _ = database.ExecContext(ctx, `delete from public.stripe_webhook_receipts where provider_object_id='cs_testpayment'`) + _, _ = database.ExecContext(ctx, `delete from public.payment_orders where booking_id=$1`, booking) + _, _ = database.ExecContext(ctx, `delete from public.bookings where id=$1`, booking) + _, _ = database.ExecContext(ctx, `delete from public.provider_payment_accounts where internal_user_id=$1`, provider) + _, _ = database.ExecContext(ctx, `delete from public.provider_profiles where internal_user_id=$1`, provider) + _, _ = database.ExecContext(ctx, `delete from public.platform_roles where internal_user_id=$1`, provider) + _, _ = database.ExecContext(ctx, `delete from public.user_accounts where internal_user_id in($1,$2)`, customer, provider) + _, _ = database.ExecContext(ctx, `delete from public.internal_users where id in($1,$2)`, customer, provider) + } + cleanup() + defer cleanup() + for id, subject := range map[uuid.UUID]string{customer: "payment_customer_" + customer.String(), provider: "payment_provider_" + provider.String()} { + if _, err := database.ExecContext(ctx, `insert into public.internal_users(id,clerk_subject) values($1,$2)`, id, subject); err != nil { + t.Fatal(err) + } + if _, err := database.ExecContext(ctx, `insert into public.user_accounts(internal_user_id,provider_enabled) values($1,$2)`, id, id == provider); err != nil { + t.Fatal(err) + } + } + var locality uuid.UUID + if err := database.QueryRowContext(ctx, `select id from public.localities order by id limit 1`).Scan(&locality); err != nil { + t.Fatal(err) + } + if _, err := database.ExecContext(ctx, `insert into public.provider_profiles(internal_user_id,display_name,provider_type,bio,primary_locality_id,max_travel_distance_km,remote_services) values($1,'Payment test provider','professional','Synthetic transactional payment test provider.',$2,0,true)`, provider, locality); err != nil { + t.Fatal(err) + } + if _, err := database.ExecContext(ctx, `insert into public.bookings(id,customer_internal_user_id,provider_internal_user_id,source_type,idempotency_key,state,scheduled_at,private_location,agreed_price_minor) values($1,$2,$3,'direct','payment-booking-test','confirmed',$4,'Synthetic private test location',12500)`, booking, customer, provider, time.Now().UTC().Add(24*time.Hour)); err != nil { + t.Fatal(err) + } + if _, err := database.ExecContext(ctx, `insert into public.provider_payment_accounts(internal_user_id,stripe_account_id,details_submitted,charges_enabled,payouts_enabled) values($1,'acct_syntheticpayment',true,true,true)`, provider); err != nil { + t.Fatal(err) + } + if _, err := database.ExecContext(ctx, `insert into public.platform_roles(id,internal_user_id,role) values($1,$2,'moderator')`, uuid.New(), provider); err != nil { + t.Fatal(err) + } + store := NewSQLStore(database) + order, account, err := store.PrepareCheckout(ctx, customer, booking, "payment-order-test", 1000) + if err != nil || order.GrossMinor != 12500 || order.PlatformFeeMinor != 1250 || order.ProviderNetMinor != 11250 || !account.PayoutsEnabled { + t.Fatalf("prepare order=%#v account=%#v err=%v", order, account, err) + } + orderID := uuid.MustParse(order.ID) + order, err = store.AttachCheckout(ctx, customer, orderID, CheckoutSession{ID: "cs_testpayment", URL: "https://checkout.stripe.test/session"}) + if err != nil || order.State != StateCheckoutCreated { + t.Fatalf("attach order=%#v err=%v", order, err) + } + event := ProviderEvent{ID: "evt_123synthetic", Kind: EventPaid, ProviderObjectID: "cs_testpayment", OrderID: order.ID, PaymentIntentID: "pi_syntheticpayment", InvoiceID: "in_syntheticpayment", OccurredAt: time.Now().UTC()} + if err := store.ApplyProviderEvent(ctx, event); err != nil { + t.Fatalf("apply event: %v", err) + } + if err := store.ApplyProviderEvent(ctx, event); err != nil { + t.Fatalf("replay event: %v", err) + } + var state string + var paidEvents, receipts int + if err := database.QueryRowContext(ctx, `select state from public.payment_orders where id=$1`, orderID).Scan(&state); err != nil { + t.Fatal(err) + } + if err := database.QueryRowContext(ctx, `select count(*) from public.payment_events where payment_order_id=$1 and event_type='paid'`, orderID).Scan(&paidEvents); err != nil { + t.Fatal(err) + } + if err := database.QueryRowContext(ctx, `select count(*) from public.stripe_webhook_receipts where stripe_event_id=$1`, event.ID).Scan(&receipts); err != nil { + t.Fatal(err) + } + if state != "paid" || paidEvents != 1 || receipts != 1 { + t.Fatalf("state=%s paid_events=%d receipts=%d", state, paidEvents, receipts) + } + adminOrders, err := store.ListAdminOrders(ctx, provider) + if err != nil || len(adminOrders) != 1 || adminOrders[0].ID != order.ID { + t.Fatalf("admin orders=%#v err=%v", adminOrders, err) + } +} diff --git a/backend/internal/payments/stripe_gateway.go b/backend/internal/payments/stripe_gateway.go new file mode 100644 index 0000000..60e03e1 --- /dev/null +++ b/backend/internal/payments/stripe_gateway.go @@ -0,0 +1,317 @@ +package payments + +import ( + "context" + "crypto/hmac" + "crypto/sha256" + "encoding/hex" + "encoding/json" + + "fmt" + "io" + "net/http" + "net/url" + "strconv" + "strings" + "time" +) + +const ( + stripeBodyLimit = 256 * 1024 + stripeTolerance = 5 * time.Minute +) + +type StripeConfig struct { + SecretKey string + WebhookSecret string + APIBase string + PublicOrigin string + HTTPClient *http.Client + Now func() time.Time +} + +type StripeGateway struct { + secretKey string + webhookSecret string + apiBase string + publicOrigin string + client *http.Client + now func() time.Time +} + +func NewStripeGateway(config StripeConfig) (*StripeGateway, error) { + config.SecretKey = strings.TrimSpace(config.SecretKey) + config.WebhookSecret = strings.TrimSpace(config.WebhookSecret) + config.APIBase = strings.TrimRight(strings.TrimSpace(config.APIBase), "/") + config.PublicOrigin = strings.TrimRight(strings.TrimSpace(config.PublicOrigin), "/") + api, apiErr := url.Parse(config.APIBase) + public, publicErr := url.Parse(config.PublicOrigin) + if !strings.HasPrefix(config.SecretKey, "sk_") || !strings.HasPrefix(config.WebhookSecret, "whsec_") || apiErr != nil || api.Host == "" || (api.Scheme != "https" && api.Hostname() != "127.0.0.1" && api.Hostname() != "localhost") || publicErr != nil || public.Scheme != "https" || public.Host == "" || public.Path != "" { + return nil, ErrUnavailable + } + if config.HTTPClient == nil { + config.HTTPClient = &http.Client{Timeout: 15 * time.Second} + } + if config.Now == nil { + config.Now = time.Now + } + return &StripeGateway{secretKey: config.SecretKey, webhookSecret: config.WebhookSecret, apiBase: config.APIBase, publicOrigin: config.PublicOrigin, client: config.HTTPClient, now: config.Now}, nil +} + +func (g *StripeGateway) CreateCheckout(ctx context.Context, input CheckoutRequest) (CheckoutSession, error) { + if input.OrderID == "" || input.BookingID == "" || !strings.HasPrefix(input.ConnectedAccountID, "acct_") || input.GrossMinor < 50 || input.FeeMinor < 0 || input.FeeMinor >= input.GrossMinor || len(input.IdempotencyKey) < 8 { + return CheckoutSession{}, ErrInvalid + } + locale := input.Locale + if locale != "pt-PT" && locale != "en" && locale != "es" { + locale = "pt-PT" + } + values := url.Values{ + "mode": {"payment"}, + "success_url": {g.publicOrigin + "/" + locale + "/account/bookings?payment=returned"}, + "cancel_url": {g.publicOrigin + "/" + locale + "/account/bookings?payment=cancelled"}, + "line_items[0][price_data][currency]": {"eur"}, + "line_items[0][price_data][unit_amount]": {strconv.FormatInt(input.GrossMinor, 10)}, + "line_items[0][price_data][product_data][name]": {"Vila service booking"}, + "line_items[0][quantity]": {"1"}, + "payment_intent_data[application_fee_amount]": {strconv.FormatInt(input.FeeMinor, 10)}, + "payment_intent_data[transfer_data][destination]": {input.ConnectedAccountID}, + "metadata[order_id]": {input.OrderID}, + "metadata[booking_id]": {input.BookingID}, + "automatic_tax[enabled]": {"true"}, + "tax_id_collection[enabled]": {"true"}, + "invoice_creation[enabled]": {"true"}, + "billing_address_collection": {"required"}, + "customer_creation": {"always"}, + "payment_method_types[0]": {"card"}, + "payment_method_types[1]": {"mb_way"}, + "consent_collection[terms_of_service]": {"required"}, + "custom_text[terms_of_service_acceptance][message]": {"I agree to Vila's terms, payment policy, and refund policy."}, + } + var response struct { + ID string `json:"id"` + URL string `json:"url"` + } + if err := g.form(ctx, http.MethodPost, "/v1/checkout/sessions", values, input.IdempotencyKey, &response); err != nil || !strings.HasPrefix(response.ID, "cs_") || !validHTTPS(response.URL) { + return CheckoutSession{}, ErrUnavailable + } + return CheckoutSession{ID: response.ID, URL: response.URL}, nil +} + +func (g *StripeGateway) CreateConnectedAccount(ctx context.Context, idempotencyKey string) (ConnectedAccount, error) { + values := url.Values{"type": {"express"}, "country": {"PT"}, "capabilities[card_payments][requested]": {"true"}, "capabilities[transfers][requested]": {"true"}, "business_type": {"individual"}} + var response stripeAccount + if err := g.form(ctx, http.MethodPost, "/v1/accounts", values, idempotencyKey, &response); err != nil || !strings.HasPrefix(response.ID, "acct_") { + return ConnectedAccount{}, ErrUnavailable + } + return response.account(), nil +} + +func (g *StripeGateway) CreateAccountLink(ctx context.Context, accountID, locale string) (AccountLink, error) { + if !strings.HasPrefix(accountID, "acct_") { + return AccountLink{}, ErrInvalid + } + values := url.Values{"account": {accountID}, "type": {"account_onboarding"}, "refresh_url": {g.publicOrigin + "/" + locale + "/account/payouts?onboarding=refresh"}, "return_url": {g.publicOrigin + "/" + locale + "/account/payouts?onboarding=returned"}} + var response struct { + URL string `json:"url"` + } + if err := g.form(ctx, http.MethodPost, "/v1/account_links", values, "", &response); err != nil || !validHTTPS(response.URL) { + return AccountLink{}, ErrUnavailable + } + return AccountLink{URL: response.URL}, nil +} + +func (g *StripeGateway) GetConnectedAccount(ctx context.Context, accountID string) (ConnectedAccount, error) { + if !strings.HasPrefix(accountID, "acct_") { + return ConnectedAccount{}, ErrInvalid + } + var response stripeAccount + if err := g.form(ctx, http.MethodGet, "/v1/accounts/"+url.PathEscape(accountID), nil, "", &response); err != nil { + return ConnectedAccount{}, ErrUnavailable + } + return response.account(), nil +} + +func (g *StripeGateway) CreateRefund(ctx context.Context, paymentIntentID, idempotencyKey string) (RefundResult, error) { + if !strings.HasPrefix(paymentIntentID, "pi_") || len(idempotencyKey) < 8 { + return RefundResult{}, ErrInvalid + } + var response struct { + ID string `json:"id"` + } + if err := g.form(ctx, http.MethodPost, "/v1/refunds", url.Values{"payment_intent": {paymentIntentID}, "reverse_transfer": {"true"}, "refund_application_fee": {"true"}}, idempotencyKey, &response); err != nil || !strings.HasPrefix(response.ID, "re_") { + return RefundResult{}, ErrUnavailable + } + return RefundResult{ID: response.ID}, nil +} + +func (g *StripeGateway) VerifyWebhook(payload []byte, signature string) (ProviderEvent, error) { + if len(payload) == 0 || len(payload) > stripeBodyLimit { + return ProviderEvent{}, ErrInvalid + } + timestamp, signatures, ok := parseStripeSignature(signature) + if !ok || g.now().Sub(time.Unix(timestamp, 0)).Abs() > stripeTolerance { + return ProviderEvent{}, ErrUnauthorized + } + mac := hmac.New(sha256.New, []byte(g.webhookSecret)) + _, _ = mac.Write([]byte(strconv.FormatInt(timestamp, 10))) + _, _ = mac.Write([]byte(".")) + _, _ = mac.Write(payload) + expected := mac.Sum(nil) + verified := false + for _, candidate := range signatures { + decoded, err := hex.DecodeString(candidate) + if err == nil && hmac.Equal(decoded, expected) { + verified = true + break + } + } + if !verified { + return ProviderEvent{}, ErrUnauthorized + } + return projectStripeEvent(payload) +} + +type stripeAccount struct { + ID string `json:"id"` + DetailsSubmitted bool `json:"details_submitted"` + ChargesEnabled bool `json:"charges_enabled"` + PayoutsEnabled bool `json:"payouts_enabled"` +} + +func (a stripeAccount) account() ConnectedAccount { + return ConnectedAccount{ID: a.ID, DetailsSubmitted: a.DetailsSubmitted, ChargesEnabled: a.ChargesEnabled, PayoutsEnabled: a.PayoutsEnabled} +} + +func (g *StripeGateway) form(ctx context.Context, method, path string, values url.Values, idempotencyKey string, target any) error { + var body io.Reader + if values != nil { + body = strings.NewReader(values.Encode()) + } + req, err := http.NewRequestWithContext(ctx, method, g.apiBase+path, body) + if err != nil { + return err + } + req.SetBasicAuth(g.secretKey, "") + if values != nil { + req.Header.Set("Content-Type", "application/x-www-form-urlencoded") + } + if idempotencyKey != "" { + req.Header.Set("Idempotency-Key", idempotencyKey) + } + response, err := g.client.Do(req) + if err != nil { + return err + } + defer response.Body.Close() + if response.StatusCode < 200 || response.StatusCode >= 300 { + _, _ = io.Copy(io.Discard, io.LimitReader(response.Body, stripeBodyLimit)) + return fmt.Errorf("stripe status %d", response.StatusCode) + } + decoder := json.NewDecoder(io.LimitReader(response.Body, stripeBodyLimit)) + return decoder.Decode(target) +} + +func parseStripeSignature(value string) (int64, []string, bool) { + var timestamp int64 + var signatures []string + for _, part := range strings.Split(value, ",") { + key, raw, ok := strings.Cut(strings.TrimSpace(part), "=") + if !ok { + continue + } + switch key { + case "t": + timestamp, _ = strconv.ParseInt(raw, 10, 64) + case "v1": + signatures = append(signatures, raw) + } + } + return timestamp, signatures, timestamp > 0 && len(signatures) > 0 +} + +func projectStripeEvent(payload []byte) (ProviderEvent, error) { + var envelope struct { + ID string `json:"id"` + Type string `json:"type"` + Created int64 `json:"created"` + Data struct { + Object json.RawMessage `json:"object"` + } `json:"data"` + } + if err := json.Unmarshal(payload, &envelope); err != nil || !strings.HasPrefix(envelope.ID, "evt_") { + return ProviderEvent{}, ErrInvalid + } + event := ProviderEvent{ID: envelope.ID, OccurredAt: time.Unix(envelope.Created, 0).UTC()} + switch envelope.Type { + case "checkout.session.completed", "checkout.session.async_payment_succeeded", "checkout.session.async_payment_failed": + var object struct { + ID string `json:"id"` + PaymentStatus string `json:"payment_status"` + PaymentIntent string `json:"payment_intent"` + Invoice string `json:"invoice"` + Metadata map[string]string `json:"metadata"` + } + if json.Unmarshal(envelope.Data.Object, &object) != nil || !strings.HasPrefix(object.ID, "cs_") || object.Metadata["order_id"] == "" { + return ProviderEvent{}, ErrInvalid + } + event.ProviderObjectID, event.OrderID, event.PaymentIntentID, event.InvoiceID = object.ID, object.Metadata["order_id"], object.PaymentIntent, object.Invoice + if envelope.Type == "checkout.session.async_payment_failed" { + event.Kind = EventFailed + } else if object.PaymentStatus == "paid" || envelope.Type == "checkout.session.async_payment_succeeded" { + event.Kind = EventPaid + } else { + event.Kind = EventProcessing + } + case "charge.refunded": + var object struct { + ID string `json:"id"` + PaymentIntent string `json:"payment_intent"` + Currency string `json:"currency"` + AmountRefunded int64 `json:"amount_refunded"` + } + if json.Unmarshal(envelope.Data.Object, &object) != nil { + return ProviderEvent{}, ErrInvalid + } + event.Kind, event.ProviderObjectID, event.ChargeID, event.PaymentIntentID, event.AmountMinor, event.Currency = EventRefunded, object.ID, object.ID, object.PaymentIntent, object.AmountRefunded, strings.ToUpper(object.Currency) + case "charge.dispute.created", "charge.dispute.closed": + var object struct { + ID string `json:"id"` + Charge string `json:"charge"` + PaymentIntent string `json:"payment_intent"` + Status string `json:"status"` + Reason string `json:"reason"` + Currency string `json:"currency"` + Amount int64 `json:"amount"` + } + if json.Unmarshal(envelope.Data.Object, &object) != nil { + return ProviderEvent{}, ErrInvalid + } + event.ProviderObjectID, event.DisputeID, event.ChargeID, event.PaymentIntentID, event.DisputeState, event.DisputeReason, event.AmountMinor, event.Currency = object.ID, object.ID, object.Charge, object.PaymentIntent, object.Status, object.Reason, object.Amount, strings.ToUpper(object.Currency) + switch object.Status { + case "won": + event.Kind = EventDisputeWon + case "lost", "warning_closed": + event.Kind = EventDisputeLost + default: + event.Kind = EventDisputeOpened + } + case "account.updated": + var object stripeAccount + if json.Unmarshal(envelope.Data.Object, &object) != nil || !strings.HasPrefix(object.ID, "acct_") { + return ProviderEvent{}, ErrInvalid + } + event.Kind, event.ProviderObjectID, event.AccountID, event.DetailsSubmitted, event.ChargesEnabled, event.PayoutsEnabled = EventAccountUpdate, object.ID, object.ID, object.DetailsSubmitted, object.ChargesEnabled, object.PayoutsEnabled + default: + return ProviderEvent{}, ErrInvalid + } + if event.OccurredAt.IsZero() { + event.OccurredAt = time.Unix(0, 0).UTC() + } + return event, nil +} + +func validHTTPS(raw string) bool { + parsed, err := url.Parse(raw) + return err == nil && parsed.Scheme == "https" && parsed.Host != "" +} diff --git a/backend/internal/payments/stripe_gateway_test.go b/backend/internal/payments/stripe_gateway_test.go new file mode 100644 index 0000000..23b0000 --- /dev/null +++ b/backend/internal/payments/stripe_gateway_test.go @@ -0,0 +1,94 @@ +package payments + +import ( + "context" + "crypto/hmac" + "crypto/sha256" + "encoding/hex" + "io" + "net/http" + "net/http/httptest" + "net/url" + "strings" + "testing" + "time" +) + +func TestStripeGatewayCreatesServerPricedDestinationCheckout(t *testing.T) { + server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + if r.URL.Path != "/v1/checkout/sessions" || r.Method != http.MethodPost { + t.Fatalf("unexpected Stripe request %s %s", r.Method, r.URL.Path) + } + if user, _, ok := r.BasicAuth(); !ok || user != "sk_test_synthetic" { + t.Fatal("missing Stripe server authentication") + } + body, _ := io.ReadAll(r.Body) + values, _ := url.ParseQuery(string(body)) + for key, expected := range map[string]string{ + "mode": "payment", + "line_items[0][price_data][currency]": "eur", + "line_items[0][price_data][unit_amount]": "12500", + "line_items[0][quantity]": "1", + "payment_intent_data[application_fee_amount]": "1250", + "payment_intent_data[transfer_data][destination]": "acct_provider", + "metadata[order_id]": "aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa", + "automatic_tax[enabled]": "true", + "tax_id_collection[enabled]": "true", + "invoice_creation[enabled]": "true", + } { + if values.Get(key) != expected { + t.Errorf("%s = %q, want %q", key, values.Get(key), expected) + } + } + w.Header().Set("Content-Type", "application/json") + _, _ = w.Write([]byte(`{"id":"cs_test_checkout","url":"https://checkout.stripe.test/session"}`)) + })) + defer server.Close() + + gateway, err := NewStripeGateway(StripeConfig{SecretKey: "sk_test_synthetic", WebhookSecret: "whsec_synthetic", APIBase: server.URL, PublicOrigin: "https://vila.example", Now: time.Now}) + if err != nil { + t.Fatalf("gateway: %v", err) + } + session, err := gateway.CreateCheckout(context.Background(), CheckoutRequest{OrderID: "aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa", BookingID: "bbbbbbbb-bbbb-4bbb-8bbb-bbbbbbbbbbbb", ConnectedAccountID: "acct_provider", GrossMinor: 12500, FeeMinor: 1250, Locale: "pt-PT", IdempotencyKey: "checkout-key-123"}) + if err != nil { + t.Fatalf("checkout: %v", err) + } + if session.ID != "cs_test_checkout" || session.URL != "https://checkout.stripe.test/session" { + t.Fatalf("session = %#v", session) + } +} + +func TestStripeGatewayVerifiesRawWebhookAndRejectsTampering(t *testing.T) { + now := time.Unix(1_800_000_000, 0) + gateway, err := NewStripeGateway(StripeConfig{SecretKey: "sk_test_synthetic", WebhookSecret: "whsec_synthetic", APIBase: "https://api.stripe.test", PublicOrigin: "https://vila.example", Now: func() time.Time { return now }}) + if err != nil { + t.Fatal(err) + } + payload := []byte(`{"id":"evt_checkout","type":"checkout.session.completed","data":{"object":{"id":"cs_live","payment_status":"paid","payment_intent":"pi_live","invoice":"in_live","metadata":{"order_id":"aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa"}}}}`) + signed := []byte("1800000000." + string(payload)) + mac := hmac.New(sha256.New, []byte("whsec_synthetic")) + _, _ = mac.Write(signed) + signature := "t=1800000000,v1=" + hex.EncodeToString(mac.Sum(nil)) + + event, err := gateway.VerifyWebhook(payload, signature) + if err != nil { + t.Fatalf("verify: %v", err) + } + if event.ID != "evt_checkout" || event.Kind != EventPaid || event.OrderID != "aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa" || event.PaymentIntentID != "pi_live" || event.InvoiceID != "in_live" { + t.Fatalf("event = %#v", event) + } + if _, err := gateway.VerifyWebhook([]byte(strings.ReplaceAll(string(payload), "paid", "unpaid")), signature); err == nil { + t.Fatal("tampered payload accepted") + } +} + +func TestProjectStripeRefundAndDisputeFields(t *testing.T) { + refund, err := projectStripeEvent([]byte(`{"id":"evt_refund","type":"charge.refunded","created":1800000000,"data":{"object":{"id":"ch_refund","payment_intent":"pi_refund","amount_refunded":12500,"currency":"eur"}}}`)) + if err != nil || refund.Kind != EventRefunded || refund.PaymentIntentID != "pi_refund" || refund.AmountMinor != 12500 || refund.Currency != "EUR" { + t.Fatalf("refund = %#v, err = %v", refund, err) + } + dispute, err := projectStripeEvent([]byte(`{"id":"evt_dispute","type":"charge.dispute.created","created":1800000000,"data":{"object":{"id":"dp_test","charge":"ch_test","payment_intent":"pi_test","status":"needs_response","reason":"fraudulent","amount":12500,"currency":"eur"}}}`)) + if err != nil || dispute.Kind != EventDisputeOpened || dispute.PaymentIntentID != "pi_test" || dispute.DisputeID != "dp_test" || dispute.AmountMinor != 12500 { + t.Fatalf("dispute = %#v, err = %v", dispute, err) + } +} diff --git a/backend/internal/provideraccess/service.go b/backend/internal/provideraccess/service.go new file mode 100644 index 0000000..647d269 --- /dev/null +++ b/backend/internal/provideraccess/service.go @@ -0,0 +1,60 @@ +package provideraccess + +import ( + "context" + "errors" + + "github.com/SourceSenseiTheRealOne/juntly/backend/internal/accounts" + "github.com/SourceSenseiTheRealOne/juntly/backend/internal/users" +) + +var ( + ErrUnauthorized = errors.New("provider access unauthorized") + ErrForbidden = errors.New("provider access forbidden") + ErrUnavailable = errors.New("provider access unavailable") +) + +type InternalUserReconciler interface { + Reconcile(context.Context, users.VerifiedIdentity) (users.InternalUser, bool, error) +} + +type CapabilityReader interface { + Get(context.Context, users.VerifiedIdentity) (accounts.Account, error) +} + +type Service interface { + RequireProvider(context.Context, users.VerifiedIdentity) (users.InternalUser, error) +} + +type service struct { + identities InternalUserReconciler + capabilities CapabilityReader +} + +func NewService(identities InternalUserReconciler, capabilities CapabilityReader) Service { + return service{identities: identities, capabilities: capabilities} +} + +func (s service) RequireProvider(ctx context.Context, identity users.VerifiedIdentity) (users.InternalUser, error) { + if s.identities == nil || s.capabilities == nil { + return users.InternalUser{}, ErrUnavailable + } + owner, _, err := s.identities.Reconcile(ctx, identity) + if err != nil { + if errors.Is(err, users.ErrInvalidIdentity) { + return users.InternalUser{}, ErrUnauthorized + } + return users.InternalUser{}, ErrUnavailable + } + account, err := s.capabilities.Get(ctx, identity) + if err != nil { + if errors.Is(err, accounts.ErrInvalidIdentity) { + return users.InternalUser{}, ErrUnauthorized + } + return users.InternalUser{}, ErrUnavailable + } + if !account.ProviderEnabled { + return users.InternalUser{}, ErrForbidden + } + return owner, nil +} diff --git a/backend/internal/provideraccess/service_test.go b/backend/internal/provideraccess/service_test.go new file mode 100644 index 0000000..6d6a3ce --- /dev/null +++ b/backend/internal/provideraccess/service_test.go @@ -0,0 +1,106 @@ +package provideraccess + +import ( + "context" + "errors" + "testing" + "time" + + "github.com/SourceSenseiTheRealOne/juntly/backend/internal/accounts" + "github.com/SourceSenseiTheRealOne/juntly/backend/internal/users" + "github.com/google/uuid" +) + +func TestRequireProviderRejectsInvalidIdentity(t *testing.T) { + t.Parallel() + + identities := &recordingIdentityService{err: users.ErrInvalidIdentity} + capabilities := &recordingCapabilityReader{} + _, err := NewService(identities, capabilities).RequireProvider(context.Background(), users.VerifiedIdentity{}) + + if !errors.Is(err, ErrUnauthorized) { + t.Fatalf("error = %v, want ErrUnauthorized", err) + } + if capabilities.calls != 0 { + t.Fatalf("capability calls = %d, want 0", capabilities.calls) + } +} + +func TestRequireProviderRejectsDisabledCapability(t *testing.T) { + t.Parallel() + + identities := &recordingIdentityService{user: testInternalUser()} + capabilities := &recordingCapabilityReader{account: accounts.Account{CustomerEnabled: true}} + _, err := NewService(identities, capabilities).RequireProvider( + context.Background(), + users.VerifiedIdentity{Subject: "user_provider"}, + ) + + if !errors.Is(err, ErrForbidden) { + t.Fatalf("error = %v, want ErrForbidden", err) + } +} + +func TestRequireProviderReturnsVerifiedInternalOwner(t *testing.T) { + t.Parallel() + + owner := testInternalUser() + identities := &recordingIdentityService{user: owner} + capabilities := &recordingCapabilityReader{account: accounts.Account{CustomerEnabled: true, ProviderEnabled: true}} + + result, err := NewService(identities, capabilities).RequireProvider( + context.Background(), + users.VerifiedIdentity{Subject: "user_provider"}, + ) + + if err != nil || result != owner { + t.Fatalf("owner = %#v, err = %v, want %#v", result, err, owner) + } + if identities.calls != 1 || capabilities.calls != 1 { + t.Fatalf("calls = identity:%d capability:%d, want 1:1", identities.calls, capabilities.calls) + } +} + +func TestRequireProviderMapsDependencyFailure(t *testing.T) { + t.Parallel() + + identities := &recordingIdentityService{user: testInternalUser()} + capabilities := &recordingCapabilityReader{err: accounts.ErrUnavailable} + _, err := NewService(identities, capabilities).RequireProvider( + context.Background(), + users.VerifiedIdentity{Subject: "user_provider"}, + ) + + if !errors.Is(err, ErrUnavailable) { + t.Fatalf("error = %v, want ErrUnavailable", err) + } +} + +func testInternalUser() users.InternalUser { + return users.InternalUser{ + ID: uuid.MustParse("b00f5bf7-72a8-4a7d-bb23-2ef4c4daf3fb"), + CreatedAt: time.Date(2026, 8, 23, 16, 0, 0, 0, time.UTC), + } +} + +type recordingIdentityService struct { + user users.InternalUser + err error + calls int +} + +func (s *recordingIdentityService) Reconcile(context.Context, users.VerifiedIdentity) (users.InternalUser, bool, error) { + s.calls++ + return s.user, false, s.err +} + +type recordingCapabilityReader struct { + account accounts.Account + err error + calls int +} + +func (r *recordingCapabilityReader) Get(context.Context, users.VerifiedIdentity) (accounts.Account, error) { + r.calls++ + return r.account, r.err +} diff --git a/backend/internal/providers/ent_repository.go b/backend/internal/providers/ent_repository.go new file mode 100644 index 0000000..1575fd4 --- /dev/null +++ b/backend/internal/providers/ent_repository.go @@ -0,0 +1,185 @@ +package providers + +import ( + "bytes" + "context" + "errors" + "slices" + "time" + + "github.com/SourceSenseiTheRealOne/juntly/backend/ent" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/providerprofile" + "github.com/google/uuid" +) + +type entRepository struct{ client *ent.Client } + +func NewEntRepository(client *ent.Client) Repository { + return entRepository{client: client} +} + +func (r entRepository) FindByOwner(ctx context.Context, owner uuid.UUID) (*Profile, error) { + if r.client == nil { + return nil, errors.New("Ent client is nil") + } + entity, err := queryProviderProfile(ctx, r.client, owner) + if err != nil { + if ent.IsNotFound(err) { + return nil, nil + } + return nil, err + } + profile := profileFromEnt(entity) + return &profile, nil +} + +func (r entRepository) Replace(ctx context.Context, owner uuid.UUID, input ReplaceProfile) (Profile, error) { + if r.client == nil { + return Profile{}, errors.New("Ent client is nil") + } + return r.replace(ctx, owner, canonicalReplacement(input), true) +} + +func (r entRepository) replace(ctx context.Context, owner uuid.UUID, input ReplaceProfile, retryConflict bool) (Profile, error) { + tx, err := r.client.Tx(ctx) + if err != nil { + return Profile{}, err + } + committed := false + defer func() { + if !committed { + _ = tx.Rollback() + } + }() + client := tx.Client() + + existing, err := queryProviderProfile(ctx, client, owner) + switch { + case ent.IsNotFound(err): + _, err = client.ProviderProfile.Create(). + SetID(owner). + SetDisplayName(input.DisplayName). + SetProviderType(string(input.ProviderType)). + SetBio(input.Bio). + SetPrimaryLocalityID(input.PrimaryLocalityID). + SetMaxTravelDistanceKm(input.MaxTravelDistanceKM). + SetTravelsToCustomer(input.TravelsToCustomer). + SetReceivesCustomer(input.ReceivesCustomer). + SetRemoteServices(input.RemoteServices). + AddServiceLocalityIDs(input.ServiceLocalityIDs...). + AddSpokenLanguageIDs(input.LanguageCodes...). + Save(ctx) + if err != nil { + _ = tx.Rollback() + committed = true + if retryConflict && ent.IsConstraintError(err) { + return r.replace(ctx, owner, input, false) + } + return Profile{}, err + } + case err != nil: + return Profile{}, err + case profileMatchesReplacement(profileFromEnt(existing), input): + profile := profileFromEnt(existing) + if err := tx.Commit(); err != nil { + return Profile{}, err + } + committed = true + return profile, nil + default: + _, err = client.ProviderProfile.UpdateOneID(owner). + SetDisplayName(input.DisplayName). + SetProviderType(string(input.ProviderType)). + SetBio(input.Bio). + SetPrimaryLocalityID(input.PrimaryLocalityID). + SetMaxTravelDistanceKm(input.MaxTravelDistanceKM). + SetTravelsToCustomer(input.TravelsToCustomer). + SetReceivesCustomer(input.ReceivesCustomer). + SetRemoteServices(input.RemoteServices). + ClearServiceLocalities(). + AddServiceLocalityIDs(input.ServiceLocalityIDs...). + ClearSpokenLanguages(). + AddSpokenLanguageIDs(input.LanguageCodes...). + Save(ctx) + if err != nil { + return Profile{}, err + } + } + + canonical, err := queryProviderProfile(ctx, client, owner) + if err != nil { + return Profile{}, err + } + profile := profileFromEnt(canonical) + if err := tx.Commit(); err != nil { + return Profile{}, err + } + committed = true + return profile, nil +} + +func queryProviderProfile(ctx context.Context, client *ent.Client, owner uuid.UUID) (*ent.ProviderProfile, error) { + return client.ProviderProfile.Query(). + Where(providerprofile.IDEQ(owner)). + WithServiceLocalities(). + WithSpokenLanguages(). + Only(ctx) +} + +func profileFromEnt(entity *ent.ProviderProfile) Profile { + localityIDs := make([]uuid.UUID, 0, len(entity.Edges.ServiceLocalities)) + for _, locality := range entity.Edges.ServiceLocalities { + localityIDs = append(localityIDs, locality.ID) + } + languageCodes := make([]string, 0, len(entity.Edges.SpokenLanguages)) + for _, language := range entity.Edges.SpokenLanguages { + languageCodes = append(languageCodes, language.ID) + } + sortUUIDs(localityIDs) + slices.Sort(languageCodes) + return Profile{ + DisplayName: entity.DisplayName, + ProviderType: ProviderType(entity.ProviderType), + Bio: entity.Bio, + PrimaryLocalityID: entity.PrimaryLocalityID, + ServiceLocalityIDs: localityIDs, + MaxTravelDistanceKM: entity.MaxTravelDistanceKm, + TravelsToCustomer: entity.TravelsToCustomer, + ReceivesCustomer: entity.ReceivesCustomer, + RemoteServices: entity.RemoteServices, + LanguageCodes: languageCodes, + CreatedAt: normalizeTime(entity.CreatedAt), + UpdatedAt: normalizeTime(entity.UpdatedAt), + } +} + +func canonicalReplacement(input ReplaceProfile) ReplaceProfile { + input.ServiceLocalityIDs = append([]uuid.UUID(nil), input.ServiceLocalityIDs...) + input.LanguageCodes = append([]string(nil), input.LanguageCodes...) + sortUUIDs(input.ServiceLocalityIDs) + slices.Sort(input.LanguageCodes) + return input +} + +func profileMatchesReplacement(profile Profile, input ReplaceProfile) bool { + return profile.DisplayName == input.DisplayName && + profile.ProviderType == input.ProviderType && + profile.Bio == input.Bio && + profile.PrimaryLocalityID == input.PrimaryLocalityID && + profile.MaxTravelDistanceKM == input.MaxTravelDistanceKM && + profile.TravelsToCustomer == input.TravelsToCustomer && + profile.ReceivesCustomer == input.ReceivesCustomer && + profile.RemoteServices == input.RemoteServices && + slices.Equal(profile.ServiceLocalityIDs, input.ServiceLocalityIDs) && + slices.Equal(profile.LanguageCodes, input.LanguageCodes) +} + +func sortUUIDs(values []uuid.UUID) { + slices.SortFunc(values, func(left, right uuid.UUID) int { + return bytes.Compare(left[:], right[:]) + }) +} + +func normalizeTime(value time.Time) time.Time { + return value.UTC().Truncate(time.Microsecond) +} diff --git a/backend/internal/providers/ent_repository_test.go b/backend/internal/providers/ent_repository_test.go new file mode 100644 index 0000000..bb090e8 --- /dev/null +++ b/backend/internal/providers/ent_repository_test.go @@ -0,0 +1,252 @@ +package providers + +import ( + "context" + "database/sql" + "os" + "reflect" + "sync" + "testing" + + "entgo.io/ent/dialect" + entsql "entgo.io/ent/dialect/sql" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/locality" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/spokenlanguage" + "github.com/SourceSenseiTheRealOne/juntly/backend/internal/accounts" + "github.com/SourceSenseiTheRealOne/juntly/backend/internal/users" + "github.com/google/uuid" + _ "github.com/jackc/pgx/v5/stdlib" +) + +func TestEntRepositoryCreatesReadsReplacesAndKeepsIdempotentTimestamps(t *testing.T) { + client := openProviderIntegrationClient(t) + ctx := context.Background() + owner := createProviderOwner(t, client) + localities, languages := seededProfileReferences(t, client) + repository := NewEntRepository(client) + + firstInput := integrationReplacement(localities[:1], languages[:1]) + created, err := repository.Replace(ctx, owner.ID, firstInput) + if err != nil { + t.Fatalf("create profile: %v", err) + } + assertProfileMatchesInput(t, created, firstInput) + + found, err := repository.FindByOwner(ctx, owner.ID) + if err != nil || found == nil { + t.Fatalf("find profile = %#v, err = %v", found, err) + } + assertProfileMatchesInput(t, *found, firstInput) + + secondInput := integrationReplacement(localities[:2], languages[:2]) + secondInput.DisplayName = "Prestador atualizado" + updated, err := repository.Replace(ctx, owner.ID, secondInput) + if err != nil { + t.Fatalf("replace profile: %v", err) + } + assertProfileMatchesInput(t, updated, secondInput) + if updated.CreatedAt != created.CreatedAt { + t.Fatalf("created timestamp changed: got %s want %s", updated.CreatedAt, created.CreatedAt) + } + + repeated, err := repository.Replace(ctx, owner.ID, secondInput) + if err != nil { + t.Fatalf("repeat profile replacement: %v", err) + } + if repeated.UpdatedAt != updated.UpdatedAt { + t.Fatalf("idempotent updated timestamp changed: got %s want %s", repeated.UpdatedAt, updated.UpdatedAt) + } + + missing, err := repository.FindByOwner(ctx, uuid.New()) + if err != nil || missing != nil { + t.Fatalf("cross-owner lookup = %#v, err = %v, want nil nil", missing, err) + } +} + +func TestEntRepositoryRollsBackScalarChangesWhenChildReplacementFails(t *testing.T) { + client := openProviderIntegrationClient(t) + ctx := context.Background() + owner := createProviderOwner(t, client) + localities, languages := seededProfileReferences(t, client) + repository := NewEntRepository(client) + originalInput := integrationReplacement(localities[:1], languages[:1]) + original, err := repository.Replace(ctx, owner.ID, originalInput) + if err != nil { + t.Fatalf("create original profile: %v", err) + } + + invalid := originalInput + invalid.DisplayName = "Must roll back" + invalid.LanguageCodes = []string{"missing-language"} + if _, err := repository.Replace(ctx, owner.ID, invalid); err == nil { + t.Fatal("invalid child replacement error = nil") + } + + found, err := repository.FindByOwner(ctx, owner.ID) + if err != nil || found == nil { + t.Fatalf("find after rollback = %#v, err = %v", found, err) + } + assertProfileMatchesInput(t, *found, originalInput) + if found.UpdatedAt != original.UpdatedAt { + t.Fatalf("rollback changed updated timestamp: got %s want %s", found.UpdatedAt, original.UpdatedAt) + } +} + +func TestEntRepositoryConcurrentFirstReplacementProducesOneStableProfile(t *testing.T) { + client := openProviderIntegrationClient(t) + ctx := context.Background() + owner := createProviderOwner(t, client) + localities, languages := seededProfileReferences(t, client) + repository := NewEntRepository(client) + input := integrationReplacement(localities[:2], languages[:2]) + + const attempts = 8 + start := make(chan struct{}) + results := make(chan Profile, attempts) + errs := make(chan error, attempts) + var workers sync.WaitGroup + for range attempts { + workers.Add(1) + go func() { + defer workers.Done() + <-start + profile, err := repository.Replace(ctx, owner.ID, input) + if err != nil { + errs <- err + return + } + results <- profile + }() + } + close(start) + workers.Wait() + close(results) + close(errs) + for err := range errs { + t.Fatalf("concurrent replace: %v", err) + } + + var stable *Profile + for profile := range results { + if stable == nil { + value := profile + stable = &value + continue + } + if !reflect.DeepEqual(profile, *stable) { + t.Fatalf("profile = %#v, want stable %#v", profile, *stable) + } + } + if stable == nil { + t.Fatal("no concurrent profile result") + } + assertProfileMatchesInput(t, *stable, input) + + var count int + if err := providerDatabase(t, client).QueryRowContext(ctx, "select count(*) from public.provider_profiles where internal_user_id = $1", owner.ID).Scan(&count); err != nil { + t.Fatalf("count provider profiles: %v", err) + } + if count != 1 { + t.Fatalf("provider profile count = %d, want 1", count) + } +} + +func openProviderIntegrationClient(t *testing.T) *ent.Client { + t.Helper() + databaseURL := os.Getenv("TEST_DATABASE_URL") + if databaseURL == "" { + t.Skip("TEST_DATABASE_URL is required for PostgreSQL integration tests") + } + database, err := sql.Open("pgx", databaseURL) + if err != nil { + t.Fatalf("open database: %v", err) + } + client := ent.NewClient(ent.Driver(entsql.OpenDB(dialect.Postgres, database))) + t.Cleanup(func() { + if err := client.Close(); err != nil { + t.Errorf("close client: %v", err) + } + }) + providerDatabases.Store(client, database) + return client +} + +var providerDatabases sync.Map + +func providerDatabase(t *testing.T, client *ent.Client) *sql.DB { + t.Helper() + value, ok := providerDatabases.Load(client) + if !ok { + t.Fatal("provider database not registered") + } + return value.(*sql.DB) +} + +func createProviderOwner(t *testing.T, client *ent.Client) users.InternalUser { + t.Helper() + ctx := context.Background() + identity := users.VerifiedIdentity{Subject: "test_provider_" + uuid.NewString()} + owner, _, err := users.NewService(users.NewEntRepository(client)).Reconcile(ctx, identity) + if err != nil { + t.Fatalf("reconcile owner: %v", err) + } + accountRepository := accounts.NewEntRepository(client) + if _, err := accountRepository.Create(ctx, owner.ID); err != nil { + t.Fatalf("create owner account: %v", err) + } + if _, err := accountRepository.SetProviderEnabled(ctx, owner.ID, true); err != nil { + t.Fatalf("enable provider: %v", err) + } + t.Cleanup(func() { + if err := client.InternalUser.DeleteOneID(owner.ID).Exec(ctx); err != nil { + t.Errorf("cleanup owner: %v", err) + } + }) + return owner +} + +func seededProfileReferences(t *testing.T, client *ent.Client) ([]uuid.UUID, []string) { + t.Helper() + ctx := context.Background() + localityIDs, err := client.Locality.Query().Where(locality.ActiveEQ(true)).Order(ent.Asc(locality.FieldID)).IDs(ctx) + if err != nil || len(localityIDs) < 2 { + t.Fatalf("seeded localities = %v, err = %v", localityIDs, err) + } + languageIDs, err := client.SpokenLanguage.Query().Where(spokenlanguage.ActiveEQ(true)).Order(ent.Asc(spokenlanguage.FieldID)).IDs(ctx) + if err != nil || len(languageIDs) < 2 { + t.Fatalf("seeded languages = %v, err = %v", languageIDs, err) + } + return localityIDs, languageIDs +} + +func integrationReplacement(localityIDs []uuid.UUID, languageCodes []string) ReplaceProfile { + return ReplaceProfile{ + DisplayName: "Prestador integrado", + ProviderType: ProviderTypeProfessional, + Bio: "Perfil sintético de integração.", + PrimaryLocalityID: localityIDs[0], + ServiceLocalityIDs: append([]uuid.UUID(nil), localityIDs...), + MaxTravelDistanceKM: 25, + TravelsToCustomer: true, + ReceivesCustomer: false, + RemoteServices: false, + LanguageCodes: append([]string(nil), languageCodes...), + } +} + +func assertProfileMatchesInput(t *testing.T, profile Profile, input ReplaceProfile) { + t.Helper() + if profile.DisplayName != input.DisplayName || profile.ProviderType != input.ProviderType || profile.Bio != input.Bio || profile.PrimaryLocalityID != input.PrimaryLocalityID || profile.MaxTravelDistanceKM != input.MaxTravelDistanceKM || profile.TravelsToCustomer != input.TravelsToCustomer || profile.ReceivesCustomer != input.ReceivesCustomer || profile.RemoteServices != input.RemoteServices { + t.Fatalf("profile scalars = %#v, want %#v", profile, input) + } + if !reflect.DeepEqual(profile.ServiceLocalityIDs, input.ServiceLocalityIDs) { + t.Fatalf("service localities = %v, want %v", profile.ServiceLocalityIDs, input.ServiceLocalityIDs) + } + if !reflect.DeepEqual(profile.LanguageCodes, input.LanguageCodes) { + t.Fatalf("language codes = %v, want %v", profile.LanguageCodes, input.LanguageCodes) + } + if profile.CreatedAt.IsZero() || profile.UpdatedAt.IsZero() { + t.Fatalf("profile timestamps are zero: %#v", profile) + } +} diff --git a/backend/internal/providers/model.go b/backend/internal/providers/model.go new file mode 100644 index 0000000..8f854fa --- /dev/null +++ b/backend/internal/providers/model.go @@ -0,0 +1,49 @@ +package providers + +import ( + "errors" + "time" + + "github.com/google/uuid" +) + +var ( + ErrInvalidProfile = errors.New("invalid provider profile") + ErrUnavailable = errors.New("provider profile unavailable") +) + +type ProviderType string + +const ( + ProviderTypeIndividual ProviderType = "individual" + ProviderTypeProfessional ProviderType = "professional" + ProviderTypeBusiness ProviderType = "business" +) + +type ReplaceProfile struct { + DisplayName string + ProviderType ProviderType + Bio string + PrimaryLocalityID uuid.UUID + ServiceLocalityIDs []uuid.UUID + MaxTravelDistanceKM int + TravelsToCustomer bool + ReceivesCustomer bool + RemoteServices bool + LanguageCodes []string +} + +type Profile struct { + DisplayName string + ProviderType ProviderType + Bio string + PrimaryLocalityID uuid.UUID + ServiceLocalityIDs []uuid.UUID + MaxTravelDistanceKM int + TravelsToCustomer bool + ReceivesCustomer bool + RemoteServices bool + LanguageCodes []string + CreatedAt time.Time + UpdatedAt time.Time +} diff --git a/backend/internal/providers/repository.go b/backend/internal/providers/repository.go new file mode 100644 index 0000000..f6f2f7f --- /dev/null +++ b/backend/internal/providers/repository.go @@ -0,0 +1,12 @@ +package providers + +import ( + "context" + + "github.com/google/uuid" +) + +type Repository interface { + FindByOwner(context.Context, uuid.UUID) (*Profile, error) + Replace(context.Context, uuid.UUID, ReplaceProfile) (Profile, error) +} diff --git a/backend/internal/providers/service.go b/backend/internal/providers/service.go new file mode 100644 index 0000000..2619736 --- /dev/null +++ b/backend/internal/providers/service.go @@ -0,0 +1,170 @@ +package providers + +import ( + "context" + "errors" + "strings" + "unicode/utf8" + + "github.com/SourceSenseiTheRealOne/juntly/backend/internal/reference" + "github.com/SourceSenseiTheRealOne/juntly/backend/internal/users" + "github.com/google/uuid" +) + +type ProviderAuthorizer interface { + RequireProvider(context.Context, users.VerifiedIdentity) (users.InternalUser, error) +} + +type ReferenceValidator interface { + ValidateProfileReferences(context.Context, reference.ProfileReferences) error +} + +type Service interface { + Get(context.Context, users.VerifiedIdentity) (*Profile, error) + Put(context.Context, users.VerifiedIdentity, ReplaceProfile) (Profile, error) +} + +type service struct { + authorizer ProviderAuthorizer + repository Repository + references ReferenceValidator +} + +func NewService(authorizer ProviderAuthorizer, repository Repository, references ReferenceValidator) Service { + return service{authorizer: authorizer, repository: repository, references: references} +} + +func (s service) Get(ctx context.Context, identity users.VerifiedIdentity) (*Profile, error) { + owner, err := s.authorize(ctx, identity) + if err != nil { + return nil, err + } + if s.repository == nil { + return nil, ErrUnavailable + } + profile, err := s.repository.FindByOwner(ctx, owner.ID) + if err != nil { + return nil, ErrUnavailable + } + return cloneProfile(profile), nil +} + +func (s service) Put(ctx context.Context, identity users.VerifiedIdentity, input ReplaceProfile) (Profile, error) { + owner, err := s.authorize(ctx, identity) + if err != nil { + return Profile{}, err + } + input, valid := normalizeReplacement(input) + if !valid { + return Profile{}, ErrInvalidProfile + } + if s.references == nil || s.repository == nil { + return Profile{}, ErrUnavailable + } + if err := s.references.ValidateProfileReferences(ctx, reference.ProfileReferences{ + PrimaryLocalityID: input.PrimaryLocalityID, + ServiceLocalityIDs: input.ServiceLocalityIDs, + LanguageCodes: input.LanguageCodes, + }); err != nil { + if errors.Is(err, reference.ErrInvalidReference) { + return Profile{}, ErrInvalidProfile + } + return Profile{}, ErrUnavailable + } + profile, err := s.repository.Replace(ctx, owner.ID, input) + if err != nil { + return Profile{}, ErrUnavailable + } + return *cloneProfile(&profile), nil +} + +func (s service) authorize(ctx context.Context, identity users.VerifiedIdentity) (users.InternalUser, error) { + if s.authorizer == nil { + return users.InternalUser{}, ErrUnavailable + } + return s.authorizer.RequireProvider(ctx, identity) +} + +func normalizeReplacement(input ReplaceProfile) (ReplaceProfile, bool) { + input.DisplayName = strings.TrimSpace(input.DisplayName) + input.Bio = strings.TrimSpace(input.Bio) + input.ServiceLocalityIDs = append([]uuid.UUID(nil), input.ServiceLocalityIDs...) + input.LanguageCodes = append([]string(nil), input.LanguageCodes...) + if utf8.RuneCountInString(input.DisplayName) < 2 || utf8.RuneCountInString(input.DisplayName) > 100 { + return ReplaceProfile{}, false + } + switch input.ProviderType { + case ProviderTypeIndividual, ProviderTypeProfessional, ProviderTypeBusiness: + default: + return ReplaceProfile{}, false + } + if utf8.RuneCountInString(input.Bio) > 1000 || input.PrimaryLocalityID == uuid.Nil { + return ReplaceProfile{}, false + } + if input.MaxTravelDistanceKM < 0 || input.MaxTravelDistanceKM > 200 { + return ReplaceProfile{}, false + } + if !input.TravelsToCustomer && !input.ReceivesCustomer && !input.RemoteServices { + return ReplaceProfile{}, false + } + if input.MaxTravelDistanceKM == 0 && input.TravelsToCustomer && !input.ReceivesCustomer && !input.RemoteServices { + return ReplaceProfile{}, false + } + if !validUUIDSet(input.ServiceLocalityIDs, 1, 20, input.PrimaryLocalityID) { + return ReplaceProfile{}, false + } + if !validLanguageSet(input.LanguageCodes) { + return ReplaceProfile{}, false + } + return input, true +} + +func validUUIDSet(values []uuid.UUID, minimum, maximum int, required uuid.UUID) bool { + if len(values) < minimum || len(values) > maximum { + return false + } + seen := make(map[uuid.UUID]struct{}, len(values)) + hasRequired := false + for _, value := range values { + if value == uuid.Nil { + return false + } + if _, exists := seen[value]; exists { + return false + } + seen[value] = struct{}{} + if value == required { + hasRequired = true + } + } + return hasRequired +} + +func validLanguageSet(values []string) bool { + if len(values) < 1 || len(values) > 10 { + return false + } + seen := make(map[string]struct{}, len(values)) + for index, value := range values { + value = strings.TrimSpace(value) + if value == "" || utf8.RuneCountInString(value) > 10 { + return false + } + if _, exists := seen[value]; exists { + return false + } + seen[value] = struct{}{} + values[index] = value + } + return true +} + +func cloneProfile(profile *Profile) *Profile { + if profile == nil { + return nil + } + cloned := *profile + cloned.ServiceLocalityIDs = append([]uuid.UUID(nil), profile.ServiceLocalityIDs...) + cloned.LanguageCodes = append([]string(nil), profile.LanguageCodes...) + return &cloned +} diff --git a/backend/internal/providers/service_test.go b/backend/internal/providers/service_test.go new file mode 100644 index 0000000..6cbb82e --- /dev/null +++ b/backend/internal/providers/service_test.go @@ -0,0 +1,239 @@ +package providers + +import ( + "context" + "errors" + "reflect" + "strings" + "testing" + "time" + + "github.com/SourceSenseiTheRealOne/juntly/backend/internal/provideraccess" + "github.com/SourceSenseiTheRealOne/juntly/backend/internal/reference" + "github.com/SourceSenseiTheRealOne/juntly/backend/internal/users" + "github.com/google/uuid" +) + +func TestServiceGetIsScopedToAuthorizedOwner(t *testing.T) { + t.Parallel() + + owner := providerOwner() + authorizer := &recordingAuthorizer{owner: owner} + repository := &recordingRepository{} + + profile, err := NewService(authorizer, repository, &recordingReferenceValidator{}).Get( + context.Background(), users.VerifiedIdentity{Subject: "user_provider"}, + ) + + if err != nil || profile != nil { + t.Fatalf("profile = %#v, err = %v, want nil nil", profile, err) + } + if repository.owner != owner.ID { + t.Fatalf("repository owner = %s, want %s", repository.owner, owner.ID) + } +} + +func TestServiceRejectsUnauthorizedAndDisabledProviderBeforeRepository(t *testing.T) { + t.Parallel() + + for _, authorizationError := range []error{provideraccess.ErrUnauthorized, provideraccess.ErrForbidden, provideraccess.ErrUnavailable} { + repository := &recordingRepository{} + _, err := NewService( + &recordingAuthorizer{err: authorizationError}, + repository, + &recordingReferenceValidator{}, + ).Get(context.Background(), users.VerifiedIdentity{}) + if !errors.Is(err, authorizationError) { + t.Fatalf("error = %v, want %v", err, authorizationError) + } + if repository.calls != 0 { + t.Fatalf("repository calls = %d, want 0", repository.calls) + } + } +} + +func TestServiceValidatesFullReplacementBeforeReferencesAndRepository(t *testing.T) { + t.Parallel() + + valid := validReplacement() + cases := map[string]ReplaceProfile{ + "short display name": replace(valid, func(value *ReplaceProfile) { value.DisplayName = "A" }), + "invalid provider type": replace(valid, func(value *ReplaceProfile) { value.ProviderType = "admin" }), + "long biography": replace(valid, func(value *ReplaceProfile) { value.Bio = strings.Repeat("x", 1001) }), + "missing primary locality": replace(valid, func(value *ReplaceProfile) { value.PrimaryLocalityID = uuid.Nil }), + "empty service localities": replace(valid, func(value *ReplaceProfile) { value.ServiceLocalityIDs = nil }), + "duplicate localities": replace(valid, func(value *ReplaceProfile) { + value.ServiceLocalityIDs = []uuid.UUID{value.PrimaryLocalityID, value.PrimaryLocalityID} + }), + "primary locality omitted": replace(valid, func(value *ReplaceProfile) { value.ServiceLocalityIDs = []uuid.UUID{uuid.New()} }), + "negative radius": replace(valid, func(value *ReplaceProfile) { value.MaxTravelDistanceKM = -1 }), + "radius too large": replace(valid, func(value *ReplaceProfile) { value.MaxTravelDistanceKM = 201 }), + "no service mode": replace(valid, func(value *ReplaceProfile) { + value.TravelsToCustomer = false + value.ReceivesCustomer = false + value.RemoteServices = false + }), + "zero travel only": replace(valid, func(value *ReplaceProfile) { + value.MaxTravelDistanceKM = 0 + value.TravelsToCustomer = true + value.ReceivesCustomer = false + value.RemoteServices = false + }), + "empty languages": replace(valid, func(value *ReplaceProfile) { value.LanguageCodes = nil }), + "duplicate languages": replace(valid, func(value *ReplaceProfile) { value.LanguageCodes = []string{"pt-PT", "pt-PT"} }), + } + + for name, input := range cases { + name, input := name, input + t.Run(name, func(t *testing.T) { + t.Parallel() + repository := &recordingRepository{} + references := &recordingReferenceValidator{} + _, err := NewService(&recordingAuthorizer{owner: providerOwner()}, repository, references).Put( + context.Background(), users.VerifiedIdentity{Subject: "user_provider"}, input, + ) + if !errors.Is(err, ErrInvalidProfile) { + t.Fatalf("error = %v, want ErrInvalidProfile", err) + } + if references.calls != 0 || repository.calls != 0 { + t.Fatalf("calls = references:%d repository:%d, want none", references.calls, repository.calls) + } + }) + } +} + +func TestServiceRejectsInactiveOrMissingReferences(t *testing.T) { + t.Parallel() + + references := &recordingReferenceValidator{err: reference.ErrInvalidReference} + repository := &recordingRepository{} + _, err := NewService(&recordingAuthorizer{owner: providerOwner()}, repository, references).Put( + context.Background(), users.VerifiedIdentity{Subject: "user_provider"}, validReplacement(), + ) + + if !errors.Is(err, ErrInvalidProfile) { + t.Fatalf("error = %v, want ErrInvalidProfile", err) + } + if repository.calls != 0 { + t.Fatalf("repository calls = %d, want 0", repository.calls) + } +} + +func TestServiceReplacesProfileForAuthorizedOwner(t *testing.T) { + t.Parallel() + + owner := providerOwner() + input := validReplacement() + stored := profileFromReplacement(input) + repository := &recordingRepository{replacement: stored} + references := &recordingReferenceValidator{} + + profile, err := NewService(&recordingAuthorizer{owner: owner}, repository, references).Put( + context.Background(), users.VerifiedIdentity{Subject: "user_provider"}, input, + ) + + if err != nil || !reflect.DeepEqual(profile, stored) { + t.Fatalf("profile = %#v, err = %v, want %#v", profile, err, stored) + } + if repository.owner != owner.ID || !reflect.DeepEqual(repository.input, input) { + t.Fatalf("repository owner/input = %s %#v", repository.owner, repository.input) + } + if references.calls != 1 { + t.Fatalf("reference calls = %d, want 1", references.calls) + } +} + +func TestServiceMapsRepositoryFailureToUnavailable(t *testing.T) { + t.Parallel() + + repository := &recordingRepository{err: errors.New("database private details")} + _, err := NewService(&recordingAuthorizer{owner: providerOwner()}, repository, &recordingReferenceValidator{}).Get( + context.Background(), users.VerifiedIdentity{Subject: "user_provider"}, + ) + if !errors.Is(err, ErrUnavailable) { + t.Fatalf("error = %v, want ErrUnavailable", err) + } +} + +func providerOwner() users.InternalUser { + return users.InternalUser{ID: uuid.MustParse("b00f5bf7-72a8-4a7d-bb23-2ef4c4daf3fb")} +} + +func validReplacement() ReplaceProfile { + primary := uuid.MustParse("9cd8c899-75ad-458d-9e40-a9f8ecdc7e48") + return ReplaceProfile{ + DisplayName: "Prestador local", + ProviderType: ProviderTypeIndividual, + Bio: "Trabalho local de confiança.", + PrimaryLocalityID: primary, + ServiceLocalityIDs: []uuid.UUID{primary}, + MaxTravelDistanceKM: 25, + TravelsToCustomer: true, + ReceivesCustomer: false, + RemoteServices: false, + LanguageCodes: []string{"pt-PT"}, + } +} + +func replace(value ReplaceProfile, mutate func(*ReplaceProfile)) ReplaceProfile { + mutate(&value) + return value +} + +func profileFromReplacement(value ReplaceProfile) Profile { + return Profile{ + DisplayName: value.DisplayName, + ProviderType: value.ProviderType, + Bio: value.Bio, + PrimaryLocalityID: value.PrimaryLocalityID, + ServiceLocalityIDs: value.ServiceLocalityIDs, + MaxTravelDistanceKM: value.MaxTravelDistanceKM, + TravelsToCustomer: value.TravelsToCustomer, + ReceivesCustomer: value.ReceivesCustomer, + RemoteServices: value.RemoteServices, + LanguageCodes: value.LanguageCodes, + CreatedAt: time.Date(2026, 8, 23, 16, 0, 0, 0, time.UTC), + UpdatedAt: time.Date(2026, 8, 23, 16, 0, 0, 0, time.UTC), + } +} + +type recordingAuthorizer struct { + owner users.InternalUser + err error +} + +func (a *recordingAuthorizer) RequireProvider(context.Context, users.VerifiedIdentity) (users.InternalUser, error) { + return a.owner, a.err +} + +type recordingReferenceValidator struct { + err error + calls int +} + +func (v *recordingReferenceValidator) ValidateProfileReferences(context.Context, reference.ProfileReferences) error { + v.calls++ + return v.err +} + +type recordingRepository struct { + profile *Profile + replacement Profile + err error + calls int + owner uuid.UUID + input ReplaceProfile +} + +func (r *recordingRepository) FindByOwner(_ context.Context, owner uuid.UUID) (*Profile, error) { + r.calls++ + r.owner = owner + return r.profile, r.err +} + +func (r *recordingRepository) Replace(_ context.Context, owner uuid.UUID, input ReplaceProfile) (Profile, error) { + r.calls++ + r.owner = owner + r.input = input + return r.replacement, r.err +} diff --git a/backend/internal/quotations/model.go b/backend/internal/quotations/model.go new file mode 100644 index 0000000..3ffe119 --- /dev/null +++ b/backend/internal/quotations/model.go @@ -0,0 +1,76 @@ +package quotations + +import ( + "errors" + "time" + + "github.com/google/uuid" +) + +var ( + ErrInvalid = errors.New("quotation invalid request") + ErrUnauthorized = errors.New("quotation unauthorized") + ErrForbidden = errors.New("quotation forbidden") + ErrNotFound = errors.New("quotation not found") + ErrConflict = errors.New("quotation conflict") + ErrUnavailable = errors.New("quotation unavailable") +) + +type RequestState string + +const ( + RequestOpen RequestState = "open" + RequestAccepted RequestState = "accepted" + RequestClosed RequestState = "closed" +) + +type ProposalState string + +const ( + ProposalSubmitted ProposalState = "submitted" + ProposalAccepted ProposalState = "accepted" + ProposalRejected ProposalState = "rejected" + ProposalExpired ProposalState = "expired" +) + +type CreateRequest struct { + Title string + Description string + CategoryID uuid.UUID + LocalityID uuid.UUID + BudgetMinor *int + ProposalDeadline time.Time +} +type Request struct { + ID uuid.UUID + CustomerID uuid.UUID + Title string + Description string + CategoryID uuid.UUID + LocalityID uuid.UUID + BudgetMinor *int + ProposalDeadline time.Time + State RequestState + CreatedAt time.Time + UpdatedAt time.Time +} +type SubmitProposal struct { + PriceMinor int + Message string + AvailableAt time.Time + EstimatedMinutes *int + ExpiresAt *time.Time +} +type Proposal struct { + ID uuid.UUID + RequestID uuid.UUID + ProviderID uuid.UUID + PriceMinor int + Message string + AvailableAt time.Time + EstimatedMinutes *int + ExpiresAt *time.Time + State ProposalState + CreatedAt time.Time + UpdatedAt time.Time +} diff --git a/backend/internal/quotations/service.go b/backend/internal/quotations/service.go new file mode 100644 index 0000000..b38ca1a --- /dev/null +++ b/backend/internal/quotations/service.go @@ -0,0 +1,137 @@ +package quotations + +import ( + "context" + "errors" + "strings" + "time" + "unicode/utf8" + + "github.com/SourceSenseiTheRealOne/juntly/backend/internal/users" + "github.com/google/uuid" +) + +type IdentityReconciler interface { + Reconcile(context.Context, users.VerifiedIdentity) (users.InternalUser, bool, error) +} +type Store interface { + CreateRequest(context.Context, uuid.UUID, CreateRequest) (Request, error) + ListCustomerRequests(context.Context, uuid.UUID) ([]Request, error) + ListOpportunities(context.Context, uuid.UUID) ([]Request, error) + SubmitProposal(context.Context, uuid.UUID, uuid.UUID, SubmitProposal) (Proposal, error) + ListProposals(context.Context, uuid.UUID, uuid.UUID) ([]Proposal, error) + AcceptProposal(context.Context, uuid.UUID, uuid.UUID, uuid.UUID) (Proposal, error) +} +type Service interface { + CreateRequest(context.Context, users.VerifiedIdentity, CreateRequest) (Request, error) + ListCustomerRequests(context.Context, users.VerifiedIdentity) ([]Request, error) + ListOpportunities(context.Context, users.VerifiedIdentity) ([]Request, error) + SubmitProposal(context.Context, users.VerifiedIdentity, uuid.UUID, SubmitProposal) (Proposal, error) + ListProposals(context.Context, users.VerifiedIdentity, uuid.UUID) ([]Proposal, error) + AcceptProposal(context.Context, users.VerifiedIdentity, uuid.UUID, uuid.UUID) (Proposal, error) +} +type service struct { + identities IdentityReconciler + store Store + now func() time.Time +} + +func NewService(identities IdentityReconciler, store Store, now func() time.Time) Service { + return service{identities: identities, store: store, now: now} +} +func (s service) CreateRequest(ctx context.Context, identity users.VerifiedIdentity, input CreateRequest) (Request, error) { + input.Title = strings.TrimSpace(input.Title) + input.Description = strings.TrimSpace(input.Description) + now := s.currentTime() + if utf8.RuneCountInString(input.Title) < 5 || utf8.RuneCountInString(input.Title) > 140 || utf8.RuneCountInString(input.Description) < 20 || utf8.RuneCountInString(input.Description) > 4000 || input.CategoryID == uuid.Nil || input.LocalityID == uuid.Nil || !input.ProposalDeadline.After(now) || input.ProposalDeadline.After(now.Add(90*24*time.Hour)) || (input.BudgetMinor != nil && *input.BudgetMinor <= 0) { + return Request{}, ErrInvalid + } + actor, err := s.actor(ctx, identity) + if err != nil { + return Request{}, err + } + v, err := s.store.CreateRequest(ctx, actor, input) + return v, normalize(err) +} +func (s service) ListCustomerRequests(ctx context.Context, identity users.VerifiedIdentity) ([]Request, error) { + actor, err := s.actor(ctx, identity) + if err != nil { + return nil, err + } + v, err := s.store.ListCustomerRequests(ctx, actor) + return v, normalize(err) +} +func (s service) ListOpportunities(ctx context.Context, identity users.VerifiedIdentity) ([]Request, error) { + actor, err := s.actor(ctx, identity) + if err != nil { + return nil, err + } + v, err := s.store.ListOpportunities(ctx, actor) + return v, normalize(err) +} +func (s service) SubmitProposal(ctx context.Context, identity users.VerifiedIdentity, requestID uuid.UUID, input SubmitProposal) (Proposal, error) { + input.Message = strings.TrimSpace(input.Message) + now := s.currentTime() + if requestID == uuid.Nil || input.PriceMinor <= 0 || utf8.RuneCountInString(input.Message) < 5 || utf8.RuneCountInString(input.Message) > 2000 || input.AvailableAt.Before(now) || (input.EstimatedMinutes != nil && (*input.EstimatedMinutes < 15 || *input.EstimatedMinutes > 525600)) || (input.ExpiresAt != nil && (!input.ExpiresAt.After(now) || input.ExpiresAt.After(now.Add(90*24*time.Hour)))) { + return Proposal{}, ErrInvalid + } + actor, err := s.actor(ctx, identity) + if err != nil { + return Proposal{}, err + } + v, err := s.store.SubmitProposal(ctx, actor, requestID, input) + return v, normalize(err) +} +func (s service) ListProposals(ctx context.Context, identity users.VerifiedIdentity, requestID uuid.UUID) ([]Proposal, error) { + if requestID == uuid.Nil { + return nil, ErrInvalid + } + actor, err := s.actor(ctx, identity) + if err != nil { + return nil, err + } + v, err := s.store.ListProposals(ctx, actor, requestID) + return v, normalize(err) +} +func (s service) AcceptProposal(ctx context.Context, identity users.VerifiedIdentity, requestID, proposalID uuid.UUID) (Proposal, error) { + if requestID == uuid.Nil || proposalID == uuid.Nil { + return Proposal{}, ErrInvalid + } + actor, err := s.actor(ctx, identity) + if err != nil { + return Proposal{}, err + } + v, err := s.store.AcceptProposal(ctx, actor, requestID, proposalID) + return v, normalize(err) +} +func (s service) actor(ctx context.Context, identity users.VerifiedIdentity) (uuid.UUID, error) { + if s.identities == nil || s.store == nil { + return uuid.Nil, ErrUnavailable + } + u, _, err := s.identities.Reconcile(ctx, identity) + if err != nil { + if errors.Is(err, users.ErrInvalidIdentity) { + return uuid.Nil, ErrUnauthorized + } + return uuid.Nil, ErrUnavailable + } + if u.ID == uuid.Nil { + return uuid.Nil, ErrUnauthorized + } + return u.ID, nil +} +func (s service) currentTime() time.Time { + if s.now == nil { + return time.Time{} + } + return s.now().UTC() +} +func normalize(err error) error { + if err == nil { + return nil + } + if errors.Is(err, ErrInvalid) || errors.Is(err, ErrUnauthorized) || errors.Is(err, ErrForbidden) || errors.Is(err, ErrNotFound) || errors.Is(err, ErrConflict) { + return err + } + return ErrUnavailable +} diff --git a/backend/internal/quotations/service_test.go b/backend/internal/quotations/service_test.go new file mode 100644 index 0000000..3d987b8 --- /dev/null +++ b/backend/internal/quotations/service_test.go @@ -0,0 +1,90 @@ +package quotations + +import ( + "context" + "errors" + "testing" + "time" + + "github.com/SourceSenseiTheRealOne/juntly/backend/internal/users" + "github.com/google/uuid" +) + +func TestServiceCreatesCustomerRequestWithReconciledOwner(t *testing.T) { + t.Parallel() + ownerID := uuid.MustParse("11111111-1111-4111-8111-111111111111") + categoryID := uuid.MustParse("22222222-2222-4222-8222-222222222222") + localityID := uuid.MustParse("33333333-3333-4333-8333-333333333333") + deadline := time.Date(2026, 9, 8, 12, 0, 0, 0, time.UTC) + store := &recordingStore{request: Request{ID: uuid.New(), CustomerID: ownerID}} + service := NewService(staticIdentity{user: users.InternalUser{ID: ownerID}}, store, func() time.Time { return time.Date(2026, 9, 1, 12, 0, 0, 0, time.UTC) }) + + value, err := service.CreateRequest(context.Background(), users.VerifiedIdentity{Subject: "user_customer"}, CreateRequest{Title: "Reparar telhado", Description: "Preciso de reparar uma pequena infiltração no telhado.", CategoryID: categoryID, LocalityID: localityID, ProposalDeadline: deadline}) + if err != nil { + t.Fatalf("CreateRequest() error = %v", err) + } + if value.ID != store.request.ID || store.actorID != ownerID || store.created.Title != "Reparar telhado" { + t.Fatalf("request/store = %#v/%s/%#v", value, store.actorID, store.created) + } +} + +func TestServiceRejectsInvalidProposalBeforeStore(t *testing.T) { + t.Parallel() + store := &recordingStore{} + service := NewService(staticIdentity{user: users.InternalUser{ID: uuid.New()}}, store, time.Now) + _, err := service.SubmitProposal(context.Background(), users.VerifiedIdentity{Subject: "user_provider"}, uuid.New(), SubmitProposal{PriceMinor: 0, Message: "ok"}) + if !errors.Is(err, ErrInvalid) { + t.Fatalf("SubmitProposal() error = %v, want ErrInvalid", err) + } + if store.submitted.PriceMinor != 0 || store.actorID != uuid.Nil { + t.Fatal("store was called for invalid proposal") + } +} + +func TestServicePreservesProposalPrivacyDenial(t *testing.T) { + t.Parallel() + store := &recordingStore{listErr: ErrForbidden} + service := NewService(staticIdentity{user: users.InternalUser{ID: uuid.New()}}, store, time.Now) + _, err := service.ListProposals(context.Background(), users.VerifiedIdentity{Subject: "user_competitor"}, uuid.New()) + if !errors.Is(err, ErrForbidden) { + t.Fatalf("ListProposals() error = %v, want ErrForbidden", err) + } +} + +type staticIdentity struct { + user users.InternalUser + err error +} + +func (s staticIdentity) Reconcile(context.Context, users.VerifiedIdentity) (users.InternalUser, bool, error) { + return s.user, false, s.err +} + +type recordingStore struct { + request Request + actorID uuid.UUID + created CreateRequest + submitted SubmitProposal + listErr error +} + +func (s *recordingStore) CreateRequest(_ context.Context, actorID uuid.UUID, input CreateRequest) (Request, error) { + s.actorID, s.created = actorID, input + return s.request, nil +} +func (s *recordingStore) ListCustomerRequests(context.Context, uuid.UUID) ([]Request, error) { + return nil, nil +} +func (s *recordingStore) ListOpportunities(context.Context, uuid.UUID) ([]Request, error) { + return nil, nil +} +func (s *recordingStore) SubmitProposal(_ context.Context, actorID, requestID uuid.UUID, input SubmitProposal) (Proposal, error) { + s.actorID, s.submitted = actorID, input + return Proposal{}, nil +} +func (s *recordingStore) ListProposals(context.Context, uuid.UUID, uuid.UUID) ([]Proposal, error) { + return nil, s.listErr +} +func (s *recordingStore) AcceptProposal(context.Context, uuid.UUID, uuid.UUID, uuid.UUID) (Proposal, error) { + return Proposal{}, nil +} diff --git a/backend/internal/quotations/sql_store.go b/backend/internal/quotations/sql_store.go new file mode 100644 index 0000000..58ac1e4 --- /dev/null +++ b/backend/internal/quotations/sql_store.go @@ -0,0 +1,181 @@ +package quotations + +import ( + "context" + "database/sql" + "errors" + + "github.com/google/uuid" +) + +type sqlStore struct{ database *sql.DB } + +func NewSQLStore(database *sql.DB) Store { return sqlStore{database: database} } +func (s sqlStore) CreateRequest(ctx context.Context, actor uuid.UUID, input CreateRequest) (Request, error) { + tx, err := s.database.BeginTx(ctx, nil) + if err != nil { + return Request{}, err + } + defer tx.Rollback() + var v Request + err = tx.QueryRowContext(ctx, `insert into public.quotation_requests (customer_internal_user_id,category_id,locality_id,title,description,budget_minor,proposal_deadline) select $1,$2,$3,$4,$5,$6,$7 where exists(select 1 from public.service_categories where id=$2 and active) and exists(select 1 from public.localities where id=$3 and active) returning id,customer_internal_user_id,title,description,category_id,locality_id,budget_minor,proposal_deadline,state,created_at,updated_at`, actor, input.CategoryID, input.LocalityID, input.Title, input.Description, input.BudgetMinor, input.ProposalDeadline).Scan(&v.ID, &v.CustomerID, &v.Title, &v.Description, &v.CategoryID, &v.LocalityID, &v.BudgetMinor, &v.ProposalDeadline, &v.State, &v.CreatedAt, &v.UpdatedAt) + if errors.Is(err, sql.ErrNoRows) { + return Request{}, ErrInvalid + } + if err != nil { + return Request{}, err + } + if _, err = tx.ExecContext(ctx, `insert into public.notifications(recipient_internal_user_id,kind,resource_id,in_app_visible) + select distinct l.internal_user_id,'request_published',$1,coalesce(p.in_app_enabled,true) + from public.listings l left join public.provider_service_localities psl on psl.internal_user_id=l.internal_user_id and psl.locality_id=$3 + left join public.notification_preferences p on p.internal_user_id=l.internal_user_id + where l.category_id=$2 and l.state='active' and l.internal_user_id<>$4 and (l.primary_locality_id=$3 or psl.locality_id is not null) + and coalesce(p.in_app_enabled or p.email_enabled,true) on conflict do nothing`, v.ID, v.CategoryID, v.LocalityID, actor); err != nil { + return Request{}, err + } + if _, err = tx.ExecContext(ctx, `insert into public.notification_email_outbox(notification_id,recipient_internal_user_id) + select n.id,n.recipient_internal_user_id from public.notifications n left join public.notification_preferences p on p.internal_user_id=n.recipient_internal_user_id + where n.kind='request_published' and n.resource_id=$1 and coalesce(p.email_enabled,true) on conflict(notification_id) do nothing`, v.ID); err != nil { + return Request{}, err + } + if err = tx.Commit(); err != nil { + return Request{}, err + } + return v, nil +} +func (s sqlStore) ListCustomerRequests(ctx context.Context, actor uuid.UUID) ([]Request, error) { + rows, err := s.database.QueryContext(ctx, `select id,customer_internal_user_id,title,description,category_id,locality_id,budget_minor,proposal_deadline,state,created_at,updated_at from public.quotation_requests where customer_internal_user_id=$1 order by updated_at desc,id`, actor) + if err != nil { + return nil, err + } + defer rows.Close() + return scanRequests(rows) +} +func (s sqlStore) ListOpportunities(ctx context.Context, actor uuid.UUID) ([]Request, error) { + rows, err := s.database.QueryContext(ctx, `select distinct r.id,r.customer_internal_user_id,r.title,r.description,r.category_id,r.locality_id,r.budget_minor,r.proposal_deadline,r.state,r.created_at,r.updated_at from public.quotation_requests r join public.listings l on l.internal_user_id=$1 and l.category_id=r.category_id and l.state='active' left join public.provider_service_localities psl on psl.internal_user_id=$1 and psl.locality_id=r.locality_id where r.state='open' and r.proposal_deadline>timezone('utc',now()) and r.customer_internal_user_id<>$1 and (l.primary_locality_id=r.locality_id or psl.locality_id is not null) order by r.updated_at desc,r.id`, actor) + if err != nil { + return nil, err + } + defer rows.Close() + return scanRequests(rows) +} +func (s sqlStore) SubmitProposal(ctx context.Context, actor, requestID uuid.UUID, input SubmitProposal) (Proposal, error) { + tx, err := s.database.BeginTx(ctx, nil) + if err != nil { + return Proposal{}, err + } + defer tx.Rollback() + var v Proposal + err = tx.QueryRowContext(ctx, `insert into public.quotation_proposals (request_id,provider_internal_user_id,price_minor,message,available_at,estimated_minutes,expires_at) select r.id,$1,$3,$4,$5,$6,$7 from public.quotation_requests r where r.id=$2 and r.state='open' and r.proposal_deadline>timezone('utc',now()) and r.customer_internal_user_id<>$1 and exists(select 1 from public.listings l left join public.provider_service_localities psl on psl.internal_user_id=$1 and psl.locality_id=r.locality_id where l.internal_user_id=$1 and l.category_id=r.category_id and l.state='active' and (l.primary_locality_id=r.locality_id or psl.locality_id is not null)) on conflict(request_id,provider_internal_user_id) do nothing returning id,request_id,provider_internal_user_id,price_minor,message,available_at,estimated_minutes,expires_at,state,created_at,updated_at`, actor, requestID, input.PriceMinor, input.Message, input.AvailableAt, input.EstimatedMinutes, input.ExpiresAt).Scan(&v.ID, &v.RequestID, &v.ProviderID, &v.PriceMinor, &v.Message, &v.AvailableAt, &v.EstimatedMinutes, &v.ExpiresAt, &v.State, &v.CreatedAt, &v.UpdatedAt) + if errors.Is(err, sql.ErrNoRows) { + return Proposal{}, ErrForbidden + } + if err != nil { + return Proposal{}, err + } + if _, err = tx.ExecContext(ctx, `insert into public.notifications(recipient_internal_user_id,kind,resource_id,in_app_visible) + select r.customer_internal_user_id,'proposal_received',$1,coalesce(p.in_app_enabled,true) from public.quotation_requests r + left join public.notification_preferences p on p.internal_user_id=r.customer_internal_user_id where r.id=$2 + and coalesce(p.in_app_enabled or p.email_enabled,true) on conflict do nothing`, v.ID, requestID); err != nil { + return Proposal{}, err + } + if _, err = tx.ExecContext(ctx, `insert into public.notification_email_outbox(notification_id,recipient_internal_user_id) + select n.id,n.recipient_internal_user_id from public.notifications n left join public.notification_preferences p on p.internal_user_id=n.recipient_internal_user_id + where n.kind='proposal_received' and n.resource_id=$1 and coalesce(p.email_enabled,true) on conflict(notification_id) do nothing`, v.ID); err != nil { + return Proposal{}, err + } + if err = tx.Commit(); err != nil { + return Proposal{}, err + } + return v, nil +} +func (s sqlStore) ListProposals(ctx context.Context, actor, requestID uuid.UUID) ([]Proposal, error) { + if _, err := s.database.ExecContext(ctx, `update public.quotation_proposals set state='expired',updated_at=timezone('utc',now()) where request_id=$1 and state='submitted' and expires_at is not null and expires_at<=timezone('utc',now())`, requestID); err != nil { + return nil, err + } + rows, err := s.database.QueryContext(ctx, `select p.id,p.request_id,p.provider_internal_user_id,p.price_minor,p.message,p.available_at,p.estimated_minutes,p.expires_at,p.state,p.created_at,p.updated_at from public.quotation_proposals p join public.quotation_requests r on r.id=p.request_id where p.request_id=$1 and (r.customer_internal_user_id=$2 or p.provider_internal_user_id=$2) order by p.created_at,p.id`, requestID, actor) + if err != nil { + return nil, err + } + defer rows.Close() + values, err := scanProposals(rows) + if err == nil && len(values) == 0 { + var allowed bool + if e := s.database.QueryRowContext(ctx, `select exists(select 1 from public.quotation_requests where id=$1 and customer_internal_user_id=$2)`, requestID, actor).Scan(&allowed); e != nil { + return nil, e + } + if !allowed { + return nil, ErrForbidden + } + } + return values, err +} +func (s sqlStore) AcceptProposal(ctx context.Context, actor, requestID, proposalID uuid.UUID) (Proposal, error) { + tx, err := s.database.BeginTx(ctx, nil) + if err != nil { + return Proposal{}, err + } + defer tx.Rollback() + var state RequestState + if err = tx.QueryRowContext(ctx, `select state from public.quotation_requests where id=$1 and customer_internal_user_id=$2 for update`, requestID, actor).Scan(&state); err != nil { + if errors.Is(err, sql.ErrNoRows) { + return Proposal{}, ErrForbidden + } + return Proposal{}, err + } + if state != RequestOpen { + return Proposal{}, ErrConflict + } + var v Proposal + err = tx.QueryRowContext(ctx, `update public.quotation_proposals set state='accepted',updated_at=timezone('utc',now()) where id=$1 and request_id=$2 and state='submitted' and (expires_at is null or expires_at>timezone('utc',now())) returning id,request_id,provider_internal_user_id,price_minor,message,available_at,estimated_minutes,expires_at,state,created_at,updated_at`, proposalID, requestID).Scan(&v.ID, &v.RequestID, &v.ProviderID, &v.PriceMinor, &v.Message, &v.AvailableAt, &v.EstimatedMinutes, &v.ExpiresAt, &v.State, &v.CreatedAt, &v.UpdatedAt) + if errors.Is(err, sql.ErrNoRows) { + return Proposal{}, ErrConflict + } + if err != nil { + return Proposal{}, err + } + if _, err = tx.ExecContext(ctx, `update public.quotation_proposals set state='rejected',updated_at=timezone('utc',now()) where request_id=$1 and id<>$2 and state='submitted'`, requestID, proposalID); err != nil { + return Proposal{}, err + } + if _, err = tx.ExecContext(ctx, `update public.quotation_requests set state='accepted',updated_at=timezone('utc',now()) where id=$1`, requestID); err != nil { + return Proposal{}, err + } + if _, err = tx.ExecContext(ctx, `insert into public.notifications(recipient_internal_user_id,kind,resource_id,in_app_visible) + select qp.provider_internal_user_id,case when qp.id=$2 then 'proposal_accepted' else 'proposal_rejected' end,qp.id,coalesce(np.in_app_enabled,true) + from public.quotation_proposals qp left join public.notification_preferences np on np.internal_user_id=qp.provider_internal_user_id + where qp.request_id=$1 and qp.state in ('accepted','rejected') and coalesce(np.in_app_enabled or np.email_enabled,true) on conflict do nothing`, requestID, proposalID); err != nil { + return Proposal{}, err + } + if _, err = tx.ExecContext(ctx, `insert into public.notification_email_outbox(notification_id,recipient_internal_user_id) + select n.id,n.recipient_internal_user_id from public.notifications n left join public.notification_preferences p on p.internal_user_id=n.recipient_internal_user_id + where n.kind in ('proposal_accepted','proposal_rejected') and n.resource_id in (select id from public.quotation_proposals where request_id=$1) + and coalesce(p.email_enabled,true) on conflict(notification_id) do nothing`, requestID); err != nil { + return Proposal{}, err + } + if err = tx.Commit(); err != nil { + return Proposal{}, err + } + return v, nil +} +func scanRequests(rows *sql.Rows) ([]Request, error) { + values := []Request{} + for rows.Next() { + var v Request + if err := rows.Scan(&v.ID, &v.CustomerID, &v.Title, &v.Description, &v.CategoryID, &v.LocalityID, &v.BudgetMinor, &v.ProposalDeadline, &v.State, &v.CreatedAt, &v.UpdatedAt); err != nil { + return nil, err + } + values = append(values, v) + } + return values, rows.Err() +} +func scanProposals(rows *sql.Rows) ([]Proposal, error) { + values := []Proposal{} + for rows.Next() { + var v Proposal + if err := rows.Scan(&v.ID, &v.RequestID, &v.ProviderID, &v.PriceMinor, &v.Message, &v.AvailableAt, &v.EstimatedMinutes, &v.ExpiresAt, &v.State, &v.CreatedAt, &v.UpdatedAt); err != nil { + return nil, err + } + values = append(values, v) + } + return values, rows.Err() +} diff --git a/backend/internal/readiness/service.go b/backend/internal/readiness/service.go new file mode 100644 index 0000000..1c6f4d4 --- /dev/null +++ b/backend/internal/readiness/service.go @@ -0,0 +1,19 @@ +package readiness + +import "context" + +type Pinger interface{ PingContext(context.Context) error } +type Result struct { + Ready bool `json:"ready"` + Database string `json:"database"` +} +type Service interface{ Check(context.Context) Result } +type service struct{ database Pinger } + +func NewService(database Pinger) Service { return service{database: database} } +func (s service) Check(ctx context.Context) Result { + if s.database == nil || s.database.PingContext(ctx) != nil { + return Result{Database: "unavailable"} + } + return Result{Ready: true, Database: "ready"} +} diff --git a/backend/internal/readiness/service_test.go b/backend/internal/readiness/service_test.go new file mode 100644 index 0000000..7d15890 --- /dev/null +++ b/backend/internal/readiness/service_test.go @@ -0,0 +1,28 @@ +package readiness + +import ( + "context" + "errors" + "testing" +) + +func TestServiceReportsReadyWhenDatabaseResponds(t *testing.T) { + t.Parallel() + service := NewService(staticPinger{}) + value := service.Check(context.Background()) + if !value.Ready || value.Database != "ready" { + t.Fatalf("Check() = %#v", value) + } +} +func TestServiceReportsUnavailableWithoutLeakingDependencyError(t *testing.T) { + t.Parallel() + service := NewService(staticPinger{err: errors.New("secret connection details")}) + value := service.Check(context.Background()) + if value.Ready || value.Database != "unavailable" { + t.Fatalf("Check() = %#v", value) + } +} + +type staticPinger struct{ err error } + +func (p staticPinger) PingContext(context.Context) error { return p.err } diff --git a/backend/internal/reference/model.go b/backend/internal/reference/model.go new file mode 100644 index 0000000..8bdcf67 --- /dev/null +++ b/backend/internal/reference/model.go @@ -0,0 +1,53 @@ +package reference + +import ( + "errors" + + "github.com/google/uuid" +) + +var ( + ErrInvalidRequest = errors.New("invalid reference request") + ErrInvalidReference = errors.New("invalid profile reference") + ErrNotFound = errors.New("reference not found") + ErrUnavailable = errors.New("reference data unavailable") +) + +const ( + AttributionText = "© OpenStreetMap contributors" + AttributionURL = "https://www.openstreetmap.org/copyright" +) + +type Category struct { + ID uuid.UUID + ParentID *uuid.UUID + Slug string + Name string + SortOrder int +} + +type Language struct { + Code string + Name string + SortOrder int +} + +type Locality struct { + ID uuid.UUID + Slug string + Name string + ParishName string + MunicipalityName string + DistrictName string +} + +type LocalityDistance struct { + Locality + DistanceMeters int +} + +type ProfileReferences struct { + PrimaryLocalityID uuid.UUID + ServiceLocalityIDs []uuid.UUID + LanguageCodes []string +} diff --git a/backend/internal/reference/repository.go b/backend/internal/reference/repository.go new file mode 100644 index 0000000..ff44a6d --- /dev/null +++ b/backend/internal/reference/repository.go @@ -0,0 +1,15 @@ +package reference + +import ( + "context" + + "github.com/google/uuid" +) + +type Repository interface { + Categories(context.Context, string) ([]Category, error) + Languages(context.Context, string) ([]Language, error) + Localities(context.Context, string) ([]Locality, error) + NearbyLocalities(context.Context, uuid.UUID, int, string) ([]LocalityDistance, error) + ValidateProfileReferences(context.Context, ProfileReferences) error +} diff --git a/backend/internal/reference/service.go b/backend/internal/reference/service.go new file mode 100644 index 0000000..e54930c --- /dev/null +++ b/backend/internal/reference/service.go @@ -0,0 +1,87 @@ +package reference + +import ( + "context" + "errors" + + "github.com/google/uuid" +) + +type Service interface { + Categories(context.Context, string) ([]Category, error) + Languages(context.Context, string) ([]Language, error) + Localities(context.Context, string) ([]Locality, error) + NearbyLocalities(context.Context, uuid.UUID, int, string) ([]LocalityDistance, error) +} + +type service struct{ repository Repository } + +func NewService(repository Repository) Service { + return service{repository: repository} +} + +func (s service) Categories(ctx context.Context, locale string) ([]Category, error) { + if !validLocale(locale) { + return nil, ErrInvalidRequest + } + if s.repository == nil { + return nil, ErrUnavailable + } + values, err := s.repository.Categories(ctx, locale) + return values, publicRepositoryError(err) +} + +func (s service) Languages(ctx context.Context, locale string) ([]Language, error) { + if !validLocale(locale) { + return nil, ErrInvalidRequest + } + if s.repository == nil { + return nil, ErrUnavailable + } + values, err := s.repository.Languages(ctx, locale) + return values, publicRepositoryError(err) +} + +func (s service) Localities(ctx context.Context, locale string) ([]Locality, error) { + if !validLocale(locale) { + return nil, ErrInvalidRequest + } + if s.repository == nil { + return nil, ErrUnavailable + } + values, err := s.repository.Localities(ctx, locale) + return values, publicRepositoryError(err) +} + +func (s service) NearbyLocalities(ctx context.Context, origin uuid.UUID, radiusKM int, locale string) ([]LocalityDistance, error) { + if origin == uuid.Nil || radiusKM < 1 || radiusKM > 200 || !validLocale(locale) { + return nil, ErrInvalidRequest + } + if s.repository == nil { + return nil, ErrUnavailable + } + values, err := s.repository.NearbyLocalities(ctx, origin, radiusKM, locale) + return values, publicRepositoryError(err) +} + +func validLocale(locale string) bool { + switch locale { + case "pt-PT", "en", "es": + return true + default: + return false + } +} + +func publicRepositoryError(err error) error { + switch { + case err == nil: + return nil + case errors.Is(err, ErrNotFound): + return ErrNotFound + case errors.Is(err, ErrInvalidReference): + return ErrInvalidReference + default: + return ErrUnavailable + } +} diff --git a/backend/internal/reference/service_test.go b/backend/internal/reference/service_test.go new file mode 100644 index 0000000..42618f4 --- /dev/null +++ b/backend/internal/reference/service_test.go @@ -0,0 +1,128 @@ +package reference + +import ( + "context" + "errors" + "testing" + + "github.com/google/uuid" +) + +func TestServiceRejectsUnsupportedLocaleBeforeRepositoryAccess(t *testing.T) { + t.Parallel() + + repository := &recordingRepository{} + service := NewService(repository) + for _, locale := range []string{"", "fr", "pt", "pt-PT-extra"} { + if _, err := service.Categories(context.Background(), locale); !errors.Is(err, ErrInvalidRequest) { + t.Fatalf("locale %q error = %v, want ErrInvalidRequest", locale, err) + } + } + if repository.calls != 0 { + t.Fatalf("repository calls = %d, want 0", repository.calls) + } +} + +func TestServiceReturnsReferenceCatalogs(t *testing.T) { + t.Parallel() + + category := Category{ID: uuid.New(), Slug: "cleaning", Name: "Limpeza", SortOrder: 10} + language := Language{Code: "pt-PT", Name: "Português", SortOrder: 10} + locality := Locality{ID: uuid.New(), Slug: "zebreira", Name: "Zebreira", ParishName: "Zebreira e Segura", MunicipalityName: "Idanha-a-Nova", DistrictName: "Castelo Branco"} + repository := &recordingRepository{ + categories: []Category{category}, + languages: []Language{language}, + localities: []Locality{locality}, + } + service := NewService(repository) + ctx := context.Background() + + categories, err := service.Categories(ctx, "pt-PT") + if err != nil || len(categories) != 1 || categories[0] != category { + t.Fatalf("categories = %#v, err = %v", categories, err) + } + languages, err := service.Languages(ctx, "pt-PT") + if err != nil || len(languages) != 1 || languages[0] != language { + t.Fatalf("languages = %#v, err = %v", languages, err) + } + localities, err := service.Localities(ctx, "pt-PT") + if err != nil || len(localities) != 1 || localities[0] != locality { + t.Fatalf("localities = %#v, err = %v", localities, err) + } +} + +func TestServiceValidatesNearbyLocalityRequest(t *testing.T) { + t.Parallel() + + repository := &recordingRepository{} + service := NewService(repository) + ctx := context.Background() + validID := uuid.New() + + for _, test := range []struct { + origin uuid.UUID + radius int + locale string + }{ + {uuid.Nil, 10, "pt-PT"}, + {validID, 0, "pt-PT"}, + {validID, 201, "pt-PT"}, + {validID, 10, "fr"}, + } { + if _, err := service.NearbyLocalities(ctx, test.origin, test.radius, test.locale); !errors.Is(err, ErrInvalidRequest) { + t.Fatalf("request %#v error = %v, want ErrInvalidRequest", test, err) + } + } + if repository.calls != 0 { + t.Fatalf("repository calls = %d, want 0", repository.calls) + } +} + +func TestServiceMapsRepositoryFailures(t *testing.T) { + t.Parallel() + + repository := &recordingRepository{err: errors.New("database internal details")} + service := NewService(repository) + ctx := context.Background() + + if _, err := service.Categories(ctx, "en"); !errors.Is(err, ErrUnavailable) { + t.Fatalf("categories error = %v, want ErrUnavailable", err) + } + if _, err := service.NearbyLocalities(ctx, uuid.New(), 10, "es"); !errors.Is(err, ErrUnavailable) { + t.Fatalf("nearby error = %v, want ErrUnavailable", err) + } +} + +type recordingRepository struct { + categories []Category + languages []Language + localities []Locality + nearby []LocalityDistance + err error + calls int +} + +func (r *recordingRepository) Categories(context.Context, string) ([]Category, error) { + r.calls++ + return r.categories, r.err +} + +func (r *recordingRepository) Languages(context.Context, string) ([]Language, error) { + r.calls++ + return r.languages, r.err +} + +func (r *recordingRepository) Localities(context.Context, string) ([]Locality, error) { + r.calls++ + return r.localities, r.err +} + +func (r *recordingRepository) NearbyLocalities(context.Context, uuid.UUID, int, string) ([]LocalityDistance, error) { + r.calls++ + return r.nearby, r.err +} + +func (r *recordingRepository) ValidateProfileReferences(context.Context, ProfileReferences) error { + r.calls++ + return r.err +} diff --git a/backend/internal/reference/sql_repository.go b/backend/internal/reference/sql_repository.go new file mode 100644 index 0000000..20c557f --- /dev/null +++ b/backend/internal/reference/sql_repository.go @@ -0,0 +1,231 @@ +package reference + +import ( + "context" + "database/sql" + "errors" + + "github.com/google/uuid" +) + +type sqlRepository struct{ database *sql.DB } + +func NewSQLRepository(database *sql.DB) Repository { + return sqlRepository{database: database} +} + +func (r sqlRepository) Categories(ctx context.Context, locale string) ([]Category, error) { + if r.database == nil { + return nil, errors.New("database is nil") + } + rows, err := r.database.QueryContext(ctx, ` + select category.id, category.parent_id, category.slug, translation.name, category.sort_order + from public.service_categories category + join public.service_category_translations translation + on translation.category_id = category.id and translation.locale = $1 + join public.supported_locales locale + on locale.id = translation.locale and locale.active + left join public.service_categories parent on parent.id = category.parent_id + where category.active and (category.parent_id is null or parent.active) + order by coalesce(parent.sort_order, category.sort_order), + case when category.parent_id is null then 0 else 1 end, + category.sort_order, + category.id + `, locale) + if err != nil { + return nil, err + } + defer rows.Close() + + categories := make([]Category, 0) + for rows.Next() { + var category Category + if err := rows.Scan(&category.ID, &category.ParentID, &category.Slug, &category.Name, &category.SortOrder); err != nil { + return nil, err + } + categories = append(categories, category) + } + if err := rows.Err(); err != nil { + return nil, err + } + return categories, nil +} + +func (r sqlRepository) Languages(ctx context.Context, locale string) ([]Language, error) { + if r.database == nil { + return nil, errors.New("database is nil") + } + rows, err := r.database.QueryContext(ctx, ` + select language.id, translation.name, language.sort_order + from public.spoken_languages language + join public.spoken_language_translations translation + on translation.language_code = language.id and translation.locale = $1 + join public.supported_locales locale + on locale.id = translation.locale and locale.active + where language.active + order by language.sort_order, language.id + `, locale) + if err != nil { + return nil, err + } + defer rows.Close() + + languages := make([]Language, 0) + for rows.Next() { + var language Language + if err := rows.Scan(&language.Code, &language.Name, &language.SortOrder); err != nil { + return nil, err + } + languages = append(languages, language) + } + if err := rows.Err(); err != nil { + return nil, err + } + return languages, nil +} + +func (r sqlRepository) Localities(ctx context.Context, locale string) ([]Locality, error) { + if r.database == nil { + return nil, errors.New("database is nil") + } + rows, err := r.database.QueryContext(ctx, localitySelectSQL+` + cross join public.supported_locales locale + where locality.active and parish.active and municipality.active and district.active + and locale.id = $1 and locale.active + order by locality.name, locality.id + `, locale) + if err != nil { + return nil, err + } + defer rows.Close() + return scanLocalities(rows) +} + +func (r sqlRepository) NearbyLocalities(ctx context.Context, origin uuid.UUID, radiusKM int, locale string) ([]LocalityDistance, error) { + if r.database == nil { + return nil, errors.New("database is nil") + } + rows, err := r.database.QueryContext(ctx, ` + with origin as ( + select center from public.localities where id = $1 and active + ) + select locality.id, locality.slug, locality.name, + parish.name, municipality.name, district.name, + round(st_distance(locality.center, origin.center))::integer as distance_meters + from origin + cross join public.localities locality + join public.administrative_areas parish on parish.id = locality.parent_parish_id + join public.administrative_areas municipality on municipality.id = parish.parent_id + join public.administrative_areas district on district.id = municipality.parent_id + cross join public.supported_locales locale + where locality.active and parish.active and municipality.active and district.active + and locale.id = $3 and locale.active + and st_dwithin(locality.center, origin.center, $2 * 1000) + order by distance_meters, locality.id + `, origin, radiusKM, locale) + if err != nil { + return nil, err + } + defer rows.Close() + + values := make([]LocalityDistance, 0) + for rows.Next() { + var value LocalityDistance + if err := rows.Scan( + &value.ID, + &value.Slug, + &value.Name, + &value.ParishName, + &value.MunicipalityName, + &value.DistrictName, + &value.DistanceMeters, + ); err != nil { + return nil, err + } + values = append(values, value) + } + if err := rows.Err(); err != nil { + return nil, err + } + if len(values) == 0 { + return nil, ErrNotFound + } + return values, nil +} + +func (r sqlRepository) ValidateProfileReferences(ctx context.Context, references ProfileReferences) error { + if r.database == nil { + return errors.New("database is nil") + } + localityIDs := append([]uuid.UUID{references.PrimaryLocalityID}, references.ServiceLocalityIDs...) + seenLocalities := make(map[uuid.UUID]struct{}, len(localityIDs)) + for _, id := range localityIDs { + if id == uuid.Nil { + return ErrInvalidReference + } + if _, ok := seenLocalities[id]; ok { + continue + } + seenLocalities[id] = struct{}{} + var active bool + if err := r.database.QueryRowContext(ctx, "select active from public.localities where id = $1", id).Scan(&active); err != nil { + if errors.Is(err, sql.ErrNoRows) { + return ErrInvalidReference + } + return err + } + if !active { + return ErrInvalidReference + } + } + + seenLanguages := make(map[string]struct{}, len(references.LanguageCodes)) + for _, code := range references.LanguageCodes { + if _, ok := seenLanguages[code]; ok { + continue + } + seenLanguages[code] = struct{}{} + var active bool + if err := r.database.QueryRowContext(ctx, "select active from public.spoken_languages where id = $1", code).Scan(&active); err != nil { + if errors.Is(err, sql.ErrNoRows) { + return ErrInvalidReference + } + return err + } + if !active { + return ErrInvalidReference + } + } + return nil +} + +const localitySelectSQL = ` + select locality.id, locality.slug, locality.name, + parish.name, municipality.name, district.name + from public.localities locality + join public.administrative_areas parish on parish.id = locality.parent_parish_id + join public.administrative_areas municipality on municipality.id = parish.parent_id + join public.administrative_areas district on district.id = municipality.parent_id +` + +func scanLocalities(rows *sql.Rows) ([]Locality, error) { + localities := make([]Locality, 0) + for rows.Next() { + var locality Locality + if err := rows.Scan( + &locality.ID, + &locality.Slug, + &locality.Name, + &locality.ParishName, + &locality.MunicipalityName, + &locality.DistrictName, + ); err != nil { + return nil, err + } + localities = append(localities, locality) + } + if err := rows.Err(); err != nil { + return nil, err + } + return localities, nil +} diff --git a/backend/internal/reference/sql_repository_test.go b/backend/internal/reference/sql_repository_test.go new file mode 100644 index 0000000..b286fdc --- /dev/null +++ b/backend/internal/reference/sql_repository_test.go @@ -0,0 +1,150 @@ +package reference + +import ( + "context" + "database/sql" + "os" + "testing" + + "github.com/google/uuid" + _ "github.com/jackc/pgx/v5/stdlib" +) + +func TestSQLRepositoryReturnsCompleteLocalizedReferenceCatalog(t *testing.T) { + database := openReferenceDatabase(t) + repository := NewSQLRepository(database) + ctx := context.Background() + + categories, err := repository.Categories(ctx, "pt-PT") + if err != nil { + t.Fatalf("categories: %v", err) + } + if len(categories) != 18 { + t.Fatalf("category count = %d, want 18", len(categories)) + } + if categories[0].Slug != "home-repairs" || categories[0].ParentID != nil || categories[0].Name != "Reparações domésticas" { + t.Fatalf("first category = %#v", categories[0]) + } + seen := make(map[uuid.UUID]struct{}, len(categories)) + for _, category := range categories { + if _, exists := seen[category.ID]; exists { + t.Fatalf("duplicate category ID %s", category.ID) + } + seen[category.ID] = struct{}{} + } + + languages, err := repository.Languages(ctx, "en") + if err != nil { + t.Fatalf("languages: %v", err) + } + if len(languages) != 3 || languages[0].Code != "pt-PT" || languages[0].Name != "Portuguese" { + t.Fatalf("languages = %#v", languages) + } + + localities, err := repository.Localities(ctx, "es") + if err != nil { + t.Fatalf("localities: %v", err) + } + if len(localities) != 5 { + t.Fatalf("locality count = %d, want 5", len(localities)) + } + for _, locality := range localities { + if locality.ID == uuid.Nil || locality.Name == "" || locality.ParishName == "" || locality.MunicipalityName == "" || locality.DistrictName == "" { + t.Fatalf("incomplete locality = %#v", locality) + } + } +} + +func TestSQLRepositoryRadiusOrderingAndActiveFiltering(t *testing.T) { + database := openReferenceDatabase(t) + ctx := context.Background() + parentID := seededParishID(t, database, "050205") + ids := []uuid.UUID{ + uuid.MustParse("00000000-0000-4000-8000-000000000010"), + uuid.MustParse("00000000-0000-4000-8000-000000000011"), + uuid.MustParse("00000000-0000-4000-8000-000000000012"), + uuid.MustParse("00000000-0000-4000-8000-000000000013"), + uuid.MustParse("00000000-0000-4000-8000-000000000014"), + } + rows := []struct { + id uuid.UUID + slug string + latitude float64 + longitude float64 + active bool + }{ + {ids[0], "test-radius-origin", 39.8, -7.0, true}, + {ids[1], "test-radius-near-a", 39.8, -7.01, true}, + {ids[2], "test-radius-near-b", 39.8, -6.99, true}, + {ids[3], "test-radius-inactive", 39.8, -7.005, false}, + {ids[4], "test-radius-out", 39.8, -7.1, true}, + } + for _, row := range rows { + _, err := database.ExecContext(ctx, ` + insert into public.localities + (id, slug, name, parent_parish_id, source, source_element_id, source_version, source_retrieved_at, latitude, longitude, active) + values ($1, $2, $3, $4, 'synthetic-test', $5, '1', '2026-08-23T00:00:00Z', $6, $7, $8) + `, row.id, row.slug, row.slug, parentID, "T:"+row.slug, row.latitude, row.longitude, row.active) + if err != nil { + t.Fatalf("insert locality %s: %v", row.slug, err) + } + } + t.Cleanup(func() { + for _, id := range ids { + if _, err := database.ExecContext(ctx, "delete from public.localities where id = $1", id); err != nil { + t.Errorf("cleanup locality: %v", err) + } + } + }) + + nearby, err := NewSQLRepository(database).NearbyLocalities(ctx, ids[0], 2, "pt-PT") + if err != nil { + t.Fatalf("nearby localities: %v", err) + } + if len(nearby) != 3 { + t.Fatalf("nearby count = %d, want 3 (%#v)", len(nearby), nearby) + } + if nearby[0].ID != ids[0] || nearby[0].DistanceMeters != 0 { + t.Fatalf("origin result = %#v", nearby[0]) + } + if nearby[1].ID != ids[1] || nearby[2].ID != ids[2] { + t.Fatalf("equal-distance ordering = %#v", nearby) + } + for _, result := range nearby { + if result.ID == ids[3] || result.ID == ids[4] { + t.Fatalf("inactive/out-of-range locality included: %#v", result) + } + } + + _, err = NewSQLRepository(database).NearbyLocalities(ctx, uuid.New(), 2, "pt-PT") + if err != ErrNotFound { + t.Fatalf("missing origin error = %v, want ErrNotFound", err) + } +} + +func openReferenceDatabase(t *testing.T) *sql.DB { + t.Helper() + databaseURL := os.Getenv("TEST_DATABASE_URL") + if databaseURL == "" { + t.Skip("TEST_DATABASE_URL is required for PostgreSQL integration tests") + } + database, err := sql.Open("pgx", databaseURL) + if err != nil { + t.Fatalf("open database: %v", err) + } + t.Cleanup(func() { + if err := database.Close(); err != nil { + t.Errorf("close database: %v", err) + } + }) + return database +} + +func seededParishID(t *testing.T, database *sql.DB, code string) uuid.UUID { + t.Helper() + var id uuid.UUID + if err := database.QueryRow("select id from public.administrative_areas where source = 'caop' and external_code = $1", code).Scan(&id); err != nil { + t.Fatalf("load seeded parish: %v", err) + } + return id +} diff --git a/backend/internal/reviews/model.go b/backend/internal/reviews/model.go new file mode 100644 index 0000000..25d14bc --- /dev/null +++ b/backend/internal/reviews/model.go @@ -0,0 +1,40 @@ +package reviews + +import ( + "errors" + "github.com/google/uuid" + "time" +) + +var ( + ErrInvalid = errors.New("review invalid request") + ErrUnauthorized = errors.New("review unauthorized") + ErrForbidden = errors.New("review forbidden") + ErrConflict = errors.New("review conflict") + ErrNotFound = errors.New("review not found") + ErrUnavailable = errors.New("review unavailable") +) + +type CreateReview struct { + BookingID uuid.UUID + Rating int + Body string +} +type Review struct { + ID uuid.UUID + BookingID uuid.UUID + CustomerID uuid.UUID + ProviderID uuid.UUID + Rating int + Body string + ProviderResponse string + VerifiedBooking bool + State string + CreatedAt time.Time + UpdatedAt time.Time +} +type Aggregate struct { + ProviderID uuid.UUID + AverageRating float64 + ReviewCount int +} diff --git a/backend/internal/reviews/service.go b/backend/internal/reviews/service.go new file mode 100644 index 0000000..d3dd39e --- /dev/null +++ b/backend/internal/reviews/service.go @@ -0,0 +1,96 @@ +package reviews + +import ( + "context" + "errors" + "github.com/SourceSenseiTheRealOne/juntly/backend/internal/users" + "github.com/google/uuid" + "strings" + "unicode/utf8" +) + +type IdentityReconciler interface { + Reconcile(context.Context, users.VerifiedIdentity) (users.InternalUser, bool, error) +} +type Store interface { + Create(context.Context, uuid.UUID, CreateReview) (Review, error) + ListForProvider(context.Context, uuid.UUID) ([]Review, error) + Respond(context.Context, uuid.UUID, uuid.UUID, string) (Review, error) + Aggregate(context.Context, uuid.UUID) (Aggregate, error) +} +type Service interface { + Create(context.Context, users.VerifiedIdentity, CreateReview) (Review, error) + ListForProvider(context.Context, users.VerifiedIdentity) ([]Review, error) + Respond(context.Context, users.VerifiedIdentity, uuid.UUID, string) (Review, error) + Aggregate(context.Context, uuid.UUID) (Aggregate, error) +} +type service struct { + identities IdentityReconciler + store Store +} + +func NewService(i IdentityReconciler, s Store) Service { return service{identities: i, store: s} } +func (s service) Create(ctx context.Context, identity users.VerifiedIdentity, input CreateReview) (Review, error) { + input.Body = strings.TrimSpace(input.Body) + if input.BookingID == uuid.Nil || input.Rating < 1 || input.Rating > 5 || utf8.RuneCountInString(input.Body) < 10 || utf8.RuneCountInString(input.Body) > 2000 { + return Review{}, ErrInvalid + } + actor, err := s.actor(ctx, identity) + if err != nil { + return Review{}, err + } + v, err := s.store.Create(ctx, actor, input) + return v, normalize(err) +} +func (s service) ListForProvider(ctx context.Context, identity users.VerifiedIdentity) ([]Review, error) { + actor, err := s.actor(ctx, identity) + if err != nil { + return nil, err + } + v, err := s.store.ListForProvider(ctx, actor) + return v, normalize(err) +} +func (s service) Respond(ctx context.Context, identity users.VerifiedIdentity, id uuid.UUID, response string) (Review, error) { + response = strings.TrimSpace(response) + if id == uuid.Nil || utf8.RuneCountInString(response) < 3 || utf8.RuneCountInString(response) > 1000 { + return Review{}, ErrInvalid + } + actor, err := s.actor(ctx, identity) + if err != nil { + return Review{}, err + } + v, err := s.store.Respond(ctx, actor, id, response) + return v, normalize(err) +} +func (s service) Aggregate(ctx context.Context, provider uuid.UUID) (Aggregate, error) { + if s.store == nil || provider == uuid.Nil { + return Aggregate{}, ErrInvalid + } + v, err := s.store.Aggregate(ctx, provider) + return v, normalize(err) +} +func (s service) actor(ctx context.Context, identity users.VerifiedIdentity) (uuid.UUID, error) { + if s.identities == nil || s.store == nil { + return uuid.Nil, ErrUnavailable + } + u, _, err := s.identities.Reconcile(ctx, identity) + if err != nil { + if errors.Is(err, users.ErrInvalidIdentity) { + return uuid.Nil, ErrUnauthorized + } + return uuid.Nil, ErrUnavailable + } + if u.ID == uuid.Nil { + return uuid.Nil, ErrUnauthorized + } + return u.ID, nil +} +func normalize(err error) error { + if err == nil { + return nil + } + if errors.Is(err, ErrInvalid) || errors.Is(err, ErrUnauthorized) || errors.Is(err, ErrForbidden) || errors.Is(err, ErrConflict) || errors.Is(err, ErrNotFound) { + return err + } + return ErrUnavailable +} diff --git a/backend/internal/reviews/service_test.go b/backend/internal/reviews/service_test.go new file mode 100644 index 0000000..889e49f --- /dev/null +++ b/backend/internal/reviews/service_test.go @@ -0,0 +1,78 @@ +package reviews + +import ( + "context" + "errors" + "testing" + + "github.com/SourceSenseiTheRealOne/juntly/backend/internal/users" + "github.com/google/uuid" +) + +func TestServiceCreatesVerifiedCompletedBookingReview(t *testing.T) { + t.Parallel() + actor := uuid.New() + bookingID := uuid.New() + store := &recordingStore{review: Review{ID: uuid.New(), BookingID: bookingID, CustomerID: actor, VerifiedBooking: true}} + service := NewService(staticIdentity{user: users.InternalUser{ID: actor}}, store) + value, err := service.Create(context.Background(), users.VerifiedIdentity{Subject: "user_customer"}, CreateReview{BookingID: bookingID, Rating: 5, Body: "Trabalho muito bem executado."}) + if err != nil { + t.Fatalf("Create() error = %v", err) + } + if !value.VerifiedBooking || store.actorID != actor || store.input.Rating != 5 { + t.Fatalf("review/store = %#v/%s/%#v", value, store.actorID, store.input) + } +} +func TestServiceRejectsInvalidRatingBeforeStore(t *testing.T) { + t.Parallel() + store := &recordingStore{} + service := NewService(staticIdentity{user: users.InternalUser{ID: uuid.New()}}, store) + _, err := service.Create(context.Background(), users.VerifiedIdentity{Subject: "user_customer"}, CreateReview{BookingID: uuid.New(), Rating: 6, Body: "Texto suficientemente longo."}) + if !errors.Is(err, ErrInvalid) { + t.Fatalf("error = %v", err) + } + if store.actorID != uuid.Nil { + t.Fatal("store called") + } +} +func TestServiceAllowsProviderResponse(t *testing.T) { + t.Parallel() + actor := uuid.New() + reviewID := uuid.New() + store := &recordingStore{review: Review{ID: reviewID, ProviderResponse: "Obrigado."}} + service := NewService(staticIdentity{user: users.InternalUser{ID: actor}}, store) + value, err := service.Respond(context.Background(), users.VerifiedIdentity{Subject: "user_provider"}, reviewID, "Obrigado pelo comentário.") + if err != nil || value.ID != reviewID || store.actorID != actor { + t.Fatalf("Respond() = %#v/%v/%s", value, err, store.actorID) + } +} + +type staticIdentity struct { + user users.InternalUser + err error +} + +func (s staticIdentity) Reconcile(context.Context, users.VerifiedIdentity) (users.InternalUser, bool, error) { + return s.user, false, s.err +} + +type recordingStore struct { + review Review + actorID uuid.UUID + input CreateReview +} + +func (s *recordingStore) Create(_ context.Context, actor uuid.UUID, input CreateReview) (Review, error) { + s.actorID, s.input = actor, input + return s.review, nil +} +func (s *recordingStore) ListForProvider(context.Context, uuid.UUID) ([]Review, error) { + return nil, nil +} +func (s *recordingStore) Respond(_ context.Context, actor, reviewID uuid.UUID, response string) (Review, error) { + s.actorID = actor + return s.review, nil +} +func (s *recordingStore) Aggregate(context.Context, uuid.UUID) (Aggregate, error) { + return Aggregate{}, nil +} diff --git a/backend/internal/reviews/sql_store.go b/backend/internal/reviews/sql_store.go new file mode 100644 index 0000000..d172bc4 --- /dev/null +++ b/backend/internal/reviews/sql_store.go @@ -0,0 +1,108 @@ +package reviews + +import ( + "context" + "database/sql" + "errors" + "github.com/google/uuid" +) + +type sqlStore struct{ database *sql.DB } + +func NewSQLStore(db *sql.DB) Store { return sqlStore{database: db} } +func (s sqlStore) Create(ctx context.Context, actor uuid.UUID, input CreateReview) (Review, error) { + tx, err := s.database.BeginTx(ctx, nil) + if err != nil { + return Review{}, err + } + defer tx.Rollback() + var provider uuid.UUID + if err = tx.QueryRowContext(ctx, `select provider_internal_user_id from public.bookings where id=$1 and customer_internal_user_id=$2 and state='completed'`, input.BookingID, actor).Scan(&provider); err != nil { + if errors.Is(err, sql.ErrNoRows) { + return Review{}, ErrForbidden + } + return Review{}, err + } + var v Review + err = tx.QueryRowContext(ctx, `insert into public.reviews(booking_id,customer_internal_user_id,provider_internal_user_id,rating,body) values($1,$2,$3,$4,$5) on conflict(booking_id) do nothing returning id,booking_id,customer_internal_user_id,provider_internal_user_id,rating,body,coalesce(provider_response,''),verified_booking,state,created_at,updated_at`, input.BookingID, actor, provider, input.Rating, input.Body).Scan(reviewScan(&v)...) + if errors.Is(err, sql.ErrNoRows) { + return Review{}, ErrConflict + } + if err != nil { + return Review{}, err + } + if _, err = tx.ExecContext(ctx, `insert into public.provider_rating_aggregates(provider_internal_user_id,rating_sum,review_count) values($1,$2,1) on conflict(provider_internal_user_id) do update set rating_sum=provider_rating_aggregates.rating_sum+excluded.rating_sum,review_count=provider_rating_aggregates.review_count+1,updated_at=timezone('utc',now())`, provider, input.Rating); err != nil { + return Review{}, err + } + if err = s.notify(ctx, tx, provider, "review_received", v.ID); err != nil { + return Review{}, err + } + if err = tx.Commit(); err != nil { + return Review{}, err + } + return v, nil +} +func (s sqlStore) ListForProvider(ctx context.Context, actor uuid.UUID) ([]Review, error) { + rows, err := s.database.QueryContext(ctx, `select id,booking_id,customer_internal_user_id,provider_internal_user_id,rating,body,coalesce(provider_response,''),verified_booking,state,created_at,updated_at from public.reviews where provider_internal_user_id=$1 order by created_at desc,id`, actor) + if err != nil { + return nil, err + } + defer rows.Close() + values := []Review{} + for rows.Next() { + var v Review + if err := rows.Scan(reviewScan(&v)...); err != nil { + return nil, err + } + values = append(values, v) + } + return values, rows.Err() +} +func (s sqlStore) Respond(ctx context.Context, actor, id uuid.UUID, response string) (Review, error) { + tx, err := s.database.BeginTx(ctx, nil) + if err != nil { + return Review{}, err + } + defer tx.Rollback() + var v Review + err = tx.QueryRowContext(ctx, `update public.reviews set provider_response=$1,updated_at=timezone('utc',now()) where id=$2 and provider_internal_user_id=$3 and provider_response is null returning id,booking_id,customer_internal_user_id,provider_internal_user_id,rating,body,provider_response,verified_booking,state,created_at,updated_at`, response, id, actor).Scan(reviewScan(&v)...) + if errors.Is(err, sql.ErrNoRows) { + return Review{}, ErrForbidden + } + if err != nil { + return Review{}, err + } + if err = s.notify(ctx, tx, v.CustomerID, "review_response", v.ID); err != nil { + return Review{}, err + } + if err = tx.Commit(); err != nil { + return Review{}, err + } + return v, nil +} +func (s sqlStore) Aggregate(ctx context.Context, provider uuid.UUID) (Aggregate, error) { + var sum int64 + var count int + err := s.database.QueryRowContext(ctx, `select coalesce(rating_sum,0),coalesce(review_count,0) from public.provider_rating_aggregates where provider_internal_user_id=$1`, provider).Scan(&sum, &count) + if errors.Is(err, sql.ErrNoRows) { + return Aggregate{ProviderID: provider}, nil + } + if err != nil { + return Aggregate{}, err + } + average := 0.0 + if count > 0 { + average = float64(sum) / float64(count) + } + return Aggregate{ProviderID: provider, AverageRating: average, ReviewCount: count}, nil +} +func (s sqlStore) notify(ctx context.Context, tx *sql.Tx, recipient uuid.UUID, kind string, resource uuid.UUID) error { + if _, err := tx.ExecContext(ctx, `insert into public.notifications(recipient_internal_user_id,kind,resource_id,in_app_visible) select $1,$2,$3,coalesce(p.in_app_enabled,true) from (select 1)x left join public.notification_preferences p on p.internal_user_id=$1 where coalesce(p.in_app_enabled or p.email_enabled,true) on conflict do nothing`, recipient, kind, resource); err != nil { + return err + } + _, err := tx.ExecContext(ctx, `insert into public.notification_email_outbox(notification_id,recipient_internal_user_id) select n.id,n.recipient_internal_user_id from public.notifications n left join public.notification_preferences p on p.internal_user_id=n.recipient_internal_user_id where n.recipient_internal_user_id=$1 and n.kind=$2 and n.resource_id=$3 and coalesce(p.email_enabled,true) on conflict(notification_id) do nothing`, recipient, kind, resource) + return err +} +func reviewScan(v *Review) []any { + return []any{&v.ID, &v.BookingID, &v.CustomerID, &v.ProviderID, &v.Rating, &v.Body, &v.ProviderResponse, &v.VerifiedBooking, &v.State, &v.CreatedAt, &v.UpdatedAt} +} diff --git a/backend/internal/users/ent_repository.go b/backend/internal/users/ent_repository.go new file mode 100644 index 0000000..5c2c6c0 --- /dev/null +++ b/backend/internal/users/ent_repository.go @@ -0,0 +1,55 @@ +package users + +import ( + "context" + "errors" + "time" + + "github.com/SourceSenseiTheRealOne/juntly/backend/ent" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/internaluser" +) + +type entRepository struct { + client *ent.Client +} + +func NewEntRepository(client *ent.Client) Repository { + return entRepository{client: client} +} + +func (r entRepository) FindBySubject(ctx context.Context, subject string) (InternalUser, bool, error) { + if r.client == nil { + return InternalUser{}, false, errors.New("Ent client is nil") + } + + entity, err := r.client.InternalUser.Query().Where(internaluser.ClerkSubjectEQ(subject)).Only(ctx) + if err != nil { + if ent.IsNotFound(err) { + return InternalUser{}, false, nil + } + return InternalUser{}, false, err + } + return internalUserFromEnt(entity), true, nil +} + +func (r entRepository) Create(ctx context.Context, subject string) (InternalUser, error) { + if r.client == nil { + return InternalUser{}, errors.New("Ent client is nil") + } + + entity, err := r.client.InternalUser.Create().SetClerkSubject(subject).Save(ctx) + if err != nil { + if ent.IsConstraintError(err) { + return InternalUser{}, ErrSubjectConflict + } + return InternalUser{}, err + } + return internalUserFromEnt(entity), nil +} + +func internalUserFromEnt(entity *ent.InternalUser) InternalUser { + return InternalUser{ + ID: entity.ID, + CreatedAt: entity.CreatedAt.UTC().Truncate(time.Microsecond), + } +} diff --git a/backend/internal/users/ent_repository_test.go b/backend/internal/users/ent_repository_test.go new file mode 100644 index 0000000..14825ee --- /dev/null +++ b/backend/internal/users/ent_repository_test.go @@ -0,0 +1,135 @@ +package users + +import ( + "context" + "database/sql" + "os" + "sync" + "testing" + + "github.com/SourceSenseiTheRealOne/juntly/backend/ent" + "github.com/SourceSenseiTheRealOne/juntly/backend/ent/internaluser" + "github.com/google/uuid" + _ "github.com/jackc/pgx/v5/stdlib" + "entgo.io/ent/dialect" + entsql "entgo.io/ent/dialect/sql" +) + +func TestEntRepositoryFindsAndCreatesByExactSubject(t *testing.T) { + client := openIntegrationClient(t) + repository := NewEntRepository(client) + ctx := context.Background() + subject := "test_" + uuid.NewString() + + created, err := repository.Create(ctx, subject) + if err != nil { + t.Fatalf("create: %v", err) + } + t.Cleanup(func() { + if err := client.InternalUser.DeleteOneID(created.ID).Exec(ctx); err != nil { + t.Errorf("cleanup internal user: %v", err) + } + }) + + found, exists, err := repository.FindBySubject(ctx, subject) + if err != nil { + t.Fatalf("find: %v", err) + } + if !exists { + t.Fatal("exists = false, want true") + } + if found != created { + t.Fatalf("found = %#v, want %#v", found, created) + } + + _, exists, err = repository.FindBySubject(ctx, subject+"_other") + if err != nil { + t.Fatalf("find missing: %v", err) + } + if exists { + t.Fatal("exists = true for a different subject") + } +} + +func TestReconcileConcurrentSameSubjectProducesOneStableRow(t *testing.T) { + client := openIntegrationClient(t) + repository := NewEntRepository(client) + service := NewService(repository) + ctx := context.Background() + subject := "test_" + uuid.NewString() + + const attempts = 8 + start := make(chan struct{}) + results := make(chan InternalUser, attempts) + errs := make(chan error, attempts) + var workers sync.WaitGroup + for range attempts { + workers.Add(1) + go func() { + defer workers.Done() + <-start + user, _, err := service.Reconcile(ctx, VerifiedIdentity{Subject: subject}) + if err != nil { + errs <- err + return + } + results <- user + }() + } + close(start) + workers.Wait() + close(results) + close(errs) + + for err := range errs { + t.Fatalf("reconcile: %v", err) + } + + var stable InternalUser + for user := range results { + if stable.ID == uuid.Nil { + stable = user + continue + } + if user != stable { + t.Fatalf("user = %#v, want stable %#v", user, stable) + } + } + if stable.ID == uuid.Nil { + t.Fatal("no reconciliation result") + } + t.Cleanup(func() { + if _, err := client.InternalUser.Delete().Where(internaluser.IDEQ(stable.ID)).Exec(ctx); err != nil { + t.Errorf("cleanup internal user: %v", err) + } + }) + + count, err := client.InternalUser.Query().Where(internaluser.ClerkSubjectEQ(subject)).Count(ctx) + if err != nil { + t.Fatalf("count subject mappings: %v", err) + } + if count != 1 { + t.Fatalf("count = %d, want 1", count) + } +} + +func openIntegrationClient(t *testing.T) *ent.Client { + t.Helper() + + databaseURL := os.Getenv("TEST_DATABASE_URL") + if databaseURL == "" { + t.Skip("TEST_DATABASE_URL is required for PostgreSQL integration tests") + } + + database, err := sql.Open("pgx", databaseURL) + if err != nil { + t.Fatalf("open pgx database: %v", err) + } + client := ent.NewClient(ent.Driver(entsql.OpenDB(dialect.Postgres, database))) + t.Cleanup(func() { + if err := client.Close(); err != nil { + t.Errorf("close Ent client: %v", err) + } + }) + return client +} diff --git a/backend/internal/users/migration_contract_test.go b/backend/internal/users/migration_contract_test.go new file mode 100644 index 0000000..3c11198 --- /dev/null +++ b/backend/internal/users/migration_contract_test.go @@ -0,0 +1,109 @@ +package users_test + +import ( + "os" + "path/filepath" + "strings" + "testing" +) + +func TestCreateInternalUsersMigrationContract(t *testing.T) { + t.Parallel() + migration := readMigration(t, "_create_internal_users.sql") + for _, requirement := range []string{ + "create table public.internal_users", "id uuid primary key", "clerk_subject text not null unique", + "created_at timestamptz not null", "updated_at timestamptz not null", "char_length(clerk_subject) between 1 and 255", + } { + if !strings.Contains(migration, requirement) { + t.Errorf("migration does not contain %q", requirement) + } + } +} + +func TestCreateUserAccountsMigrationContract(t *testing.T) { + t.Parallel() + migration := readMigration(t, "_create_user_accounts.sql") + for _, requirement := range []string{ + "create table public.user_accounts", "internal_user_id uuid primary key", + "references public.internal_users(id) on delete cascade", "provider_enabled boolean not null default false", + "onboarding_completed_at timestamptz not null", "created_at timestamptz not null", "updated_at timestamptz not null", + } { + if !strings.Contains(migration, requirement) { + t.Errorf("migration does not contain %q", requirement) + } + } + for _, prohibited := range []string{"clerk_subject", "email", "phone", "display_name", "profile", "role", "contact"} { + if strings.Contains(migration, prohibited) { + t.Errorf("migration must not include %q", prohibited) + } + } +} + +func TestCreateTaxonomyLocationsProviderProfilesMigrationContract(t *testing.T) { + t.Parallel() + migration := readMigration(t, "_create_taxonomy_locations_provider_profiles.sql") + for _, requirement := range []string{ + "create extension if not exists postgis", "create table public.supported_locales", "create table public.service_categories", + "create table public.service_category_translations", "primary key (category_id, locale)", "create table public.spoken_languages", + "create table public.spoken_language_translations", "primary key (language_code, locale)", "create table public.administrative_areas", + "create table public.localities", "center geography(point, 4326) generated always as", "st_setsrid(st_makepoint(longitude, latitude), 4326)::geography", + "create table public.provider_profiles", "internal_user_id uuid primary key", "references public.user_accounts(internal_user_id) on delete cascade", + "provider_type in ('individual', 'professional', 'business')", "max_travel_distance_km between 0 and 200", + "travels_to_customer or receives_customer or remote_services", "create table public.provider_service_localities", + "primary key (internal_user_id, locality_id)", "create table public.provider_spoken_languages", "primary key (internal_user_id, language_code)", + "'050205'", "'050510'", "'050518'", "'050520'", "'050521'", "'r5396187'", "'r5395738'", "'n371426674'", "'r5431477'", "'n440173641'", "'home-repairs'", "'computer-repair'", + } { + if !strings.Contains(migration, requirement) { + t.Errorf("migration does not contain %q", requirement) + } + } + for _, prohibited := range []string{"clerk_subject", "email", "phone", "whatsapp", "exact_address", "payment", "identity_document"} { + if strings.Contains(migration, prohibited) { + t.Errorf("migration must not include %q", prohibited) + } + } +} + +func TestCreateListingModerationMigrationContract(t *testing.T) { + t.Parallel() + migration := readMigration(t, "_create_listing_moderation.sql") + for _, requirement := range []string{ + "create table public.platform_roles", "id uuid primary key", "unique (internal_user_id, role)", "role in ('moderator')", + "create table public.listings", "references public.provider_profiles(internal_user_id) on delete cascade", + "references public.service_categories(id) on delete restrict", "references public.localities(id) on delete restrict", + "price_type in ('fixed', 'hourly', 'daily', 'quote', 'negotiable')", "state in ('draft', 'pending_review', 'active', 'rejected', 'paused', 'archived')", + "revision integer not null default 1", "create index listings_owner_state_updated_idx", "create table public.listing_events", + "event_type in ('created', 'updated', 'submitted', 'approved', 'rejected', 'paused', 'archived')", "create table public.listing_media", + "state in ('pending_upload', 'ready', 'deleted')", "unique (listing_id, ordinal)", + } { + if !strings.Contains(migration, requirement) { + t.Errorf("migration does not contain %q", requirement) + } + } + for _, prohibited := range []string{"clerk_subject", "email", "phone", "whatsapp", "exact_address", "storage_secret", "access_key", "bucket"} { + if strings.Contains(migration, prohibited) { + t.Errorf("migration must not include %q", prohibited) + } + } +} + +func readMigration(t *testing.T, suffix string) string { + t.Helper() + directory := filepath.Join("..", "..", "..", "supabase", "migrations") + entries, err := os.ReadDir(directory) + if err != nil { + t.Fatalf("read migration directory: %v", err) + } + for _, entry := range entries { + if entry.IsDir() || !strings.HasSuffix(entry.Name(), suffix) { + continue + } + contents, err := os.ReadFile(filepath.Join(directory, entry.Name())) + if err != nil { + t.Fatalf("read migration: %v", err) + } + return strings.ToLower(string(contents)) + } + t.Fatalf("migration with suffix %q was not found", suffix) + return "" +} diff --git a/backend/internal/users/model.go b/backend/internal/users/model.go new file mode 100644 index 0000000..bb42ccd --- /dev/null +++ b/backend/internal/users/model.go @@ -0,0 +1,18 @@ +package users + +import ( + "time" + + "github.com/google/uuid" +) + +const maxSubjectLength = 255 + +type VerifiedIdentity struct { + Subject string +} + +type InternalUser struct { + ID uuid.UUID + CreatedAt time.Time +} diff --git a/backend/internal/users/repository.go b/backend/internal/users/repository.go new file mode 100644 index 0000000..9a5fba9 --- /dev/null +++ b/backend/internal/users/repository.go @@ -0,0 +1,16 @@ +package users + +import ( + "context" + "errors" +) + +var ( + ErrSubjectConflict = errors.New("internal user subject conflict") + ErrUnavailable = errors.New("internal user persistence unavailable") +) + +type Repository interface { + FindBySubject(context.Context, string) (InternalUser, bool, error) + Create(context.Context, string) (InternalUser, error) +} diff --git a/backend/internal/users/service.go b/backend/internal/users/service.go new file mode 100644 index 0000000..dcc9fb8 --- /dev/null +++ b/backend/internal/users/service.go @@ -0,0 +1,57 @@ +package users + +import ( + "context" + "errors" + "strings" + "unicode/utf8" +) + +var ErrInvalidIdentity = errors.New("invalid verified identity") + +type Service interface { + Reconcile(context.Context, VerifiedIdentity) (InternalUser, bool, error) +} + +type service struct { + repository Repository +} + +func NewService(repository Repository) Service { + return service{repository: repository} +} + +func (s service) Reconcile(ctx context.Context, identity VerifiedIdentity) (InternalUser, bool, error) { + if !validSubject(identity.Subject) { + return InternalUser{}, false, ErrInvalidIdentity + } + if s.repository == nil { + return InternalUser{}, false, ErrUnavailable + } + + existing, found, err := s.repository.FindBySubject(ctx, identity.Subject) + if err != nil { + return InternalUser{}, false, ErrUnavailable + } + if found { + return existing, false, nil + } + + created, err := s.repository.Create(ctx, identity.Subject) + if err == nil { + return created, true, nil + } + if !errors.Is(err, ErrSubjectConflict) { + return InternalUser{}, false, ErrUnavailable + } + + winner, found, err := s.repository.FindBySubject(ctx, identity.Subject) + if err != nil || !found { + return InternalUser{}, false, ErrUnavailable + } + return winner, false, nil +} + +func validSubject(subject string) bool { + return strings.TrimSpace(subject) != "" && utf8.RuneCountInString(subject) <= maxSubjectLength +} diff --git a/backend/internal/users/service_test.go b/backend/internal/users/service_test.go new file mode 100644 index 0000000..052c126 --- /dev/null +++ b/backend/internal/users/service_test.go @@ -0,0 +1,147 @@ +package users + +import ( + "context" + "errors" + "testing" + "time" + + "github.com/google/uuid" +) + +func TestReconcileRejectsInvalidSubjectBeforeRepositoryAccess(t *testing.T) { + t.Parallel() + + repository := &recordingRepository{} + _, _, err := NewService(repository).Reconcile(context.Background(), VerifiedIdentity{}) + + if !errors.Is(err, ErrInvalidIdentity) { + t.Fatalf("error = %v, want ErrInvalidIdentity", err) + } + if repository.findCalls != 0 || repository.createCalls != 0 { + t.Fatalf("repository calls = find:%d create:%d, want none", repository.findCalls, repository.createCalls) + } +} + +func TestReconcileCreatesFirstMapping(t *testing.T) { + t.Parallel() + + created := InternalUser{ + ID: uuid.MustParse("7b7b7d7e-38f9-4f0c-8a10-0fce9cf6f82b"), + CreatedAt: time.Date(2026, 8, 21, 12, 0, 0, 0, time.UTC), + } + repository := &recordingRepository{createResult: created} + + user, wasCreated, err := NewService(repository).Reconcile(context.Background(), VerifiedIdentity{Subject: "user_123"}) + + if err != nil { + t.Fatalf("reconcile: %v", err) + } + if !wasCreated { + t.Fatal("wasCreated = false, want true") + } + if user != created { + t.Fatalf("user = %#v, want %#v", user, created) + } + if repository.findCalls != 1 || repository.createCalls != 1 { + t.Fatalf("repository calls = find:%d create:%d, want 1:1", repository.findCalls, repository.createCalls) + } +} + +func TestReconcileReturnsExistingMappingWithoutCreate(t *testing.T) { + t.Parallel() + + existing := InternalUser{ + ID: uuid.MustParse("7b7b7d7e-38f9-4f0c-8a10-0fce9cf6f82b"), + CreatedAt: time.Date(2026, 8, 21, 12, 0, 0, 0, time.UTC), + } + repository := &recordingRepository{findResults: []findResult{{user: existing, found: true}}} + + user, wasCreated, err := NewService(repository).Reconcile(context.Background(), VerifiedIdentity{Subject: "user_123"}) + + if err != nil { + t.Fatalf("reconcile: %v", err) + } + if wasCreated { + t.Fatal("wasCreated = true, want false") + } + if user != existing { + t.Fatalf("user = %#v, want %#v", user, existing) + } + if repository.findCalls != 1 || repository.createCalls != 0 { + t.Fatalf("repository calls = find:%d create:%d, want 1:0", repository.findCalls, repository.createCalls) + } +} + +func TestReconcileReloadsWinnerAfterUniqueConflict(t *testing.T) { + t.Parallel() + + winner := InternalUser{ + ID: uuid.MustParse("7b7b7d7e-38f9-4f0c-8a10-0fce9cf6f82b"), + CreatedAt: time.Date(2026, 8, 21, 12, 0, 0, 0, time.UTC), + } + repository := &recordingRepository{ + findResults: []findResult{{}, {user: winner, found: true}}, + createErr: ErrSubjectConflict, + } + + user, wasCreated, err := NewService(repository).Reconcile(context.Background(), VerifiedIdentity{Subject: "user_123"}) + + if err != nil { + t.Fatalf("reconcile: %v", err) + } + if wasCreated { + t.Fatal("wasCreated = true, want false") + } + if user != winner { + t.Fatalf("user = %#v, want %#v", user, winner) + } + if repository.findCalls != 2 || repository.createCalls != 1 { + t.Fatalf("repository calls = find:%d create:%d, want 2:1", repository.findCalls, repository.createCalls) + } +} + +func TestReconcileReturnsControlledFailureForRepositoryError(t *testing.T) { + t.Parallel() + + repository := &recordingRepository{findResults: []findResult{{err: errors.New("database unavailable")}}} + + _, _, err := NewService(repository).Reconcile(context.Background(), VerifiedIdentity{Subject: "user_123"}) + + if !errors.Is(err, ErrUnavailable) { + t.Fatalf("error = %v, want ErrUnavailable", err) + } + if repository.createCalls != 0 { + t.Fatalf("create calls = %d, want 0", repository.createCalls) + } +} + +type findResult struct { + user InternalUser + found bool + err error +} + +type recordingRepository struct { + findResults []findResult + createResult InternalUser + createErr error + findCalls int + createCalls int +} + +func (r *recordingRepository) FindBySubject(_ context.Context, _ string) (InternalUser, bool, error) { + r.findCalls++ + if len(r.findResults) == 0 { + return InternalUser{}, false, nil + } + + result := r.findResults[0] + r.findResults = r.findResults[1:] + return result.user, result.found, result.err +} + +func (r *recordingRepository) Create(_ context.Context, _ string) (InternalUser, error) { + r.createCalls++ + return r.createResult, r.createErr +} diff --git a/compose.production.yaml b/compose.production.yaml new file mode 100644 index 0000000..1a8d714 --- /dev/null +++ b/compose.production.yaml @@ -0,0 +1,56 @@ +services: + api: + image: ${JUNTLY_API_IMAGE:?Set JUNTLY_API_IMAGE to an immutable image digest} + restart: unless-stopped + read_only: true + tmpfs: + - /tmp:size=64m,mode=1777 + security_opt: + - no-new-privileges:true + environment: + JUNTLY_API_ADDR: :8080 + DATABASE_URL: ${DATABASE_URL:?Set DATABASE_URL} + CLERK_SECRET_KEY: ${CLERK_SECRET_KEY:?Set CLERK_SECRET_KEY} + CLERK_JWT_KEY: ${CLERK_JWT_KEY:-} + CLERK_AUTHORIZED_PARTIES: ${CLERK_AUTHORIZED_PARTIES:?Set CLERK_AUTHORIZED_PARTIES} + CLERK_CLOCK_SKEW: ${CLERK_CLOCK_SKEW:-} + JUNTLY_CONTACT_ENCRYPTION_KEY: ${JUNTLY_CONTACT_ENCRYPTION_KEY:?Set JUNTLY_CONTACT_ENCRYPTION_KEY} + STRIPE_SECRET_KEY: ${STRIPE_SECRET_KEY:?Set a rotated STRIPE_SECRET_KEY} + STRIPE_WEBHOOK_SECRET: ${STRIPE_WEBHOOK_SECRET:?Set STRIPE_WEBHOOK_SECRET} + JUNTLY_PUBLIC_ORIGIN: ${JUNTLY_PUBLIC_ORIGIN:?Set the canonical HTTPS frontend origin} + JUNTLY_PLATFORM_FEE_BPS: ${JUNTLY_PLATFORM_FEE_BPS:?Set the reviewed platform fee in basis points} + healthcheck: + test: ["CMD-SHELL", "wget -q -O /dev/null http://127.0.0.1:8080/api/v1/ready || exit 1"] + interval: 15s + timeout: 3s + retries: 5 + start_period: 10s + networks: [private] + frontend: + image: ${JUNTLY_FRONTEND_IMAGE:?Set JUNTLY_FRONTEND_IMAGE to an immutable image digest} + restart: unless-stopped + read_only: true + tmpfs: + - /tmp:size=64m,mode=1777 + - /app/.next/cache:size=128m,mode=0755,uid=1000,gid=1000 + security_opt: + - no-new-privileges:true + environment: + JUNTLY_API_ORIGIN: http://api:8080 + NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY: ${NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY:?Set NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY} + CLERK_SECRET_KEY: ${CLERK_SECRET_KEY:?Set CLERK_SECRET_KEY} + depends_on: + api: + condition: service_healthy + ports: + - "${JUNTLY_FRONTEND_PORT:-4200}:3000" + healthcheck: + test: ["CMD-SHELL", "wget -q -O /dev/null http://127.0.0.1:3000/pt-PT || exit 1"] + interval: 15s + timeout: 3s + retries: 5 + start_period: 15s + networks: [private] +networks: + private: + internal: false diff --git a/compose.yaml b/compose.yaml new file mode 100644 index 0000000..cf1a2b9 --- /dev/null +++ b/compose.yaml @@ -0,0 +1,40 @@ +services: + api: + build: + context: ./backend + environment: + JUNTLY_API_ADDR: :8080 + DATABASE_URL: ${DATABASE_URL:-} + CLERK_SECRET_KEY: ${CLERK_SECRET_KEY:-} + CLERK_JWT_KEY: ${CLERK_JWT_KEY:-} + CLERK_AUTHORIZED_PARTIES: ${CLERK_AUTHORIZED_PARTIES:-http://localhost:4200} + CLERK_CLOCK_SKEW: ${CLERK_CLOCK_SKEW:-} + JUNTLY_CONTACT_ENCRYPTION_KEY: ${JUNTLY_CONTACT_ENCRYPTION_KEY:-} + STRIPE_SECRET_KEY: ${STRIPE_SECRET_KEY:-} + STRIPE_WEBHOOK_SECRET: ${STRIPE_WEBHOOK_SECRET:-} + STRIPE_API_BASE: ${STRIPE_API_BASE:-} + JUNTLY_PUBLIC_ORIGIN: ${JUNTLY_PUBLIC_ORIGIN:-} + JUNTLY_PLATFORM_FEE_BPS: ${JUNTLY_PLATFORM_FEE_BPS:-} + healthcheck: + test: ["CMD-SHELL", "wget -q -O /dev/null http://127.0.0.1:8080/api/v1/ready || exit 1"] + interval: 5s + timeout: 3s + retries: 10 + start_period: 2s + + frontend: + build: + context: ./frontend + environment: + JUNTLY_API_ORIGIN: http://api:8080 + depends_on: + api: + condition: service_healthy + ports: + - "4200:3000" + healthcheck: + test: ["CMD-SHELL", "wget -q -O /dev/null http://127.0.0.1:3000/pt-PT || exit 1"] + interval: 5s + timeout: 3s + retries: 10 + start_period: 5s diff --git a/context/architecture.md b/context/architecture.md index 4e4d557..ef3e9e6 100644 --- a/context/architecture.md +++ b/context/architecture.md @@ -10,7 +10,7 @@ juntly/ └── supabase/ project-owned PostgreSQL/PostGIS configuration (later slice) ``` -Only `frontend/` is created during the initial bootstrap. Empty future-runtime directories are prohibited. +The initial bootstrap created `frontend/`. The Clerk frontend identity foundation is implemented in that application, and the health-tracer foundation adds `backend/` plus the versioned OpenAPI contract because both now contain working code. `supabase/` remains absent until its approved persistence slice; empty future-runtime directories remain prohibited. ## Approved request path @@ -41,7 +41,7 @@ Future modules use domain → application → ports → adapters dependency dire ## Authentication and authorization -Clerk owns primary email/password identity and session lifecycle. The BFF verifies Clerk sessions. Go maps each verified Clerk subject uniquely to an opaque internal user and enforces platform role, provider/customer domain role, entitlement, ownership, resource membership, and administrative policy. UI visibility is presentation only. +Clerk owns primary email/password identity and session lifecycle. The frontend provides localized Clerk entry routes and resource-local server session enforcement; UI visibility is presentation only. Local browser-facing Next.js routes use the canonical `localhost:4200` origin, because mixing loopback aliases can turn Clerk continuation rewrites into recursive external proxies. Once the API parent is integrated, Go will map each verified Clerk subject uniquely to an opaque internal user and enforce platform role, provider/customer domain role, entitlement, ownership, resource membership, and administrative policy. A real authenticated browser session remains a separate verification gate. ## Data and integration ownership @@ -69,4 +69,9 @@ Local development uses a child-owned Supabase stack for PostgreSQL/PostGIS and m ## Vertical tracer for the next foundation slice -The smallest full-stack proof will cross localized Next.js UI/shared generated client → same-origin BFF → Go `/api/v1/health` transport → framework-independent application service → response mapping. It must pass tests/builds, run in containers, and return matching correlation evidence. The current frontend-only bootstrap does not claim this tracer exists. +The implemented smallest full-stack proof crosses a localized Next.js client +island → same-origin BFF → generated OpenAPI client → Go `/api/v1/health` +transport → framework-independent application service → response mapping. It +returns one matching `X-Request-ID`/body correlation value, fails closed with a +privacy-safe BFF error, and runs locally in frontend/API containers. It does +not claim any Clerk, persistence, marketplace, or payment flow exists. diff --git a/context/decisions.md b/context/decisions.md index 06e3821..6f83dab 100644 --- a/context/decisions.md +++ b/context/decisions.md @@ -73,3 +73,17 @@ - Decision: The first repository delivery contains durable context and a verified localized Next.js shell only. - Alternatives: complete Go/OpenAPI/Docker/Supabase tracer in the initial commit. - Consequences: matches the requested starting scope. Documentation and reports must not call the full-stack foundation complete until the later vertical tracer exists. + +## ADR-011: Vila user-facing brand with stable technical identifiers + +- Status: accepted +- Date: 2026-09-02 +- Decision: Present the marketplace publicly as Vila and use `https://somosvila.com` as its canonical future public origin. Preserve Juntly repository, API, environment-variable, database, migration, and package identifiers unless a separate infrastructure migration is approved. +- Consequences: customer-facing copy, metadata, and legal surfaces use Vila while deployed technical contracts remain backwards-compatible. + +## ADR-012: Advance protected Stripe marketplace payments + +- Status: accepted +- Date: 2026-09-02 +- Decision: Implement optional protected payments now through Stripe-hosted Checkout and Connect destination charges, with server-owned EUR amounts and commission, durable payment/refund/dispute records, signed idempotent webhooks, and hosted provider onboarding. Cards and MB WAY are requested when eligible; external arrangements remain allowed and commission-free. +- Consequences: payment routes fail closed without complete server configuration and a payout-ready provider. Production activation remains blocked on rotated secrets, production Clerk, Connect and payment-method enablement, legal operator details, test-mode transaction/refund/dispute evidence, monitoring, and explicit deployment approval. No custom escrow or browser-owned financial authority is introduced. diff --git a/context/product.md b/context/product.md index ff7fd55..068b33b 100644 --- a/context/product.md +++ b/context/product.md @@ -36,7 +36,7 @@ Initial focus: Zebreira, Idanha-a-Nova, Penha Garcia, Monsanto, Castelo Branco, ## Explicitly deferred until after initial validation -Full protected payments/payouts, MB WAY-compatible payments, advanced verification and analytics, saved searches, web push, dispute automation, institutional dashboards, native mobile apps, and AI-assisted matching. Payment/provider abstractions may be designed earlier, but secondary features must not delay discovery, direct contact, chat, and quotations. +Advanced verification and analytics, saved searches, web push, institutional dashboards, native mobile apps, and AI-assisted matching remain deferred. SourceSensei explicitly advanced protected Stripe Checkout/Connect payments, MB WAY-compatible checkout, durable refunds, and dispute synchronization into the current implementation scope. These capabilities remain disabled until production identity, legal, Stripe, webhook, monitoring, and live test-mode activation gates are satisfied. ## Non-functional requirements diff --git a/context/stack.md b/context/stack.md index 8f06f40..00c16c3 100644 --- a/context/stack.md +++ b/context/stack.md @@ -33,4 +33,4 @@ ## Current implementation boundary -The bootstrap implements context and the Next.js frontend shell only. Go, OpenAPI generation, BFF proxying, Clerk, Supabase, Redis, object storage, background workers, payments, and Docker runtime topology are not yet implemented and must not be represented as working. +The foundation now implements the localized Next.js shell, Clerk frontend identity/session foundation, generated OpenAPI TypeScript client, same-origin health BFF, narrow Go health API, and two-container Docker proof. Its local tests, typecheck, production build, audit, canonical-origin dynamic routes, signed-out redirect, and health tracer pass. A real authenticated browser session remains separate evidence requiring a test user. Durable Go internal-user mapping, Supabase, Redis, object storage, background workers, marketplace flows, payments, and production deployment are not yet implemented and must not be represented as working. diff --git a/context/ui.md b/context/ui.md index affe164..809c055 100644 --- a/context/ui.md +++ b/context/ui.md @@ -11,7 +11,24 @@ Juntly must feel trustworthy, local, clear, and welcoming rather than technical or corporate. Mobile use and slow rural connections are primary constraints. The interface uses plain pt-PT language, short forms, clear confirmations, helpful empty/error states, and visible distinctions between individuals, verified professionals, businesses, organic results, and promotions. -The bootstrap uses neutral, high-contrast foundation tokens only. Final brand visual direction requires a later approved design slice; the shell must not freeze an unreviewed final identity. +## Approved marketplace visual direction + +The 2026-08-25 marketplace redesign is approved. It translates the reviewed +browse-screen reference into Juntly-owned presentation rules without copying +the reference's brand, content, imagery, or assets: + +- off-white canvas, cool mist control surfaces, white cards, blue-gray borders, + and dark plum-charcoal ink; +- a shared marketplace header, prominent discovery toolbar, rounded controls, + and content-first editorial listing cards; +- a wide desktop discovery grid that becomes one readable column on narrow + screens; +- semantic `market-*` utilities for page, container, panel, card, control, + button, and chip treatments. + +The redesign remains presentation-only. It does not change locale routing, +same-origin BFF behavior, authorization, privacy boundaries, contacts, or API +contracts. ## Design-token baseline @@ -21,6 +38,10 @@ The bootstrap uses neutral, high-contrast foundation tokens only. Final brand vi - Restrained radii/shadows and minimal animation. - Motion never carries essential information and respects `prefers-reduced-motion`. +## 2026-09-01 modernization + +The approved marketplace language is now applied consistently across public, authenticated, moderation, and administration surfaces. The system retains the off-white canvas, cool-mist controls, plum-charcoal ink, and one berry accent while strengthening contrast, typography, spacing, responsive hierarchy, focus treatment, dark-mode parity, alerts, empty states, and form grouping. The landing page uses real craft photography instead of simulated product UI. Business logic, routes, field names, auth, privacy, and API contracts remain unchanged. + ## Component architecture - Primitive: Button, Link, Input, Textarea, Select, Checkbox, Dialog, Menu, Field, Badge. diff --git a/docs/operations/deployment-recovery.md b/docs/operations/deployment-recovery.md new file mode 100644 index 0000000..17a6644 --- /dev/null +++ b/docs/operations/deployment-recovery.md @@ -0,0 +1,56 @@ +# Deployment and recovery runbook + +## Release inputs + +Deploy immutable digest-pinned API and frontend images through `compose.production.yaml`. Supply runtime values through the deployment platform's secret store. Never commit an environment file. + +Required values are `JUNTLY_API_IMAGE`, `JUNTLY_FRONTEND_IMAGE`, `DATABASE_URL`, production Clerk keys and authorized parties, `JUNTLY_CONTACT_ENCRYPTION_KEY`, a rotated `STRIPE_SECRET_KEY`, the Dashboard-created `STRIPE_WEBHOOK_SECRET`, canonical HTTPS `JUNTLY_PUBLIC_ORIGIN`, reviewed `JUNTLY_PLATFORM_FEE_BPS`, and the public frontend port. Never reuse a credential pasted into chat, an issue, CI logs, or source control. + +The canonical future public origin is `https://somosvila.com`. Name the Stripe event destination `Vila Production Payments` and configure its endpoint as `https://somosvila.com/api/v1/payments/webhooks/stripe`. Subscribe only to the allowlisted payment and connected-account events documented below; do not subscribe to SetupIntent lifecycle events. + +Production Clerk must authorize `https://somosvila.com`; local development must authorize `http://localhost:4200`. Google OAuth is configured in Clerk for both origins. Keep the OAuth client secret and Clerk secret only in their provider/deployment secret stores. + +Use the manually dispatched `Publish immutable images` GitHub workflow to build a reviewed ref. It publishes `linux/amd64` API and frontend images to GHCR with the resolved full commit SHA as the only tag, disables mutable provenance/SBOM side artifacts, and records each registry digest in the workflow summary. Supply the resulting `image@sha256:...` references to `compose.production.yaml`; do not deploy a branch tag. + +## Staging promotion + +1. Apply all `supabase/migrations/` files in timestamp order to a backed-up staging database. +2. Start the digest-pinned images with `docker compose -f compose.production.yaml up -d`. +3. Require the API `/api/v1/ready` check and frontend health check to pass. +4. Run `JUNTLY_BASE_URL=https://staging.example scripts/smoke.sh` against the API origin. +5. Exercise one localized signed-out discovery journey and one Clerk-authenticated account journey. +6. Complete Stripe Connect onboarding with a test provider. Require `details_submitted`, `charges_enabled`, and `payouts_enabled` before checkout. +7. In Stripe test mode, exercise one card payment and one eligible MB WAY payment. Verify gross, fee, provider net, invoice reference, and one idempotent signed webhook receipt in PostgreSQL. +8. Exercise one refund and one synthetic dispute event. Confirm duplicate webhook delivery creates no duplicate payment event. +9. Confirm Stripe Tax registrations/settings, invoice branding, Connect platform profile, MB WAY enablement, legal operator identity, VAT/tax details, terms, privacy, and cancellation/refund wording with the responsible business/legal owner. +10. Record image digests and migration head. Promote those exact digests to production only after explicit approval. + +## Backup and restore + +Run `BACKUP_DIR= scripts/backup-postgres.sh` before migrations and on the scheduled backup cadence. Move dumps and checksums to encrypted, access-controlled storage with retention appropriate to legal obligations. + +Restore only into an isolated target first: + +`DATABASE_URL= BACKUP_FILE= CONFIRM_RESTORE=RESTORE scripts/restore-postgres.sh` + +After restore, apply pending migrations, run readiness and smoke checks, compare bounded record counts, then authorize traffic. Never test restores against production. + +## Rollback + +Application rollback uses the previous immutable image digests. Database migrations are forward-only. If a migration causes an incident, stop writes, restore into a new database from the verified pre-migration backup, apply the reviewed recovery migration if applicable, validate, and switch the database endpoint under owner approval. + +## Monitoring + +Alert on sustained `/api/v1/ready` failures, elevated 5xx rate, latency, database connection exhaustion, email-outbox failures, container restarts, Stripe webhook failures/retries, payment orders stuck in `processing` or `refund_pending`, open disputes, Connect accounts losing charge/payout capability, and payout failures visible in Stripe. Correlate requests by `X-Request-ID`; do not put message bodies, contact data, Stripe payloads/signatures, tokens, or database URLs in logs. + +## Payment activation gate + +Keep production checkout unavailable until every item below is true: + +- the previously exposed live secret is revoked and a replacement exists only in the deployment secret store; +- Stripe Connect is activated for the platform and each provider completes Stripe-hosted onboarding; +- the public webhook points to `https://somosvila.com/api/v1/payments/webhooks/stripe` and subscribes to `checkout.session.completed`, `checkout.session.async_payment_succeeded`, `checkout.session.async_payment_failed`, `charge.refunded`, `charge.dispute.created`, `charge.dispute.closed`, and `account.updated`; +- cards and MB WAY are enabled for EUR where the Stripe account and transaction are eligible; +- automatic tax, tax-ID collection, invoices, platform fee, refund policy, dispute ownership, payout schedule, and reserve exposure are reviewed; +- production Clerk, database backups, legal operator details, privacy contacts, and monitoring are configured; +- test-mode payment, payout, refund, dispute, replay, and recovery evidence has been recorded. diff --git a/docs/operations/incident-privacy.md b/docs/operations/incident-privacy.md new file mode 100644 index 0000000..fc5b16b --- /dev/null +++ b/docs/operations/incident-privacy.md @@ -0,0 +1,28 @@ +# Incident and privacy operations + +## Incident handling + +1. Assign an incident lead and timestamp the start. +2. Contain access or disable the affected capability without deleting evidence. +3. Rotate exposed credentials through their provider and restart only the affected services. +4. Preserve sanitized request IDs, audit records, image digests, and deployment metadata. Do not copy private messages or credentials into tickets. +5. Determine affected data subjects and legal notification obligations with the data controller. +6. Restore service from verified artifacts, monitor, and write a blameless follow-up with concrete controls. + +## Data-subject requests + +Authenticate the requester through Clerk and reconcile the durable internal user before processing access, correction, portability, or deletion requests. Record request date, scope, decision, completion date, and legal-retention exceptions in restricted operational records. Export only the requester's data through an encrypted transfer. Never send exports through logs or public object URLs. + +Deletion is a reviewed workflow, not an unauthenticated endpoint. Revoke sessions, remove or anonymize marketplace profile data where legally permitted, preserve required financial/security/audit records under documented retention, and verify that backups expire through normal retention rather than being selectively edited. + +## Privacy defaults + +- Contact channels remain encrypted at rest and are revealed only through the authorized endpoint. +- Conversations, proposals, bookings, reports, and private locations remain participant- or moderator-scoped. +- Administrative metrics are aggregate-only and capped. +- Analytics must not include message bodies, contact channels, private locations, Clerk tokens, or stable cross-site identifiers. +- Notification preferences are honored before in-app or email-outbox creation. + +## Quarterly exercises + +Run a restore drill, credential-rotation drill, administrator-access review, data-subject-request tabletop, and incident communication exercise. Record only sanitized outcomes and owners. diff --git a/docs/superpowers/plans/2026-08-21-durable-internal-user-mapping.md b/docs/superpowers/plans/2026-08-21-durable-internal-user-mapping.md new file mode 100644 index 0000000..c85dcb2 --- /dev/null +++ b/docs/superpowers/plans/2026-08-21-durable-internal-user-mapping.md @@ -0,0 +1,511 @@ +# Durable Internal User Mapping Implementation Plan + +> **For agentic workers:** REQUIRED SUB-SKILL: Use `superpowers:executing-plans` task-by-task. Every behavior change follows RED → GREEN → REFACTOR. + +**Goal:** Persist one opaque Juntly UUID per Clerk-verified subject, expose it through a same-origin reconciliation route, and prove idempotency against disposable Supabase PostgreSQL. + +**Architecture:** `feature/internal-user-mapping` consumes the immutable health/API parent (`727f288`) and the committed Clerk foundation (`e154f44`) as a local stacked branch. The browser calls a Next.js BFF with no identity body; the BFF obtains a current Clerk session token, Go verifies it, and a provider-neutral service reconciles the verified subject through Ent into the single `public.internal_users` table. + +**Tech Stack:** Next.js 16.3.1, Clerk Next.js 7.7.9, Go 1.26, Clerk Go SDK v2.7.0, Ent v0.14.6, pgx v5.10.0, Supabase CLI 2.78.1, PostgreSQL, OpenAPI 3.1, Vitest. + +## Global Constraints + +- Preserve `feature/clerk-identity` and `feature/health-tracer`; consume their local commits rather than copying their source. +- The browser must never submit a Clerk subject, internal ID, profile field, role, or entitlement. +- `internal_users` stores only opaque UUID, immutable verified subject, and timestamps. No email, name, provider profile, metadata, or token is persisted. +- SQL in `supabase/migrations/` is the migration authority. Ent reflects SQL; API startup never auto-migrates. +- Bearer verification happens in Go; BFF session validation is defense in depth. +- `JUNTLY_API_ORIGIN`, database URLs, Clerk/Supabase credentials, tokens, and generated keys stay server-only and ignored. Do not print, stage, or commit values. +- The BFF maps backend configuration/upstream/malformed-payload/request-ID failures to privacy-safe documented errors; it never exposes backend topology. +- Preserve `localhost:4200` as the canonical browser auth origin. +- Do not add marketplace, profile, payment, billing, role, entitlement, webhook, or provider-ownership scope. +- No push, PR, remote Supabase action, Clerk write, or mapping-feature commit is authorized by this plan. Task 1 alone is authorized to create the local merge commit required to stack `e154f44` on the health/API base; pause after verification for an explicit delivery decision. + +--- + +### Task 1: Build the local stacked predecessor baseline + +**Objective:** Put the already verified health/API and Clerk foundations in the implementation branch without modifying their source commits. + +**Files:** +- Merge input: local commit `e154f44` from `feature/clerk-identity` +- Verify: `backend/go.mod`, `frontend/package.json`, `frontend/src/proxy.ts`, `frontend/src/app/api/v1/health/route.ts` + +**Interfaces:** +- Consumes: `727f288` as `feature/internal-user-mapping` base and `e154f44` as the Clerk source commit. +- Produces: one clean branch where the Go API and Clerk server helpers are both available. + +- [ ] **Step 1: Capture the immutable branch inputs** + +Run: + +```bash +git status --short --branch +git rev-parse HEAD development feature/health-tracer feature/clerk-identity +git merge-base --is-ancestor 727f288 HEAD +git merge-base --is-ancestor e154f44 HEAD +``` + +Expected: clean branch, health is an ancestor, Clerk is not yet an ancestor. + +- [ ] **Step 2: Merge only the local Clerk foundation** + +Run: + +```bash +git merge --no-ff e154f44 -m "chore: stack Clerk identity foundation for user mapping" +``` + +Resolve only overlapping health/Clerk source by preserving both route sets and their tests. Do not alter either predecessor commit or update a remote. + +- [ ] **Step 3: Verify the merge result** + +Run: + +```bash +npm --prefix frontend run verify +cd backend && go test ./... +cd .. && git diff --check && codegraph sync . && codegraph affected . +``` + +Expected: frontend verification and Go tests pass; whitespace check succeeds; CodeGraph is synchronized. + +- [ ] **Step 4: Checkpoint** + +Do not commit. Record the merge SHA and exact test results for the later final review. + +### Task 2: Add the authoritative Supabase migration and matching Ent schema + +**Objective:** Define one forward-only, minimal mapping table and make generated Ent code represent its exact columns and constraints. + +**Files:** +- Create: `supabase/config.toml` +- Create: `supabase/migrations/_create_internal_users.sql` +- Create: `backend/internal/users/migration_contract_test.go` +- Create: `backend/ent/entc.go` +- Create: `backend/ent/schema/internaluser.go` +- Create/generated: `backend/ent/**` +- Modify: `backend/go.mod`, `backend/go.sum` + +**Interfaces:** +- Consumes: the approved SQL contract `{ id uuid, clerk_subject text unique, created_at timestamptz, updated_at timestamptz }`. +- Produces: Ent type `InternalUser` with UUID ID, immutable unique subject, immutable creation time, and UTC update time. + +- [ ] **Step 1: Write the RED migration contract** + +Create `backend/internal/users/migration_contract_test.go` with an assertion that scans migration SQL and requires each of: + +```go +func TestCreateInternalUsersMigrationContract(t *testing.T) { + requireSQL(t, "create table public.internal_users") + requireSQL(t, "id uuid primary key") + requireSQL(t, "clerk_subject text not null unique") + requireSQL(t, "created_at timestamptz not null") + requireSQL(t, "updated_at timestamptz not null") + requireSQL(t, "char_length(clerk_subject) between 1 and 255") +} +``` + +Run: + +```bash +cd backend && go test ./internal/users -run TestCreateInternalUsersMigrationContract -count=1 +``` + +Expected: failure because the migration does not exist. + +- [ ] **Step 2: Add the minimal forward-only migration** + +Create exactly one timestamped `supabase/migrations/*_create_internal_users.sql` containing: + +```sql +create extension if not exists pgcrypto; + +create table public.internal_users ( + id uuid primary key, + clerk_subject text not null unique, + created_at timestamptz not null default timezone('utc', now()), + updated_at timestamptz not null default timezone('utc', now()), + constraint internal_users_clerk_subject_nonempty + check (char_length(clerk_subject) between 1 and 255) +); + +create index internal_users_created_at_idx + on public.internal_users (created_at); +``` + +Create Supabase config only through `supabase init` or an equivalent generated project config. Do not create a PostgreSQL service in `compose.yaml`. + +- [ ] **Step 3: Verify GREEN for the migration contract** + +Run: + +```bash +cd backend && go test ./internal/users -run TestCreateInternalUsersMigrationContract -count=1 +``` + +Expected: pass. + +- [ ] **Step 4: Write the RED Ent schema contract** + +Create `backend/ent/schema/internaluser_test.go` with static/schema assertions covering the exact `InternalUser` fields and no profile/email fields. Run: + +```bash +cd backend && go test ./ent/schema -run TestInternalUserSchemaContract -count=1 +``` + +Expected: failure because no Ent schema exists. + +- [ ] **Step 5: Add Ent dependencies, schema, and generator** + +Pin direct modules in `backend/go.mod`: + +```text +entgo.io/ent v0.14.6 +github.com/google/uuid +github.com/jackc/pgx/v5 v5.10.0 +``` + +Create `backend/ent/entc.go` with a `go:generate` directive that invokes Ent generation against `./schema`. Implement `InternalUser` so `id` uses `uuid.New`, `clerk_subject` is nonempty, max-255, unique, and immutable, `created_at` is immutable, and `updated_at` uses UTC time. Generate code with: + +```bash +cd backend && go generate ./ent +``` + +- [ ] **Step 6: Verify schema and generated output** + +Run: + +```bash +cd backend && go test ./ent/schema -run TestInternalUserSchemaContract -count=1 +go test ./... +go vet ./... +git diff --check +``` + +Expected: all pass. Inspect generated names to confirm `internal_users`, `clerk_subject`, `created_at`, and `updated_at` match the migration. + +- [ ] **Step 7: Checkpoint** + +Do not commit. Run a changed/untracked secret scan; report only paths and detection counts. + +### Task 3: Implement provider-neutral, race-safe reconciliation + +**Objective:** Reconcile a verified subject to a stable internal user without importing Clerk concepts into domain logic. + +**Files:** +- Create: `backend/internal/users/model.go` +- Create: `backend/internal/users/repository.go` +- Create: `backend/internal/users/service.go` +- Create: `backend/internal/users/ent_repository.go` +- Create: `backend/internal/users/service_test.go` +- Create: `backend/internal/users/ent_repository_test.go` + +**Interfaces:** + +```go +type VerifiedIdentity struct { Subject string } + +type InternalUser struct { + ID uuid.UUID + CreatedAt time.Time +} + +type Repository interface { + FindBySubject(context.Context, string) (InternalUser, bool, error) + Create(context.Context, string) (InternalUser, error) +} + +type Service interface { + Reconcile(context.Context, VerifiedIdentity) (InternalUser, bool, error) +} +``` + +- [ ] **Step 1: Write RED service cases** + +In `service_test.go`, use a recording fake repository and require: + +```go +func TestReconcileRejectsEmptySubjectBeforeRepositoryAccess(t *testing.T) +func TestReconcileCreatesFirstMapping(t *testing.T) +func TestReconcileReturnsExistingMappingWithoutMutation(t *testing.T) +func TestReconcileReloadsWinnerAfterUniqueConflict(t *testing.T) +func TestReconcileReturnsControlledFailureForRepositoryError(t *testing.T) +``` + +Run: + +```bash +cd backend && go test ./internal/users -run '^TestReconcile' -count=1 +``` + +Expected: failure because the service does not exist. + +- [ ] **Step 2: Implement the minimal service** + +Add only: + +```go +func (s service) Reconcile(ctx context.Context, identity VerifiedIdentity) (InternalUser, bool, error) +``` + +Validation occurs before `FindBySubject`. It returns an existing row with `false`; it creates a missing row with `true`; it handles only the repository’s typed unique-conflict error by finding the exact same subject once more and returning the winner with `false`. + +- [ ] **Step 3: Verify GREEN** + +Run the focused service command from Step 1. Expected: all named cases pass. + +- [ ] **Step 4: Write the RED Ent repository integration cases** + +Create database-backed tests that require: + +```go +func TestEntRepositoryFindsAndCreatesByExactSubject(t *testing.T) +func TestReconcileConcurrentSameSubjectProducesOneStableRow(t *testing.T) +``` + +The test harness must receive only a disposable `TEST_DATABASE_URL` through its environment and skip with an explicit message when absent. The final mandatory integration invocation must set that URL and must not treat a skipped test as proof. + +- [ ] **Step 5: Implement the Ent repository** + +Implement exact-subject query/create mapping. Translate only PostgreSQL/Ent uniqueness violations into the domain’s typed unique-conflict error. Do not retry arbitrary database errors and do not query by any display field or timestamp. + +- [ ] **Step 6: Verify against a disposable database** + +Start only the project-owned local Supabase stack, apply the entire migration ledger, supply the disposable test URL through environment, then run: + +```bash +cd backend && go test ./internal/users -run 'TestEntRepository|TestReconcileConcurrent' -count=1 +``` + +Expected: one mapping row, one stable UUID, and no duplicate mapping for a synthetic subject. Stop/reset the local stack afterwards without printing connection details. + +### Task 4: Verify bearer identity in Go before reconciliation + +**Objective:** Derive `VerifiedIdentity` only from a Clerk-verified session token and keep health publicly reachable. + +**Files:** +- Create: `backend/internal/authn/verifier.go` +- Create: `backend/internal/authn/clerk_verifier.go` +- Create: `backend/internal/authn/middleware.go` +- Create: `backend/internal/authn/middleware_test.go` +- Modify: `backend/go.mod`, `backend/go.sum` +- Modify: `backend/cmd/api/main.go` +- Modify: `backend/internal/httpapi/health_handler.go` + +**Interfaces:** + +```go +type Verifier interface { + Verify(context.Context, string) (users.VerifiedIdentity, error) +} + +func RequireVerifiedIdentity(verifier Verifier, next http.Handler) http.Handler +func IdentityFromContext(context.Context) (users.VerifiedIdentity, bool) +``` + +- [ ] **Step 1: Write RED middleware cases** + +Create tests for absent bearer header, malformed scheme, verifier rejection, empty verified subject, and valid subject context propagation. Add an assertion that the downstream handler is not invoked for every invalid case. + +Run: + +```bash +cd backend && go test ./internal/authn -run TestRequireVerifiedIdentity -count=1 +``` + +Expected: failure because the middleware package is absent. + +- [ ] **Step 2: Implement a testable verification boundary** + +Add the provider-neutral `Verifier` interface and middleware. The middleware parses only `Authorization: Bearer session-token`, invokes the verifier, rejects invalid outcomes with `401`, and stores a typed identity context value. It does not parse JWT claims itself. + +- [ ] **Step 3: Implement and compile-check the Clerk adapter** + +Add `github.com/clerk/clerk-sdk-go/v2@v2.7.0`. Use its documented session-verification middleware/API from a dedicated `clerk_verifier.go` adapter, configured only by `CLERK_SECRET_KEY`, optional `CLERK_JWT_KEY`, and exact `CLERK_AUTHORIZED_PARTIES`. Require an actual compile-backed adapter test; do not retain an uncompiled guessed method call. Convert Clerk failures to the local verifier error without embedding SDK diagnostics. + +- [ ] **Step 4: Verify GREEN** + +Run: + +```bash +cd backend && go test ./internal/authn -count=1 +go test ./... +go vet ./... +go build ./cmd/api +``` + +Expected: pass. Health remains public because authentication middleware will be applied only to the reconciliation route in Task 5. + +### Task 5: Add the closed reconciliation API contract and handler + +**Objective:** Add a body-less, authenticated API that returns only an opaque mapping response. + +**Files:** +- Modify: `openapi/juntly-api.v1.yaml` +- Modify/generated: `frontend/src/shared/api/generated/**` +- Create: `backend/internal/httpapi/reconcile_handler.go` +- Create: `backend/internal/httpapi/reconcile_handler_test.go` +- Modify: `backend/internal/httpapi/health_handler.go` + +**Interfaces:** + +```go +type ReconcileService interface { + Reconcile(context.Context, users.VerifiedIdentity) (users.InternalUser, bool, error) +} + +func NewReconcileHandler(service ReconcileService) http.Handler +``` + +- [ ] **Step 1: Write RED handler cases** + +Create tests requiring: + +```go +func TestReconcileHandlerReturnsUnauthorizedWithoutVerifiedIdentity(t *testing.T) +func TestReconcileHandlerReturnsOpaqueInternalUser(t *testing.T) +func TestReconcileHandlerReturnsSafeUnavailableForDependencyFailure(t *testing.T) +func TestReconcileHandlerRejectsNonPOSTMethods(t *testing.T) +``` + +The successful response must include only UUID `id`, RFC3339 `createdAt`, and the correlation header. The failure body must not include the synthetic repository error text. + +Run: + +```bash +cd backend && go test ./internal/httpapi -run TestReconcileHandler -count=1 +``` + +Expected: failure because the handler does not exist. + +- [ ] **Step 2: Add the OpenAPI contract** + +Add `POST /api/v1/auth/reconcile` with no request body, `clerkSession` HTTP bearer security, and exact responses: + +```yaml +200: InternalUserResponse +401: ErrorResponse with code UNAUTHORIZED +503: ErrorResponse with code SERVICE_UNAVAILABLE +``` + +`InternalUserResponse` has only `id: uuid` and `createdAt: date-time`. All write schemas remain closed; no caller-controlled identity field exists. + +- [ ] **Step 3: Implement handler and route composition** + +Implement `NewReconcileHandler` using `IdentityFromContext`, `ReconcileService`, and the existing correlation-header helper. Extend `NewRouter` to register the public health handler unchanged and wrap only `/api/v1/auth/reconcile` with `RequireVerifiedIdentity`. + +- [ ] **Step 4: Regenerate and verify GREEN** + +Run: + +```bash +npm --prefix frontend run codegen +npm --prefix frontend run codegen:check +cd backend && go test ./internal/httpapi -run TestReconcileHandler -count=1 +go test ./... +``` + +Expected: generated client includes the reconciliation endpoint and all focused/full Go tests pass. + +### Task 6: Add same-origin, Clerk-aware BFF reconciliation + +**Objective:** Reconcile an authenticated session without revealing Go topology or permitting browser-provided identity claims. + +**Files:** +- Create: `frontend/src/app/api/v1/auth/reconcile/route.ts` +- Create: `frontend/src/app/api/v1/auth/reconcile/route.test.ts` +- Modify: `frontend/.env.example` +- Modify: `frontend/README.md` +- Modify: `compose.yaml` + +**Interfaces:** + +```ts +export async function POST(request: Request): Promise +``` + +The route consumes `await auth()` from `@clerk/nextjs/server`, a request-local token from `getToken()`, `process.env.JUNTLY_API_ORIGIN`, and the generated OpenAPI SDK. It produces only `InternalUserResponse` or the documented safe error envelope. + +- [ ] **Step 1: Write RED BFF cases** + +Mock Clerk server auth and upstream fetch. Require: + +```ts +it("returns 401 without an authenticated Clerk session", async () => {}) +it("returns 401 when the authenticated session has no current token", async () => {}) +it("forwards only a server-obtained bearer token and correlation ID", async () => {}) +it("maps upstream failure to a topology-safe 503", async () => {}) +it("maps malformed upstream response to a topology-safe 503", async () => {}) +``` + +The success case asserts that request headers include `Authorization: Bearer session-token` and `X-Request-ID`, while the browser response body excludes both the bearer token and backend origin. + +Run: + +```bash +npm --prefix frontend test -- src/app/api/v1/auth/reconcile/route.test.ts +``` + +Expected: failure because the route is absent. + +- [ ] **Step 2: Implement the smallest BFF** + +Call `await auth()`. Return `401` before upstream access when not authenticated or no token is available. Forward a current server-obtained token and the validated/generated request ID only to `JUNTLY_API_ORIGIN`. Validate the generated client result structurally before returning its two response fields. Convert all upstream/configuration/validation exceptions to the documented generic `503` without leakable cause text. + +- [ ] **Step 3: Add only safe configuration documentation** + +Add variable names/placeholders—never values—to `.env.example`, `compose.yaml`, and the frontend README. Document `localhost:4200` as the local browser authority and `JUNTLY_API_ORIGIN` as server-only. + +- [ ] **Step 4: Verify GREEN** + +Run: + +```bash +npm --prefix frontend test -- src/app/api/v1/auth/reconcile/route.test.ts +npm --prefix frontend run verify +docker compose config +``` + +Expected: all pass with no public backend-origin value in generated browser assets or response fixtures. + +### Task 7: Perform durable, runtime, and contract verification + +**Objective:** Produce evidence for database durability, API safety, and local authenticated behavior without fabricating a user or exposing credentials. + +**Files:** +- Modify only if evidence changes instructions: `frontend/README.md`, `context/architecture.md`, `context/stack.md` +- Verify: all Task 1–6 paths, plus untracked documentation/spec/plan files. + +- [ ] **Step 1: Run migration-chain and concurrent integration proof** + +Use a disposable local Supabase project/database only. Apply all migrations in order. Run the Task 3 integration test with `TEST_DATABASE_URL` injected only through the process environment. Capture only pass/fail, synthetic row count, UUID equality, and timestamp equality; do not print connection strings or database credentials. + +- [ ] **Step 2: Run all local quality gates** + +Run: + +```bash +codegraph sync . +codegraph affected . +cd backend && go test ./... && go vet ./... && go build ./cmd/api +cd .. && npm --prefix frontend run verify +docker compose config +git diff --check +``` + +Expected: all commands pass. Any untracked source file is inspected separately because `git diff --check` does not cover it. + +- [ ] **Step 3: Run a secret-safe changed-file review** + +Inspect staged, modified, and untracked source/docs for credential-like values. Report only path names and detection counts. Confirm ignored local config remains ignored with `git check-ignore` using path names only. + +- [ ] **Step 4: Run the optional real-session smoke only when authorized** + +With an approved development Clerk test user, start the local API/frontend at canonical `localhost:4200`, establish a real session, and call same-origin `POST /api/v1/auth/reconcile` twice. Evidence is limited to statuses, request IDs, stable opaque-ID equality, stable creation-time equality, and synthetic-safe row count. Do not record email, subject, cookie, bearer, URL, or secret. + +If no approved test user/session is available, report this gate as blocked; do not claim it from unit tests. + +- [ ] **Step 5: Final review and delivery boundary** + +Create a requirement-to-code-to-test matrix against `docs/superpowers/specs/2026-08-21-durable-internal-user-mapping-design.md`. Re-run `git status --short --branch`, `git diff --check`, and inspect every untracked mapping file. Do not commit, push, merge, deploy, or update Kanban terminal status without separate user authorization. diff --git a/docs/superpowers/plans/2026-08-23-taxonomy-locations-provider-profiles.md b/docs/superpowers/plans/2026-08-23-taxonomy-locations-provider-profiles.md new file mode 100644 index 0000000..1fc5df7 --- /dev/null +++ b/docs/superpowers/plans/2026-08-23-taxonomy-locations-provider-profiles.md @@ -0,0 +1,571 @@ +# Taxonomy, Locations, and Provider Profiles Implementation Plan + +> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. + +**Goal:** Deliver database-configurable service taxonomy, source-verifiable Portugal launch locations with PostGIS radius queries, and owner-only provider profiles through Juntly's authenticated full-stack authority chain. + +**Architecture:** Reference data is seeded by forward migrations from a deterministic reviewed manifest. Ent schemas model taxonomy, locales, languages, localities, provider profiles, and composite edge schemas; the locality table also has a SQL-generated PostGIS geography point used by a parameterized repository. Go owns provider capability, ownership, validation, transactions, and strict OpenAPI transports; Next.js provides same-origin BFF routes and a localized owner-only onboarding form. + +**Tech Stack:** Python 3.12 stdlib source verifier, DGT CAOP 2025 GeoPackage, OpenStreetMap/Nominatim one-time source verification, Go 1.26, Ent 0.14.6, pgx/PostgreSQL/PostGIS, Supabase migrations, OpenAPI 3.1, generated TypeScript, Next.js 16.3.1, Clerk, Vitest, Testing Library. + +## Global Constraints + +- Taxonomy administration APIs/UI are deferred to Slice 11; Slice 2 taxonomy is database-configurable and read-only through product APIs. +- Provider profiles remain owner-only; no public provider DTO/page/metadata exists until Slice 4. +- A caller must have `providerEnabled=true`; customer capability, paid status, or Clerk metadata never grants provider/admin authority. +- Persist no email, phone, WhatsApp, exact provider address/coordinates, identity documents, verification claims, payment data, Clerk subject/token/session, or internal-user UUID in public/profile responses. +- Browser calls same-origin `/api/v1/...`; `JUNTLY_API_ORIGIN` stays server-only. +- All request/response objects are closed. Unknown properties, explicit nulls, duplicate IDs/codes, unsupported references, invalid bounds, and trailing JSON fail before service mutation. +- Migrations are forward-only and API startup never migrates schema. +- pt-PT is the default locale; English ships; Spanish has exact message-key parity. +- Real PostgreSQL/PostGIS tests use synthetic isolated profile rows and the canonical migration ledger; they must not skip during acceptance. +- Reference data may contain only reviewed public administrative/place facts and attribution. Downloaded CAOP/Nominatim response artifacts remain temporary and are deleted. +- Each behavioral unit follows observed RED → GREEN → REFACTOR, focused/full gates, a reviewed staged allowlist, and a local commit. No push or remote mutation. + +--- + +## Frozen launch reference values + +### CAOP source + +```text +URL: https://geo2.dgterritorio.gov.pt/caop/CAOP_Continente_2025-gpkg.zip +SHA-256: 87cd67f4b1fbadf23d9324e6fb231ff05531e4db347af36ccc7c6cbabe3ecd1d +Bytes: 111647845 +Layers: cont_distritos, cont_municipios, cont_freguesias +``` + +Administrative rows: + +```text +country: PT — Portugal +district: 05 — Castelo Branco +municipality: 0502 — Castelo Branco +municipality: 0505 — Idanha-a-Nova +parish: 050205 — Castelo Branco +parish: 050510 — Penha Garcia +parish: 050518 — União das freguesias de Idanha-a-Nova e Alcafozes +parish: 050520 — União das freguesias de Monsanto e Idanha-a-Velha +parish: 050521 — União das freguesias de Zebreira e Segura +``` + +### OSM locality centers + +```text +castelo-branco | relation 5396187 | 39.8266322, -7.4919318 | parent 050205 +idanha-a-nova | relation 5395738 | 39.9260883, -7.2436356 | parent 050518 +zebreira | node 440173641 | 39.8455920, -7.0703366 | parent 050521 +penha-garcia | relation 5431477 | 40.0422569, -7.0163521 | parent 050510 +monsanto | node 371426674 | 40.0387510, -7.1151133 | parent 050520 +``` + +The manifest records `source=OpenStreetMap`, source type/ID, retrieval date `2026-08-23`, ODbL URL, and attribution `© OpenStreetMap contributors`. + +### Initial taxonomy + +Top-level slugs and child slugs: + +```text +home-repairs: plumbing, electrical-work, construction, small-repairs +home-and-garden: cleaning, gardening +rural-and-transport: agricultural-assistance, transport +care-and-learning: elderly-assistance, animal-care, private-lessons +food-and-technology: meal-preparation, computer-repair +``` + +Supported locale/language codes are `pt-PT`, `en`, and `es`. + +## File map + +### Reference tooling/data + +- `reference/portugal/launch-area-2025.json` — deterministic reviewed source manifest. +- `scripts/build_launch_reference.py` — CAOP checksum/SQLite extraction plus policy-compliant Nominatim resolver. +- `scripts/tests/test_build_launch_reference.py` — offline synthetic archive/geocoder contract tests. + +### Persistence + +- `supabase/migrations/_create_taxonomy_locations_provider_profiles.sql` — reference/profile tables, PostGIS center, constraints, seeds. +- `backend/ent/schema/supportedlocale.go` +- `backend/ent/schema/servicecategory.go` +- `backend/ent/schema/servicecategorytranslation.go` — composite edge schema. +- `backend/ent/schema/spokenlanguage.go` +- `backend/ent/schema/spokenlanguagetranslation.go` — composite edge schema. +- `backend/ent/schema/administrativearea.go` +- `backend/ent/schema/locality.go` +- `backend/ent/schema/providerprofile.go` +- `backend/ent/schema/providerservicelocality.go` — composite edge schema. +- `backend/ent/schema/providerspokenlanguage.go` — composite edge schema. +- `backend/ent/schema/*_test.go` — structural privacy/constraint contracts. +- `backend/ent/**` — generated Ent output. +- `backend/internal/users/migration_contract_test.go` — complete SQL migration/seed contract. + +### Go modules + +- `backend/internal/reference/model.go` +- `backend/internal/reference/repository.go` +- `backend/internal/reference/sql_repository.go` +- `backend/internal/reference/service.go` +- `backend/internal/reference/*_test.go` +- `backend/internal/provideraccess/service.go` +- `backend/internal/provideraccess/service_test.go` +- `backend/internal/providers/model.go` +- `backend/internal/providers/repository.go` +- `backend/internal/providers/ent_repository.go` +- `backend/internal/providers/service.go` +- `backend/internal/providers/*_test.go` +- `backend/internal/httpapi/reference_handler.go` +- `backend/internal/httpapi/provider_profile_handler.go` +- related handler/router/OpenAPI tests +- `backend/cmd/api/main.go` — dependency composition using the existing DB/Ent client. + +### Contract/BFF/UI + +- `openapi/juntly-api.v1.yaml` +- `frontend/src/shared/api/generated/**` +- `frontend/src/app/api/v1/catalog/categories/route.ts` +- `frontend/src/app/api/v1/reference/localities/route.ts` +- `frontend/src/app/api/v1/reference/languages/route.ts` +- `frontend/src/app/api/v1/me/provider-profile/route.ts` +- matching route tests +- `frontend/src/features/provider/provider-profile-form.tsx` +- `frontend/src/features/provider/provider-profile-form.test.tsx` +- `frontend/src/app/[locale]/account/provider-profile/page.tsx` +- `frontend/src/app/[locale]/account/provider-profile/page.test.tsx` +- `frontend/src/features/account/account-capabilities-card.tsx` and test +- `frontend/messages/{pt-PT,en,es}.json` +- `frontend/src/i18n/messages.test.ts` + +## Task 1: Freeze and verify launch reference data + +**Files:** +- Create: `reference/portugal/launch-area-2025.json` +- Create: `scripts/build_launch_reference.py` +- Create: `scripts/tests/test_build_launch_reference.py` +- Modify: `.gitignore` only if a precise source-tool cache path needs exclusion; do not ignore broad data directories. + +**Interfaces:** + +```python +def verify_caop_archive(path: Path, expected_sha256: str) -> None: ... +def extract_administrative_rows(path: Path, required: tuple[RequiredArea, ...]) -> list[AdministrativeArea]: ... +def resolve_localities(resolve: Callable[[str], list[dict[str, object]]]) -> list[Locality]: ... +def build_manifest(caop_zip: Path, resolve: Callable[[str], list[dict[str, object]]]) -> dict[str, object]: ... +``` + +- [ ] **Step 1: Write offline RED tests.** + +Create a synthetic ZIP containing a SQLite GeoPackage-like database with `cont_distritos`, `cont_municipios`, and `cont_freguesias`. Assert checksum mismatch rejection, missing/duplicate exact row rejection, required code/name/parent extraction, deterministic ordering, exact five OSM element selection, request spacing hook invocation, and manifest output equality. Assert the manifest contains no postal code or source response dump. + +- [ ] **Step 2: Run RED.** + +```bash +python -m unittest scripts.tests.test_build_launch_reference -v +``` + +Expected: import failure because `scripts/build_launch_reference.py` is absent. + +- [ ] **Step 3: Implement the stdlib verifier.** + +Use `urllib.request`, `hashlib`, `zipfile`, `sqlite3`, `tempfile`, and `shutil`. The network CLI uses an identifying `User-Agent`, serial Nominatim requests with at least 1.05 seconds between calls, `limit=3`, `countrycodes=pt`, and exact allowlisted OSM type/ID selection from the frozen values. Always delete the temporary root in `finally`. + +- [ ] **Step 4: Write the deterministic manifest.** + +The checked-in JSON contains the frozen CAOP/OSM values above, source/license metadata, locale/category/language seeds, and no unreviewed candidates. + +- [ ] **Step 5: Run GREEN and reproducibility checks.** + +```bash +python -m unittest scripts.tests.test_build_launch_reference -v +python scripts/build_launch_reference.py --verify reference/portugal/launch-area-2025.json +``` + +The first is offline. The second is a deliberate one-time network verification; it compares bytes to the checked-in manifest and removes temporary files. + +- [ ] **Step 6: Commit.** + +```bash +git add reference/portugal/launch-area-2025.json scripts/build_launch_reference.py scripts/tests/test_build_launch_reference.py .gitignore +git commit -m "chore: verify launch reference data" +``` + +## Task 2: Add taxonomy, location, language, and provider-profile schema + +**Files:** +- Create the migration and ten Ent schema files listed in the file map. +- Create focused schema tests. +- Modify `backend/internal/users/migration_contract_test.go`. +- Regenerate `backend/ent/**` and `backend/go.sum` if the generator changes it. + +**Interfaces and schema rules:** + +- `SupportedLocale`: string ID, active, sort order. +- `ServiceCategory`: UUID ID, optional parent UUID, stable slug, active, sort order, timestamps. +- `ServiceCategoryTranslation`: edge schema between category and locale; `field.ID("category_id", "locale")`; name/optional description. +- `SpokenLanguage`: string ID, active, sort order. +- `SpokenLanguageTranslation`: edge schema between language and locale; composite ID. +- `AdministrativeArea`: UUID ID, source/version/external code/kind/name/optional parent/active/timestamps. +- `Locality`: UUID ID, slug/name/parent parish/source/type/source ID/latitude/longitude/active/timestamps. SQL adds generated stored `center geography(Point,4326)` from longitude/latitude. +- `ProviderProfile`: internal-user UUID ID stored as `internal_user_id`; provider type, display name, bio, primary locality ID, radius, three service mode booleans, timestamps. +- `ProviderServiceLocality`: edge schema with composite `(internal_user_id, locality_id)`. +- `ProviderSpokenLanguage`: edge schema with composite `(internal_user_id, language_code)`. + +- [ ] **Step 1: Write RED schema/migration tests.** + +Assert every table, FK/delete action, composite key, unique slug/code, hierarchy kind check, coordinate/radius/name/bio bounds, at-least-one-service-mode check, generated PostGIS center expression, seed counts/slugs/translations, exact CAOP codes, exact five locality source IDs, and prohibited profile/contact/identity columns. + +- [ ] **Step 2: Run RED.** + +```bash +cd backend +go test ./ent/schema ./internal/users -run 'Test.*(Taxonomy|Location|Language|ProviderProfile|Migration)' -count=1 +``` + +Expected: missing schema/migration contracts. + +- [ ] **Step 3: Add one forward migration.** + +Enable PostGIS if absent; create tables/constraints/indexes; insert deterministic UUIDs and manifest values. The API runtime does not execute this migration. + +- [ ] **Step 4: Implement Ent schemas and composite edge schemas.** + +Use installed Ent `field.ID` on edge schemas. Add inverse/through edges only where needed by typed provider transactions. Do not model the generated `center` column as mutable Ent state. + +- [ ] **Step 5: Generate and run GREEN.** + +```bash +cd backend +go generate ./ent +go test ./ent/schema ./internal/users -run 'Test.*(Taxonomy|Location|Language|ProviderProfile|Migration)' -count=1 +``` + +- [ ] **Step 6: Apply the complete migration chain to local Supabase.** + +```bash +supabase migration up --local +``` + +Verify the migration ledger and seed counts through process-only DB configuration; do not print the URL. + +- [ ] **Step 7: Commit.** + +```bash +git add supabase/migrations backend/ent backend/internal/users/migration_contract_test.go backend/go.sum +git commit -m "feat: add provider marketplace reference schema" +``` + +## Task 3: Implement reference catalog and PostGIS radius service + +**Files:** +- Create `backend/internal/reference/{model,repository,sql_repository,service}.go` and tests. + +**Interfaces:** + +```go +type Repository interface { + Categories(context.Context, string) ([]Category, error) + Languages(context.Context, string) ([]Language, error) + Localities(context.Context, string) ([]Locality, error) + NearbyLocalities(context.Context, uuid.UUID, int, string) ([]LocalityDistance, error) + ValidateProfileReferences(context.Context, ProfileReferences) error +} + +type Service interface { + Categories(context.Context, string) ([]Category, error) + Languages(context.Context, string) ([]Language, error) + Localities(context.Context, string) ([]Locality, error) + NearbyLocalities(context.Context, uuid.UUID, int, string) ([]LocalityDistance, error) +} +``` + +Public DTOs contain UUID/slug/localized names/hierarchy labels and optional integer distance metres. They contain no raw coordinates or provider rows. + +- [ ] **Step 1: Write RED service tests.** + +Cover supported/unsupported locales, inactive omission, stable sort order, missing origin, radius bounds 1–200, and controlled unavailability. + +- [ ] **Step 2: Write RED real-PostgreSQL tests.** + +Use at least three synthetic localities: two in range and one out of range. Assert boundary inclusion, ascending distance, UUID tie-break, inactive omission, no raw coordinate fields, and multi-parent category translation correctness. + +- [ ] **Step 3: Run RED.** + +```bash +cd backend +go test ./internal/reference -count=1 +``` + +Expected: missing package symbols. + +- [ ] **Step 4: Implement parameterized SQL repository.** + +Reuse the existing `*sql.DB`. Use fixed SQL identifiers and bound values only. Radius SQL uses `ST_DWithin`, `ST_Distance`, active predicates, and `ORDER BY distance_meters, id`. Category queries never apply a global child limit; each category/subcategory contributes once. + +- [ ] **Step 5: Run unit and non-skipping PostgreSQL GREEN.** + +```bash +cd backend +go test ./internal/reference -count=1 +TEST_DATABASE_URL="$DB_URL" go test ./internal/reference -run 'TestSQLRepository|TestRadius' -count=1 +``` + +- [ ] **Step 6: Commit.** + +```bash +git add backend/internal/reference +git commit -m "feat: add marketplace reference catalog" +``` + +## Task 4: Implement provider capability authorization and transactional profiles + +**Files:** +- Create `backend/internal/provideraccess/service.go` and test. +- Create `backend/internal/providers/{model,repository,ent_repository,service}.go` and tests. + +**Interfaces:** + +```go +type InternalUserReconciler interface { + Reconcile(context.Context, users.VerifiedIdentity) (users.InternalUser, bool, error) +} + +type CapabilityReader interface { + Get(context.Context, users.VerifiedIdentity) (accounts.Account, error) +} + +type ProviderAuthorizer interface { + RequireProvider(context.Context, users.VerifiedIdentity) (users.InternalUser, error) +} + +type Service interface { + Get(context.Context, users.VerifiedIdentity) (*Profile, error) + Put(context.Context, users.VerifiedIdentity, ReplaceProfile) (Profile, error) +} +``` + +`ReplaceProfile` contains only approved profile fields and reference IDs/codes. Repository methods accept the already-authorized internal user UUID; no transport can provide it. + +- [ ] **Step 1: Write provider-access RED tests.** + +Invalid identity, unavailable account, provider-disabled, and provider-enabled cases; prove no repository/profile call occurs before authorization. + +- [ ] **Step 2: Write provider service RED tests.** + +Cover every scalar bound, trim behavior, provider enum, duplicate/size-constrained locality/language lists, primary-locality membership, at-least-one mode, zero-radius rule, inactive/missing references, owner-only lookup, missing profile, create/update, and controlled failures. + +- [ ] **Step 3: Write transaction/concurrency RED tests.** + +Against PostgreSQL, prove full replacement commits scalars plus exact child sets, invalid child rolls back all changes, repeat is idempotent, concurrent first PUT yields one owner profile, and one user's ID cannot retrieve/update another's profile. + +- [ ] **Step 4: Implement minimal services and Ent repository.** + +Use one Ent transaction for profile and edge-schema replacement. Reload canonical state before commit; normalize timestamps to UTC microseconds. Retry only the documented unique-owner conflict by loading the winner; do not retry validation/FK errors. + +- [ ] **Step 5: Run GREEN.** + +```bash +cd backend +go test ./internal/provideraccess ./internal/providers -count=1 +TEST_DATABASE_URL="$DB_URL" go test -race ./internal/providers -run 'TestEntRepository|TestConcurrent|TestTransaction' -count=1 +``` + +- [ ] **Step 6: Commit.** + +```bash +git add backend/internal/provideraccess backend/internal/providers +git commit -m "feat: add owner-only provider profiles" +``` + +## Task 5: Publish Go/OpenAPI reference and profile contracts + +**Files:** +- Modify `openapi/juntly-api.v1.yaml`. +- Create reference/profile handlers and tests. +- Modify router/OpenAPI contract tests and `backend/cmd/api/main.go`. +- Regenerate `frontend/src/shared/api/generated/**`. + +**Operations:** + +```text +GET /api/v1/catalog/categories +GET /api/v1/reference/localities +GET /api/v1/reference/languages +GET /api/v1/me/provider-profile +PUT /api/v1/me/provider-profile +``` + +Reference operations accept locale; localities optionally accept paired `nearLocalityId` and `radiusKm`. Profile GET returns `{ "profile": null }` before onboarding or a closed owner-only profile. PUT accepts the exact spec request. + +- [ ] **Step 1: Write RED OpenAPI/handler/router tests.** + +Cover strict query/body parsing, locale/radius pairing, null/unknown/duplicate/bounds cases, no service call on invalid input, public reference routes, protected profile routes, `401`, `403 FORBIDDEN`, `400`, `503`, correlation parity, and output privacy. + +- [ ] **Step 2: Run RED.** + +```bash +cd backend +go test ./internal/httpapi -run 'Test.*(Category|Locality|Language|ProviderProfile|OpenAPI|Router)' -count=1 +``` + +- [ ] **Step 3: Implement handlers and dependency composition.** + +Use `url.Values` exact allowlisting for reference queries and `json.Decoder.DisallowUnknownFields()` plus second-value EOF checks for profile PUT. Add only `INVALID_REQUEST`, `FORBIDDEN`, and existing safe codes. + +- [ ] **Step 4: Extend OpenAPI closed schemas and regenerate.** + +```bash +cd frontend +npm run codegen +npm run codegen:check +``` + +- [ ] **Step 5: Run GREEN and cross-layer compile.** + +```bash +cd backend && go test ./internal/httpapi ./cmd/api -count=1 +cd ../frontend && npm run typecheck +``` + +- [ ] **Step 6: Commit.** + +```bash +git add openapi backend/internal/httpapi backend/cmd/api frontend/src/shared/api/generated +git commit -m "feat: add provider reference and profile API" +``` + +## Task 6: Add same-origin BFF routes + +**Files:** +- Create four BFF route files and matching tests listed in the file map. + +- [ ] **Step 1: Write RED reference BFF tests.** + +Prove locale/query allowlisting, generated-client invocation, exact public response parsing, correlation parity, attribution preservation, and topology-safe failure. + +- [ ] **Step 2: Write RED provider BFF tests.** + +Prove signed-out/no-token `401`, server-obtained bearer only, exact PUT object/arrays, null/unknown/duplicate/bound rejection before upstream, preserved `403`, and generic upstream/malformed/correlation `503` without origin/token/private data. + +- [ ] **Step 3: Run RED.** + +```bash +cd frontend +npm test -- src/app/api/v1/catalog/categories/route.test.ts src/app/api/v1/reference/localities/route.test.ts src/app/api/v1/reference/languages/route.test.ts src/app/api/v1/me/provider-profile/route.test.ts +``` + +- [ ] **Step 4: Implement thin generated-client BFF routes.** + +No reference route imports Clerk. Profile routes use `await auth()` and `getToken()` only. Extract shared request-ID/error helpers only after all focused tests are green and duplication is exact. + +- [ ] **Step 5: Run GREEN and full typecheck.** + +```bash +cd frontend +npm test -- src/app/api/v1/catalog/categories/route.test.ts src/app/api/v1/reference/localities/route.test.ts src/app/api/v1/reference/languages/route.test.ts src/app/api/v1/me/provider-profile/route.test.ts +npm run typecheck +``` + +- [ ] **Step 6: Commit.** + +```bash +git add frontend/src/app/api/v1/catalog frontend/src/app/api/v1/reference frontend/src/app/api/v1/me/provider-profile +git commit -m "feat: add provider profile BFF" +``` + +## Task 7: Add localized owner-only provider onboarding UI + +**Files:** +- Create provider form/page/tests. +- Modify account capability card/test to expose the onboarding link only when enabled. +- Modify three locale JSON files and message test. + +- [ ] **Step 1: Write RED page/form/i18n tests.** + +Cover protected dynamic page, provider-disabled `403`/safe state, reference loading, empty profile, all fields, accessible grouped controls, primary locality inclusion, duplicate prevention, client validation, saving lock, stale completion rejection, successful create/update, controlled error/retry, no identity/contact/coordinates in DOM, OSM attribution, pt-PT/en/es key parity, and 44×44px control classes. + +- [ ] **Step 2: Run RED.** + +```bash +cd frontend +npm test -- src/features/provider/provider-profile-form.test.tsx src/app/'[locale]'/account/provider-profile/page.test.tsx src/features/account/account-capabilities-card.test.tsx src/i18n/messages.test.ts +``` + +- [ ] **Step 3: Implement minimal UI.** + +Use only same-origin BFF fetches. Keep public source options in local state and profile mutation generation separate. Disable all dismissal/submit/toggle surfaces during save; apply a completion only when its generation still owns the rendered profile. + +- [ ] **Step 4: Run GREEN, lint, and typecheck.** + +```bash +cd frontend +npm test -- src/features/provider/provider-profile-form.test.tsx src/app/'[locale]'/account/provider-profile/page.test.tsx src/features/account/account-capabilities-card.test.tsx src/i18n/messages.test.ts +npm run lint +npm run typecheck +``` + +- [ ] **Step 5: Commit.** + +```bash +git add frontend/src/features/provider frontend/src/features/account/account-capabilities-card* frontend/src/app/'[locale]'/account/provider-profile frontend/messages frontend/src/i18n/messages.test.ts +git commit -m "feat: add provider profile onboarding" +``` + +## Task 8: Complete Slice 2 acceptance and delivery + +- [ ] **Step 1: Reapply the complete local migration ledger.** + +Start/reuse only Juntly's isolated Supabase stack and apply migrations. Verify reference seed counts/source IDs without printing DB configuration. + +- [ ] **Step 2: Run non-skipping database/race gates.** + +```bash +TEST_DATABASE_URL="$DB_URL" go test -race ./internal/reference ./internal/providers -count=1 +``` + +- [ ] **Step 3: Run complete backend gates.** + +```bash +cd backend +go test ./... +go vet ./... +go build -o "$LOCALAPPDATA/Temp/juntly-api-slice2.exe" ./cmd/api +``` + +- [ ] **Step 4: Run complete frontend/topology gates.** + +```bash +cd frontend && npm run verify +cd .. && docker compose config >/dev/null && git diff --check && git diff --cached --check +``` + +- [ ] **Step 5: Run live public reference proof.** + +Prove category/language/locality responses and radius ordering through same-origin BFF. Record only status, correlation parity, counts/slugs/source attribution, in-range ordering, and absence of coordinates/private fields. + +- [ ] **Step 6: Run real authenticated provider journey.** + +With the approved Clerk development session and process-only local clock skew only if Windows remains unsynchronized: + +```text +provider capability enabled +→ empty provider profile GET +→ strict PUT create +→ GET same owner state +→ PUT replacement +→ GET replaced state +→ fresh page render +``` + +Record only status/correlation/schema, provider type, service-mode booleans, radius, language/service-area counts, stable owner/profile timestamp properties, and no raw identity/contact/coordinate values. + +- [ ] **Step 7: Freeze and review.** + +Synchronize CodeGraph; stage the explicit Slice 2 allowlist; run fixture-aware secret/privacy scans; record binary digest/path count. Request one independent read-only contract/security review if the Codex environment is available. If unavailable for the already documented local cache/MCP reason, record that honestly and perform the repository-required direct review without claiming independence. + +- [ ] **Step 8: Commit final acceptance correction only if needed.** + +Any runtime-found defect gets a new RED test, minimal fix, affected/full gates, new digest, and local commit. Otherwise make no empty commit. + +- [ ] **Step 9: Cleanup and close.** + +Stop tracked frontend/API processes, remove temporary binaries/source downloads, verify project proof ports closed, preserve the Juntly Supabase stack only if immediately needed for the next slice, and require a clean worktree before marking Slice 2 complete. diff --git a/docs/superpowers/plans/2026-08-23-user-account-capabilities.md b/docs/superpowers/plans/2026-08-23-user-account-capabilities.md new file mode 100644 index 0000000..7289f6b --- /dev/null +++ b/docs/superpowers/plans/2026-08-23-user-account-capabilities.md @@ -0,0 +1,383 @@ +# User Account Capabilities Implementation Plan + +> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. + +**Goal:** Give every verified Juntly internal user an implicit customer capability and an explicit, durable provider capability that can be read and changed only through authenticated same-origin API paths. + +**Architecture:** Keep `internal_users` limited to the immutable Clerk-subject mapping. Add a one-to-one `user_accounts` record keyed by `internal_user_id`; it carries only the provider capability and onboarding completion timestamp. Go reconciles the verified identity before account operations, enforces strict request schemas, and owns persistence. Next.js calls the Go API only through a same-origin BFF, then renders a localized account capability card. + +**Tech Stack:** Go, Ent, project-owned Supabase PostgreSQL, OpenAPI 3.1, generated TypeScript client, Next.js App Router, Clerk server auth, Vitest, Testing Library. + +## Global Constraints + +- Do not begin code work until `clerk-authenticated-browser-proof` has passed and the mapping foundation is promoted according to branch policy. +- Customer capability is implicit for every verified internal user; it is not persisted as a mutable role. +- `providerEnabled` is the sole Slice 1 mutable capability. Admin/moderator authority, public provider data, contact values, payment fields, and marketplace profiles are out of scope. +- Persist no Clerk subject, token, email, phone, exact location, profile text, contact data, or session material in `user_accounts`. +- Browser requests remain same-origin `/api/v1/...`; `JUNTLY_API_ORIGIN` stays server-only. +- OpenAPI schemas are closed at every boundary: unknown BFF/Go request fields are rejected before service invocation. +- `internal_users` remains the authoritative Clerk-subject mapping and migration history stays forward-only. +- Every behavioral unit follows RED → GREEN → REFACTOR and is locally committed only after its focused evidence/review gate. + +--- + +## File map + +| Path | Responsibility | +|---|---| +| `supabase/migrations/_create_user_accounts.sql` | Forward-only durable one-to-one capability table. | +| `backend/ent/schema/useraccount.go` | Ent schema for the capability record. | +| `backend/ent/**` | Generated Ent model/query/mutation code. | +| `backend/internal/accounts/model.go` | Capability domain DTOs and validation errors. | +| `backend/internal/accounts/repository.go` | Repository contract and controlled errors. | +| `backend/internal/accounts/ent_repository.go` | Ent persistence adapter. | +| `backend/internal/accounts/service.go` | Identity reconciliation plus account read/update service. | +| `backend/internal/accounts/*_test.go` | Unit and disposable PostgreSQL behavior proofs. | +| `backend/internal/httpapi/account_handler.go` | Protected GET/PUT account transport and strict JSON decoding. | +| `backend/internal/httpapi/account_handler_test.go` | HTTP schema/auth/correlation tests. | +| `backend/internal/httpapi/router_test.go` | Router composition regression. | +| `backend/internal/httpapi/openapi_contract_test.go` | OpenAPI endpoint/schema regression. | +| `backend/cmd/api/main.go` | Inject account service into the router. | +| `openapi/juntly-api.v1.yaml` | Closed public account contract. | +| `frontend/src/app/api/v1/me/account/route.ts` | Clerk-aware same-origin BFF GET/PUT bridge. | +| `frontend/src/app/api/v1/me/account/route.test.ts` | BFF auth, strict body, upstream, and privacy tests. | +| `frontend/src/features/account/account-capabilities-card.tsx` | Localized interactive capability UI. | +| `frontend/src/features/account/account-capabilities-card.test.tsx` | Client UI loading/update/error behavior tests. | +| `frontend/src/app/[locale]/account/page.tsx` | Hosts the capability card after server session enforcement. | +| `frontend/src/app/[locale]/account/page.test.tsx` | Protected account page composition regression. | +| `frontend/messages/pt-PT.json`, `frontend/messages/en.json`, `frontend/messages/es.json` | Account capability UI strings. | + +## Public contract + +```yaml +GET /api/v1/me/account + 200: AccountCapabilitiesResponse + 401: ErrorResponse + 503: ErrorResponse + +PUT /api/v1/me/account + requestBody: UpdateAccountCapabilitiesRequest + 200: AccountCapabilitiesResponse + 400: ErrorResponse + 401: ErrorResponse + 503: ErrorResponse + +AccountCapabilitiesResponse: + additionalProperties: false + required: [customerEnabled, providerEnabled, onboardingCompletedAt] + properties: + customerEnabled: { type: boolean, const: true } + providerEnabled: { type: boolean } + onboardingCompletedAt: { type: string, format: date-time } + +UpdateAccountCapabilitiesRequest: + additionalProperties: false + required: [providerEnabled] + properties: + providerEnabled: { type: boolean } +``` + +`onboardingCompletedAt` is set once, on the first successful GET or PUT that creates the account record, and remains stable. The account record is created lazily and idempotently after the identity is reconciled. No caller can provide an internal-user ID or Clerk subject. + +## Task 0: Foundation gate + +**Objective:** Establish the identity prerequisite before any Slice 1 source change. + +- [ ] Obtain real browser proof for `POST /api/v1/auth/reconcile` using an approved Clerk test user. +- [ ] Record only sanitized `200`, correlation parity, and opaque UUID/timestamp-shape evidence. +- [ ] Review and promote the mapping foundation through the repository branch policy. +- [ ] Create the Slice 1 feature branch from the approved `development` base in the canonical checkout; do not create a worktree or duplicate checkout. + +## Task 1: Add the durable account capability schema + +**Files:** +- Create: `supabase/migrations/_create_user_accounts.sql` +- Create: `backend/ent/schema/useraccount.go` +- Modify: `backend/ent/entc.go` +- Regenerate: `backend/ent/**` +- Create: `backend/ent/schema/useraccount_test.go` +- Modify: `backend/internal/users/migration_contract_test.go` + +**Interfaces:** + +```go +type UserAccount struct { + InternalUserID uuid.UUID + ProviderEnabled bool + OnboardingCompletedAt time.Time + CreatedAt time.Time + UpdatedAt time.Time +} +``` + +- [ ] **Step 1: Write failing migration/schema tests** + +Assert a `user_accounts` table with `internal_user_id uuid primary key references public.internal_users(id) on delete cascade`, `provider_enabled boolean not null default false`, non-null UTC `onboarding_completed_at`, creation/update timestamps, and no identity/contact columns. + +- [ ] **Step 2: Run RED checks** + +Run: + +```bash +cd backend && go test ./ent/schema ./internal/users -run 'Test.*UserAccount|Test.*Migration' -count=1 +``` + +Expected: failure because the `UserAccount` schema/migration does not exist. + +- [ ] **Step 3: Add the forward-only SQL migration and Ent schema** + +Use a UUID primary key relation to `internal_users`; do not add a redundant subject/email field. Set `provider_enabled` default false and immutable `internal_user_id`; use UTC time defaults. + +- [ ] **Step 4: Regenerate Ent and update the migration contract** + +Run the repository-supported Ent generator. Extend the SQL migration contract test to assert table/column/FK/default semantics rather than only migration file existence. + +- [ ] **Step 5: Run GREEN checks** + +Run the Task 1 command and confirm the schema/migration tests pass. + +- [ ] **Step 6: Commit** + +```bash +git add supabase/migrations backend/ent backend/internal/users/migration_contract_test.go +git commit -m "feat: add user account capability schema" +``` + +## Task 2: Build race-safe account service and repository + +**Files:** +- Create: `backend/internal/accounts/model.go` +- Create: `backend/internal/accounts/repository.go` +- Create: `backend/internal/accounts/ent_repository.go` +- Create: `backend/internal/accounts/service.go` +- Create: `backend/internal/accounts/service_test.go` +- Create: `backend/internal/accounts/ent_repository_test.go` + +**Interfaces:** + +```go +type Account struct { + CustomerEnabled bool + ProviderEnabled bool + OnboardingCompletedAt time.Time +} + +type Service interface { + Get(context.Context, users.VerifiedIdentity) (Account, error) + SetProviderEnabled(context.Context, users.VerifiedIdentity, bool) (Account, error) +} +``` + +- [ ] **Step 1: Write failing service tests** + +Cover: invalid verified identity; first `Get` reconciles identity and creates one account with `CustomerEnabled=true`, `ProviderEnabled=false`; first update enables provider; later update disables it; unknown repository failure maps to controlled unavailability; concurrent first reads/updates produce one account and stable onboarding timestamp. + +- [ ] **Step 2: Run RED checks** + +Run: + +```bash +cd backend && go test ./internal/accounts -run 'TestService' -count=1 +``` + +Expected: failure because `internal/accounts` does not exist. + +- [ ] **Step 3: Implement the minimal domain/repository/service layer** + +The account service first calls the existing internal-user reconciliation service using only `users.VerifiedIdentity`. The Ent repository finds or inserts a record keyed by internal-user ID; unique conflicts reload the winner. Normalize timestamps to UTC microsecond precision at the persistence boundary. + +- [ ] **Step 4: Add disposable PostgreSQL integration tests** + +Require `TEST_DATABASE_URL`; use synthetic UUID/subjects; scope cleanup to created rows; prove exact one-to-one account row and concurrent winner reload against the migration-applied local Supabase database. + +- [ ] **Step 5: Run GREEN checks** + +Run: + +```bash +cd backend && go test ./internal/accounts -count=1 +TEST_DATABASE_URL="$DB_URL" go test ./internal/accounts -run 'TestEntRepository|TestConcurrent' -count=1 +``` + +Expected: both pass; integration must not skip. + +- [ ] **Step 6: Commit** + +```bash +git add backend/internal/accounts +git commit -m "feat: add account capability service" +``` + +## Task 3: Publish the protected Go account API contract + +**Files:** +- Modify: `openapi/juntly-api.v1.yaml` +- Create: `backend/internal/httpapi/account_handler.go` +- Create: `backend/internal/httpapi/account_handler_test.go` +- Modify: `backend/internal/httpapi/health_handler.go` +- Modify: `backend/internal/httpapi/router_test.go` +- Modify: `backend/internal/httpapi/openapi_contract_test.go` +- Modify: `backend/cmd/api/main.go` + +**Interfaces:** + +```go +type AccountService interface { + Get(context.Context, users.VerifiedIdentity) (accounts.Account, error) + SetProviderEnabled(context.Context, users.VerifiedIdentity, bool) (accounts.Account, error) +} +``` + +- [ ] **Step 1: Write failing HTTP/OpenAPI tests** + +Assert: unauthenticated GET/PUT return closed JSON `401`; GET returns no identity values; PUT accepts exactly `{"providerEnabled": true|false}`; `{}`, `null`, unknown properties, non-boolean values, trailing JSON, and extra JSON values return closed JSON `400` without service invocation; all responses preserve valid/generated request IDs. + +- [ ] **Step 2: Run RED checks** + +Run: + +```bash +cd backend && go test ./internal/httpapi -run 'Test.*Account|Test.*OpenAPI' -count=1 +``` + +Expected: failure because account routes and schemas are absent. + +- [ ] **Step 3: Implement the closed API boundary** + +Add the two bearer-protected operations to OpenAPI. Use Go `json.Decoder` with `DisallowUnknownFields()` and explicit end-of-input validation. Reuse only allowlisted error envelopes; map controlled account failures to `503` and invalid payloads to a documented `400` error code added to the closed contract. + +- [ ] **Step 4: Generate and verify TypeScript client** + +Run: + +```bash +cd frontend && npm run codegen && npm run codegen:check +``` + +- [ ] **Step 5: Run GREEN checks** + +Run the Task 3 backend test command and `npm run codegen:check`; both pass. + +- [ ] **Step 6: Commit** + +```bash +git add openapi backend/internal/httpapi backend/cmd/api frontend/src/shared/api/generated +git commit -m "feat: add protected account capability API" +``` + +## Task 4: Add same-origin account BFF routes + +**Files:** +- Create: `frontend/src/app/api/v1/me/account/route.ts` +- Create: `frontend/src/app/api/v1/me/account/route.test.ts` + +- [ ] **Step 1: Write failing BFF tests** + +Cover GET and PUT signed-out responses (`401`, no upstream call); server-obtained bearer forwarding; valid response/correlation pass-through; strict browser JSON rejection before upstream; missing upstream origin, upstream errors, malformed data, and correlation mismatch mapping to topology-safe `503` without host/token text. + +- [ ] **Step 2: Run RED checks** + +Run: + +```bash +cd frontend && npm test -- src/app/api/v1/me/account/route.test.ts +``` + +Expected: failure because the account BFF route does not exist. + +- [ ] **Step 3: Implement GET and PUT BFF handlers** + +Use server-side `auth()` and `getToken()` only. Validate the browser request as an exact object with the sole `providerEnabled` boolean key before calling the generated client. Forward only the bearer and correlation ID upstream. Never expose `JUNTLY_API_ORIGIN`, Clerk details, or upstream topology. + +- [ ] **Step 4: Run GREEN checks** + +Run the Task 4 command and confirm all account BFF tests pass. + +- [ ] **Step 5: Commit** + +```bash +git add frontend/src/app/api/v1/me/account + git commit -m "feat: add same-origin account capability BFF" +``` + +## Task 5: Render localized account capability controls + +**Files:** +- Create: `frontend/src/features/account/account-capabilities-card.tsx` +- Create: `frontend/src/features/account/account-capabilities-card.test.tsx` +- Modify: `frontend/src/app/[locale]/account/page.tsx` +- Modify: `frontend/src/app/[locale]/account/page.test.tsx` +- Modify: `frontend/messages/pt-PT.json` +- Modify: `frontend/messages/en.json` +- Modify: `frontend/messages/es.json` +- Modify: `frontend/src/i18n/messages.test.ts` + +- [ ] **Step 1: Write failing component/page/i18n tests** + +Cover: loading state; fetched implicit customer capability; provider switch update; disabled controls while saving; controlled error state; no internal ID or provider token rendered; page still enforces `requireAuthenticatedUser(locale)`; exact message keys exist in all three locale files. + +- [ ] **Step 2: Run RED checks** + +Run: + +```bash +cd frontend && npm test -- src/features/account/account-capabilities-card.test.tsx src/app/[locale]/account/page.test.tsx src/i18n/messages.test.ts +``` + +Expected: failure because the capability card and messages do not exist. + +- [ ] **Step 3: Implement minimal accessible UI** + +Render an account-only card with semantic status text and one labelled provider-capability toggle. Use the same-origin BFF only; capture the local update request identity before awaiting and ignore stale completions. Keep browser-visible copy in locale JSON; do not display Clerk subject, internal UUID, email, contact data, or marketplace profile controls. + +- [ ] **Step 4: Run GREEN checks** + +Run the Task 5 command and confirm all focused tests pass. + +- [ ] **Step 5: Commit** + +```bash +git add frontend/src/features/account frontend/src/app/[locale]/account frontend/messages frontend/src/i18n/messages.test.ts +git commit -m "feat: add account capability onboarding UI" +``` + +## Task 6: Perform end-to-end Slice 1 acceptance + +**Files:** +- Modify only if evidence exposes a verified defect; otherwise none. + +- [ ] **Step 1: Apply the forward migration to disposable local Supabase** + +Use the project-owned local Supabase workflow. Keep database URLs/process credentials in environment only and never print them. + +- [ ] **Step 2: Run backend gates** + +```bash +cd backend && go test ./... && go vet ./... && go build -o "$LOCALAPPDATA/Temp/juntly-api.exe" ./cmd/api +``` + +- [ ] **Step 3: Run frontend gates** + +```bash +cd frontend && npm run verify +``` + +- [ ] **Step 4: Run topology and whitespace gates** + +```bash +cd .. && docker compose config >/dev/null && git diff --check && git diff --cached --check +``` + +- [ ] **Step 5: Run live runtime/browser proof** + +Start the Go API against disposable Supabase and start Next.js with a server-only local API origin. With an approved real Clerk session, prove browser GET and provider-capability PUT through the same-origin BFF, then repeat GET to prove durable state. Record only response status, correlation parity, `customerEnabled=true`, provider boolean state, and timestamp-shape assertions. + +- [ ] **Step 6: Freeze/review/deliver the slice** + +Synchronize CodeGraph, freeze the intended path set, scan staged paths for secrets, request a read-only contract/security review, commit the frozen candidate locally, and update the slice task only after every acceptance gate passes. + +## Risks and decisions + +- The current Clerk browser proof is operationally blocked by local session clock skew/preview-session availability. Slice 1 code must not be started until that proof is genuinely green. +- Provider capability is intentionally a narrow boolean. Public provider data, service areas, verification, listings, and contact controls begin in later slices. +- No external email/storage provider is selected in Slice 1. Notification/media adapters start only in their respective slices. diff --git a/docs/superpowers/plans/2026-08-24-contact-reveal-leads.md b/docs/superpowers/plans/2026-08-24-contact-reveal-leads.md new file mode 100644 index 0000000..3c8b66c --- /dev/null +++ b/docs/superpowers/plans/2026-08-24-contact-reveal-leads.md @@ -0,0 +1,36 @@ +# Contact Reveal and Lead Events Implementation Plan + +**Goal:** Add encrypted, provider-controlled contact reveal with durable lead events and atomic customer/day abuse limits. + +**Architecture:** Keep provider contacts in a server-only encrypted channel vault. Compose owner configuration and customer reveal services over verified durable identity, listing state, and PostgreSQL transactional rate/event storage. Public discovery types are not reused for contact disclosure. + +## Task 1: Contact vault and durable reveal state + +- [ ] Write RED crypto/config tests for 32-byte server-only AES-GCM key parsing, encryption round-trip, and malformed key failure. +- [ ] Add forward-only migration/Ent schemas for contact channels, daily limits, and reveal events with FK indexes/unique constraints. +- [ ] Build transactional repository with conditional daily limit increment and same-day idempotency. +- [ ] Prove owner/channel/event cleanup and concurrency in real PostgreSQL. + +## Task 2: Provider contact configuration and reveal services + +- [ ] Write RED service tests for provider ownership, consent/enabled state, self-reveal, active-only listing, decrypt-after-policy ordering, daily cap, and event redaction. +- [ ] Add owner configuration service and customer reveal service using verified identity reconciliation. +- [ ] Run focused/race/full backend gates and commit persistence/service slice. + +## Task 3: Contract, Go transport, and same-origin BFF + +- [ ] Add closed OpenAPI schemas/routes and generated client. +- [ ] Add authenticated Go handlers: owner channel status/config and customer reveal. +- [ ] Add same-origin BFF routes that acquire Clerk token only server-side. +- [ ] Prove public APIs never expose contact fields and commit transport slice. + +## Task 4: Private provider UI and authenticated reveal UX + +- [ ] Add provider contact configuration page/card with masked status only. +- [ ] Add authenticated reveal action on public listing detail; successful plaintext stays in component state only. +- [ ] Add localized tests, browser proof, full gates, and commit UI slice. + +## Task 5: Acceptance and integration + +- [ ] Create local synthetic provider/customer channels and reveal flow through real auth/session when available. +- [ ] Prove single event/idempotency/rate cap, public absence, cleanup, ports closed, full gates, privacy scan, commit, and local development fast-forward. diff --git a/docs/superpowers/plans/2026-08-24-listings-media-moderation.md b/docs/superpowers/plans/2026-08-24-listings-media-moderation.md new file mode 100644 index 0000000..188c2d1 --- /dev/null +++ b/docs/superpowers/plans/2026-08-24-listings-media-moderation.md @@ -0,0 +1,114 @@ +# Listings, Media, and Moderation Implementation Plan + +> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. + +**Goal:** Deliver private provider listings with transactional moderation-controlled publication and safe media upload capabilities. + +**Architecture:** Extend the existing verified identity → provider capability → Ent/Postgres authority chain with persisted moderator grants, listing lifecycle/audit tables, strict OpenAPI/Go/BFF boundaries, and localized protected owner/moderator UI. All listing state transitions use optimistic revision compare-and-set transactions; media storage is injected behind a capability-safe interface. + +**Tech Stack:** Go, Ent, PostgreSQL/Supabase, OpenAPI 3.1, generated TypeScript client, Next.js 16, Clerk server auth, Vitest. + +## Global Constraints + +- Use the canonical checkout and local commits only; do not push or mutate remotes. +- Use forward-only Supabase migrations and `go generate ./ent`. +- A `moderator` grant is persisted server-side only with a unique `(internal_user_id, role)` key; no Clerk/browser role authority. +- All new behavioral code follows observed RED → GREEN → REFACTOR. +- Public/browser contracts omit exact addresses, contact data, internal user IDs, Clerk values, storage keys, credentials, and private object URLs. +- Listings are owner-only until Slice 4; no public listing pages/search in this slice. + +--- + +### Task 1: Role and listing persistence contracts + +**Files:** +- Create: `backend/ent/schema/platformrole.go`, `backend/ent/schema/listing.go`, `backend/ent/schema/listingevent.go`, `backend/ent/schema/listingmedia.go` +- Create: `supabase/migrations/_create_listing_moderation.sql` +- Modify: `backend/ent/schema/marketplace_reference_test.go`, `backend/internal/users/migration_contract_test.go` + +- [ ] Write schema/migration tests requiring role, listing, event/media tables, lifecycle checks, unique role grant, listing owner indexes, revision check, and audit FK. +- [ ] Run targeted Go contracts; expect RED for missing schemas/migration. +- [ ] Add Ent schemas and a timestamped migration with database checks/indexes and no user/listing mock seeds. +- [ ] Run `gofmt -w ent/schema/*.go && go generate ./ent`. +- [ ] Run targeted contracts; expect GREEN. +- [ ] Apply migration to isolated local Supabase and assert table/index/check cardinality without emitting URLs or identities. +- [ ] Commit the frozen schema/migration slice. + +### Task 2: Provider listing draft service and transactional repository + +**Files:** +- Create: `backend/internal/listings/model.go`, `repository.go`, `service.go`, `ent_repository.go` +- Create: matching unit and PostgreSQL repository tests + +- [ ] Write RED tests for provider-only create/list/get/update, profile/category/locality validation, price and service-mode bounds, cross-owner absence, and create audit event. +- [ ] Implement model, authorizer port, strict validation, and Ent transaction that creates/updates listing plus event atomically. +- [ ] Write RED PostgreSQL tests for child/reference failure rollback, exact event count, revision increment, and concurrent draft update conflict. +- [ ] Implement CAS queries and canonical reload with one bounded uniqueness retry only where needed. +- [ ] Run focused unit plus non-skipping PostgreSQL/race tests GREEN. +- [ ] Commit the frozen listing-draft slice. + +### Task 3: Moderator authorization and lifecycle transitions + +**Files:** +- Create: `backend/internal/moderation/service.go`, `repository.go`, `ent_repository.go` +- Modify: `backend/internal/listings/service.go`, repository/tests + +- [ ] Write RED tests proving only persisted moderator grants can review, provider submit/pause/archive is owner-scoped, and invalid transitions do not append events. +- [ ] Implement a server-owned moderator authorizer using reconciled internal users and persisted `platform_roles`. +- [ ] Implement submit/approve/reject/pause/archive as state-and-event atomic CAS transactions with revision requirement and idempotent committed-target reads. +- [ ] Run disposable PostgreSQL concurrent approval/rejection proof: exactly one outcome/event wins. +- [ ] Commit the frozen lifecycle/moderation slice. + +### Task 4: Safe media capability boundary + +**Files:** +- Create: `backend/internal/listingmedia/model.go`, `service.go`, `storage.go`, tests +- Modify: listing persistence/repository as necessary + +- [ ] Write RED tests for owner-only upload intent, content-type/size/count limits, listing-state eligibility, and public DTO redaction of adapter object references/secrets. +- [ ] Implement injected storage adapter interface and disabled production default; only an adapter-created opaque short-lived capability may cross the owner API boundary. +- [ ] Persist pending media metadata in one listing-owned transaction and require owner authorization. +- [ ] Run unit and PostgreSQL ownership/rollback tests GREEN. +- [ ] Commit the frozen media-boundary slice. + +### Task 5: OpenAPI, Go handlers, and runtime composition + +**Files:** +- Modify: `openapi/juntly-api.v1.yaml`, `backend/cmd/api/main.go`, `backend/internal/httpapi/health_handler.go` +- Create: listing/moderation/media handlers and contract tests + +- [ ] Write RED OpenAPI and HTTP tests for every endpoint, strict request bodies, correlation parity, `401/403/409/503`, and closed owner/moderator responses. +- [ ] Add contract schemas/operations, generate TypeScript client, and implement strict Go transport/routers with verified identity middleware. +- [ ] Compose Ent repositories/services in `newAPIHandler` without startup migrations or storage credentials in logs. +- [ ] Run Go focused contracts and frontend codegen/typecheck GREEN. +- [ ] Commit the frozen transport/contract slice. + +### Task 6: Same-origin BFF routes + +**Files:** +- Create: `frontend/src/app/api/v1/me/listings/**`, `frontend/src/app/api/v1/moderation/listings/**` +- Modify: generated-client consumption tests + +- [ ] Write RED Vitest routes that require server Clerk token for protected calls, strict request/response schemas, request-ID parity, 409 preservation, and no topology/role/storage leakage. +- [ ] Implement owner/moderator BFF routes with generated clients and closed allowlisted errors. +- [ ] Run focused routes plus strict TypeScript GREEN. +- [ ] Commit the frozen BFF slice. + +### Task 7: Localized owner and moderator UI + +**Files:** +- Create: `frontend/src/features/listings/**`, `frontend/src/app/[locale]/account/listings/**`, `frontend/src/app/[locale]/moderation/listings/**` +- Modify: account navigation and `frontend/messages/{pt-PT,en,es}.json` + +- [ ] Write RED component/page/i18n tests for draft creation/editing, explicit submit/pause/archive actions, disabled stale/saving controls, media unavailable state, moderator review, and private-data absence. +- [ ] Implement mobile-first protected owner and moderator pages with server protection, localized copy, accessible controls, and optimistic revision handling. +- [ ] Run focused UI tests, full frontend verify, and production route build GREEN. +- [ ] Commit the frozen UI slice. + +### Task 8: Slice acceptance and local integration + +- [ ] Apply full migration ledger to isolated Supabase. +- [ ] Run real Postgres lifecycle/race tests, complete Go tests/vet/build, frontend verify, Compose config, CodeGraph sync, whitespace and fixture-aware privacy scans. +- [ ] Run real browser proof using approved sessions: provider draft → submit; persisted moderator review → active/rejected; owner pause/archive; no raw storage authority/private fields. +- [ ] Remove synthetic acceptance rows, stop only tracked processes, verify ports are closed, freeze/scan/commit any late fix. +- [ ] Fast-forward local `development` only after all Slice 3 evidence is green and create the next canonical feature branch. diff --git a/docs/superpowers/plans/2026-08-24-public-discovery-search.md b/docs/superpowers/plans/2026-08-24-public-discovery-search.md new file mode 100644 index 0000000..032b01d --- /dev/null +++ b/docs/superpowers/plans/2026-08-24-public-discovery-search.md @@ -0,0 +1,44 @@ +# Public Discovery and Search Implementation Plan + +> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:executing-plans or superpowers:subagent-driven-development. Steps use checkbox syntax. + +**Goal:** Publish active-only discovery and SEO-safe public listing pages with deterministic locality-aware ranking. + +**Architecture:** Add a separate public projection service/repository over active listings, extend OpenAPI and generated clients, add no-auth same-origin BFF routes, then deliver text-first localized discovery/detail pages. Owner/moderator DTOs are never reused as public DTOs. + +**Tech Stack:** Go, Ent/PostgreSQL/PostGIS, OpenAPI 3.1, generated TypeScript client, Next.js, next-intl, Vitest. + +## Global Constraints + +- No Clerk/browser authentication on public discovery routes. +- Active listing state is mandatory for every public query. +- Public DTOs omit owner IDs, contacts, coordinates, service areas, media references, audit/moderation data. +- Radius uses existing PostGIS locality centers and parameterized `ST_DWithin`. +- Organic deterministic ordering only; promotions remain deferred. + +### Task 1: Public discovery service and PostGIS repository + +- [ ] Write RED unit/PostgreSQL tests for active-only state, category/text/price/service filters, locality pairing, radius ordering, stable tie-break, and private-column absence. +- [ ] Add `backend/internal/discovery` model, service, repository, and parameterized SQL query. +- [ ] Prove all reference filters against isolated Supabase/PostGIS. +- [ ] Commit the frozen discovery service slice. + +### Task 2: OpenAPI, Go transport, and public BFF + +- [ ] Write RED OpenAPI/handler/BFF tests for public collection/detail operations, strict query keys, safe errors, and closed DTOs. +- [ ] Add discovery contract, generated client, Go handlers, and no-auth BFF routes. +- [ ] Prove no Clerk token or topology is used/exposed. +- [ ] Commit the frozen transport slice. + +### Task 3: Localized discovery and SEO-safe pages + +- [ ] Write RED component/page/i18n tests for discovery filters, text cards, active detail page, metadata redaction, and non-active not-found behavior. +- [ ] Add `/:locale/discover` and `/:locale/listings/[listingId]` pages. +- [ ] Run low-bandwidth/mobile text-first browser proof. +- [ ] Commit the frozen UI slice. + +### Task 4: Acceptance and local integration + +- [ ] Run full PostgreSQL/race/Go/frontend/Compose/CodeGraph gates. +- [ ] Create local acceptance listings through the real owner/moderator flow; prove discovery finds only active listing. +- [ ] Remove synthetic acceptance records, stop ports, freeze/scan, commit, and fast-forward local development. diff --git a/docs/superpowers/proofs/2026-08-24-contact-reveal-proof-matrix.md b/docs/superpowers/proofs/2026-08-24-contact-reveal-proof-matrix.md new file mode 100644 index 0000000..68609a6 --- /dev/null +++ b/docs/superpowers/proofs/2026-08-24-contact-reveal-proof-matrix.md @@ -0,0 +1,20 @@ +# Slice 5 Contact Reveal Proof Matrix + +**Branch:** `feature/contact-reveal-leads` +**Baseline:** `56961c8` + +| Requirement | Code/Test Evidence | Live Evidence | Status | +|---|---|---|---| +| Provider contact is server-custodied encrypted | AES-256-GCM `crypto.go`; ciphertext/nonce/key-version migration; real channel-store test | Local migration applied | Proven | +| Provider controls enabled/consent state | `provider_service.go`; strict authenticated handler/BFF tests | Authenticated provider page loaded; user confirmed configuration save works | Proven | +| Public APIs/pages contain no contact fields | Closed public discovery contracts, BFF validators, `projection_test.go` | Go public search and BFF detail returned 200 with forbidden JSON keys absent; rendered public page showed only reveal buttons | Proven | +| Customer reveal requires verified identity | Go handler/router and BFF tests require server-side Clerk bearer | Unauthenticated contact BFF compile/runtime probe returned 401 | Proven | +| Policy denial precedes decryption | `reveal_service_test.go` | N/A — unit/domain boundary | Proven | +| Lead event and same-day idempotency | `sql_store_test.go` against local PostgreSQL | Synthetic provider/customer service flow: first and repeat reveal completed; event count 1; daily count 1 | Proven | +| Daily abuse cap | Concurrent real PostgreSQL test: 11 requests → 10 successes, 1 forbidden | N/A — real DB integration proof | Proven | +| Contact plaintext remains transient client state | `contact-reveal-control.tsx` tests and UI scan reject storage/query/cookie use | No contact present in public initial HTML | Proven | +| Separate authenticated customer browser reveal | BFF, Go handler, service/store tests cover flow | Separate customer clicked phone: `200`; WhatsApp unconsented channel: generic `403`; repeat phone: `200`; aggregate `lead_events=1`, `daily_successful_count=1` | Proven | + +## Acceptance result + +Source, migrations, contracts, BFFs, provider UI, authenticated provider channel configuration, separate-customer phone reveal, generic channel denial, same-day idempotency, public runtime boundary, and cleanup are verified. No identity/session was fabricated; all temporary synthetic provider/listing/channel/event/limit state and local processes were removed. diff --git a/docs/superpowers/specs/2026-08-21-durable-internal-user-mapping-design.md b/docs/superpowers/specs/2026-08-21-durable-internal-user-mapping-design.md new file mode 100644 index 0000000..dceb9bb --- /dev/null +++ b/docs/superpowers/specs/2026-08-21-durable-internal-user-mapping-design.md @@ -0,0 +1,177 @@ +# Durable Internal User Mapping Design + +**Status:** Approved for implementation planning + +## Purpose + +Create a minimal, durable, provider-isolated mapping from a Clerk-verified session subject to one opaque internal Juntly user ID. This mapping is the required ownership primitive for later provider profiles, listings, orders, and other marketplace resources. + +## Scope + +This slice adds only: + +- project-owned Supabase PostgreSQL migration history; +- an Ent representation of one internal-user mapping; +- a Go reconciliation service and race-safe repository; +- Clerk bearer-token verification at the Go API boundary; +- an authenticated, same-origin Next.js BFF reconciliation endpoint; +- OpenAPI/client types and layered verification. + +This slice deliberately excludes profiles, display names, emails, roles, tiers, organisations, Clerk metadata writes, webhooks, listings, provider/customer ownership, payments, and marketplace data. + +## Branch and integration topology + +The implementation branch is `feature/internal-user-mapping`. + +It requires these immutable local predecessors: + +- `727f288` — health/API foundation, already integrated into local `development`. +- `e154f44` — localized Clerk identity foundation, currently on `feature/clerk-identity`. + +Before feature code starts, the mapping branch must consume both predecessors as a stacked local branch. The Clerk source must not be duplicated or recreated. The existing health endpoint remains public and unchanged. + +No remote branch update, pull request, deployment, remote Supabase migration, or Clerk resource/metadata mutation is in scope. + +## Trust and data flow + +```text +Authenticated browser + → same-origin Next.js BFF + → current Clerk bearer token (server obtained) + → Go reconciliation endpoint + → Clerk token verification + → verified Clerk subject + → reconciliation service + → Ent repository + → Supabase PostgreSQL internal_users row +``` + +### Authority rules + +1. The browser never sends a Clerk subject, user ID, role, email, profile field, or internal ID. +2. The BFF calls `await auth()` and obtains a current token only after session validation. +3. The BFF forwards the token only to server-only `JUNTLY_API_ORIGIN`. +4. Go verifies every bearer token independently through the pinned Clerk Go SDK and accepts only a valid session with an allowed authorized party. +5. The verified `sub` becomes a typed `VerifiedIdentity` value. No other Clerk claims enter the mapping domain. +6. The database contains no bearer token, session, raw provider payload, email, display name, or metadata. + +The canonical browser-facing local auth origin stays `http://localhost:4200`. + +## Database model + +A forward-only migration creates `public.internal_users`: + +```sql +id uuid primary key, +clerk_subject text not null unique, +created_at timestamptz not null, +updated_at timestamptz not null +``` + +Rules: + +- `id` is generated by trusted application code as a UUID; it is opaque and not caller-controlled. +- `clerk_subject` is a nonempty, bounded Clerk `sub`, unique and immutable after insertion. +- `created_at` is immutable. +- `updated_at` is application-owned; this slice does not synchronise a user profile. +- A subject lookup is indexed through the unique constraint. +- SQL under `supabase/migrations/` is the authoritative, ordered history. Ent schema/code must match it exactly. +- The API must not run automatic schema creation or migration at startup. + +## Reconciliation contract + +The provider-neutral application interface is: + +```go +type VerifiedIdentity struct { + Subject string +} + +type InternalUser struct { + ID uuid.UUID + CreatedAt time.Time +} + +type Repository interface { + FindBySubject(context.Context, string) (InternalUser, bool, error) + Create(context.Context, string) (InternalUser, error) +} + +type Service interface { + Reconcile(context.Context, VerifiedIdentity) (InternalUser, bool, error) +} +``` + +Behavior: + +1. Validate an empty or oversized subject before repository access. +2. Return an existing mapping unchanged with `created=false`. +3. Create one mapping for a previously unseen verified subject with `created=true`. +4. If concurrent requests collide on unique subject creation, reload the exact winning mapping and return it with `created=false`. +5. Map unknown persistence failures to a controlled dependency failure, never a successful reconciliation. + +## HTTP contract + +OpenAPI adds the body-less endpoint: + +```text +POST /api/v1/auth/reconcile +Authorization: Bearer session-token +``` + +Responses: + +| Status | Body | Meaning | +|---|---|---| +| `200` | `{ id: UUID, createdAt: RFC3339 timestamp }` | Existing or newly created mapping. The response intentionally does not reveal the Clerk subject. | +| `401` | Closed unauthenticated error envelope | Token absent, invalid, expired, malformed, or unauthorized party. | +| `503` | Existing privacy-safe service-unavailable envelope | Database or dependency failure. | + +The BFF defines an equivalent same-origin route. It accepts no browser body and returns only the documented mapping response. It converts upstream failures, malformed data, request-ID mismatches, missing backend origin, and token-forwarding failure into an allowlisted error envelope. It never emits an upstream URL, bearer token, subject, Clerk diagnostic, database diagnostic, or stack trace. + +## Configuration + +Tracked files contain variable names and safe placeholders only. + +Server-only runtime variable names: + +```text +JUNTLY_API_ORIGIN +DATABASE_URL +CLERK_SECRET_KEY +CLERK_JWT_KEY # optional networkless verification +CLERK_AUTHORIZED_PARTIES +``` + +Credentials, database URLs, generated Supabase keys, and token material remain only in ignored local configuration or process environments. They must not be read, printed, staged, committed, or copied into test fixtures. + +## Verification requirements + +Every behavior follows RED → GREEN → REFACTOR. + +1. Migration contract test: one table, UUID ID, immutable unique subject, timestamps, and lookup constraint/index. +2. Ent schema test: generated model maps exactly to migration semantics. +3. Service tests: invalid subject rejects before repository calls, first creation, repeat idempotency, unique-conflict winner reload, and controlled persistence failure. +4. Authentication middleware tests: absent/malformed token, verifier failure, missing subject, unauthorized party, and verified-subject context propagation. Tests use synthetic fakes, never real tokens. +5. Handler/OpenAPI tests: body-less closed endpoint, `401` before service invocation, documented `200`, and privacy-safe `503`. +6. BFF tests: signed-out rejection, missing token, valid safe forwarding, malformed upstream rejection, and no upstream topology leakage. +7. Disposable local Supabase/PostgreSQL integration: apply the complete migration chain; concurrently reconcile one synthetic subject; prove one row, one stable opaque UUID, stable creation time, and no duplicate mapping. +8. Real local browser proof is separate: with an approved development test user, call the same-origin BFF twice and record only HTTP status, request IDs, stable opaque-ID equality, creation-time equality, and row count. Do not record subject, email, cookie, bearer token, or database URL. + +Required local gates after implementation: + +```text +codegraph affected . +go test ./... +go vet ./... +go build ./cmd/api +npm --prefix frontend run verify +supabase migration-chain validation against disposable local state +docker compose config +git diff --check +staged/untracked secret scan +``` + +## Non-goals and follow-on boundary + +A successful mapping only proves durable identity reconciliation. It does not prove an authenticated browser session until a real approved test-user journey runs, and it does not authorize marketplace work by itself. Provider profiles, listings, ownership edges, payments, and entitlements start only in later approved slices after this mapping is durable and verified. diff --git a/docs/superpowers/specs/2026-08-23-juntly-mvp-vertical-slices-design.md b/docs/superpowers/specs/2026-08-23-juntly-mvp-vertical-slices-design.md new file mode 100644 index 0000000..0a165fd --- /dev/null +++ b/docs/superpowers/specs/2026-08-23-juntly-mvp-vertical-slices-design.md @@ -0,0 +1,136 @@ +# Juntly MVP Vertical Slices Design + +**Status:** Approved by SourceSensei on 2026-08-23 + +## Goal + +Deliver Juntly's Portugal-first marketplace MVP as twelve independently testable vertical slices after the Clerk-to-internal-user mapping foundation is proven in a real browser session. + +## Scope and non-goals + +Launch scope includes privacy-safe customer/provider accounts, configurable provider supply, public discovery, authenticated contact, chat, quotations, bookings, reviews, promotions, subscriptions, moderation, notifications, analytics, and launch readiness. + +This design excludes protected marketplace payments, payouts, refunds, payment dispute automation, MB WAY-compatible payments, advanced verification/analytics, native apps, and AI matching. Those are post-validation work and must not delay discovery, direct contact, chat, or quotations. + +## Foundation gate + +No marketplace-owned resource may be created before the existing identity foundation is proven end to end: + +```text +approved Clerk browser session +→ same-origin Next.js BFF +→ server-side Clerk token retrieval +→ Go verification and authorized-party enforcement +→ durable PostgreSQL internal-user reconciliation +``` + +The proof returns only status, correlation parity, and response-shape assertions; it never reveals a token, Clerk subject, or opaque internal-user UUID. + +## Shared architecture + +Every slice follows the same authority chain: + +```text +browser +→ localized Next.js Server/Client Components +→ same-origin BFF route +→ generated OpenAPI TypeScript client +→ Go REST transport +→ application service +→ Ent repository +→ project-owned Supabase PostgreSQL/PostGIS +``` + +- Clerk is identity authority; Go owns marketplace roles, ownership, state transitions, moderation, and persistence. +- Browser/UI state is never authorization. +- OpenAPI remains the public contract authority; generated TypeScript is regenerated for each contract update. +- SQL migrations are forward-only and API startup never migrates production schema. +- Every persisted lifecycle uses a unique owner relation, transactionally written audit event where appropriate, and compare-and-set transitions. +- Every browser-facing upstream failure is normalized to an allowlisted privacy-safe error response with correlation ID. + +## Cross-cutting requirements + +- Default locale is `pt-PT`; English ships; Spanish remains structurally supported. +- Public pages expose approximate location only and never raw contact details, exact addresses, private messages, proposals, booking data, or credentials. +- Categories, locations, currencies, fees, plans, promotion periods, languages, and feature flags are administrator-configured rather than hardcoded in UI components. +- Every user-owned resource has durable server-side ownership checks. +- All significant behavioral changes use RED → GREEN → REFACTOR with focused, full, integration, and browser evidence. +- Each slice is implemented in the canonical checkout by one writer, reviewed against a frozen candidate, committed locally, then integrated through `development` according to repository policy. + +## Delivery slices + +### Slice 1 — Onboarding and account capabilities + +Extend verified internal users with customer/provider/both capability state and privacy-safe onboarding data. It establishes server-owned role/capability checks and account settings without collecting payment, identity-document, or public contact data. + +**Acceptance evidence:** a verified internal user can complete safe onboarding; unauthorized users cannot read or mutate account state; locale-aware account routes work in browser and API tests. + +### Slice 2 — Taxonomy, locations, and provider profiles + +Add administrator-managed service categories/subcategories, Portugal-ready locality hierarchy, approximate provider location, service areas, travel radius, and provider profile fields. + +**Acceptance evidence:** administrators manage taxonomy; providers manage only their own profile/service areas; public responses omit precise address and private contact data; PostGIS/radius behavior is proven against disposable PostgreSQL. + +### Slice 3 — Listings, media, and moderation + +Add provider-owned listing drafts, review states, publishing/pausing/archiving, pricing/service-mode fields, and a safe storage adapter boundary for media. + +**Acceptance evidence:** invalid lifecycle transitions and cross-provider edits fail closed; draft/published/moderation visibility is proven; upload metadata never exposes private storage authority. + +### Slice 4 — Public discovery and search + +Add SEO-safe public listing/provider pages and proximity-aware discovery by category, text, approximate location, radius, language, availability, and price type. + +**Acceptance evidence:** low-bandwidth/mobile browser journey finds relevant nearby published listings; private contact/location fields are absent from public HTML and metadata; sponsored ranking cannot override relevance/trust completely. + +### Slice 5 — Contact reveal and lead events + +Add authenticated, provider-controlled phone/WhatsApp reveal with abuse controls, rate limiting, consent/configuration checks, and durable lead events. + +**Acceptance evidence:** public responses never contain contact values; eligible authenticated requests create one authorized lead event; repeated/abusive requests are controlled; direct external arrangements remain commission-free. + +### Slice 6 — Internal chat and notifications + +Add participant-authorized conversations/messages, blocking/reporting, bounded attachment metadata, unread state, and in-app/email notification adapters. + +**Acceptance evidence:** only conversation participants and authorized moderators access data; messages and reports remain private; notification preferences are honored; sensitive message/attachment contents are excluded from logs. + +### Slice 7 — Quotation requests and proposals + +Add quotation requests, provider eligibility/matching, private proposals, customer comparison, acceptance/rejection/expiry, and relevant notifications. + +**Acceptance evidence:** competitors cannot retrieve each other's proposals; matching is based on server-owned status/category/location/availability; one accepted proposal has one durable outcome. + +### Slice 8 — Booking state machine + +Add bookings from listings, accepted proposals, or direct agreements with server-authoritative transitions, idempotency, cancellation, and dispute foundations. + +**Acceptance evidence:** transitions are compare-and-set and audit-backed; duplicates do not create duplicate bookings/events; private service locations remain inaccessible to non-participants. + +### Slice 9 — Reviews and reputation + +Add reviews only for completed eligible bookings, provider responses, moderation, and rating aggregates. + +**Acceptance evidence:** self-reviews, duplicate booking reviews, and reviews without eligible interaction are rejected; verified-booking status is derived from durable booking state. + +### Slice 10 — Promotions and subscriptions + +Add administrator-configured professional plans and promotion windows behind server-owned entitlements. Essential marketplace participation remains free. + +**Acceptance evidence:** promotions are labelled and bounded; subscription checks do not grant admin or ownership; fee/plan literals are configuration-driven. + +### Slice 11 — Administration, moderation, and analytics + +Add administrator role enforcement, moderation workflows, audit records, notification preferences, and consent-aware operational metrics. + +**Acceptance evidence:** privileged operations are server-authorized and audited; dashboards expose bounded aggregate data; sensitive data never reaches public analytics or logs. + +### Slice 12 — Launch hardening and operations + +Complete privacy/GDPR controls, accessibility, performance, backup/restore, monitoring, incident handling, Docker/staging deployment, and real end-to-end acceptance journeys. + +**Acceptance evidence:** relevant security, accessibility, mobile/slow-network, migration, backup/restore, observability, Docker, HTTP, and browser gates pass; the same immutable artifact is promoted through `development → staging → main` only with owner approval. + +## Completion policy + +Each slice may finish only after its own contract matrix, focused tests, disposable PostgreSQL proof where persistence changes, full project gates, frozen review, and real runtime/browser evidence pass. A later slice cannot be used to claim an earlier slice is complete. diff --git a/docs/superpowers/specs/2026-08-23-taxonomy-locations-provider-profiles-design.md b/docs/superpowers/specs/2026-08-23-taxonomy-locations-provider-profiles-design.md new file mode 100644 index 0000000..cbc8529 --- /dev/null +++ b/docs/superpowers/specs/2026-08-23-taxonomy-locations-provider-profiles-design.md @@ -0,0 +1,353 @@ +# Taxonomy, Locations, and Provider Profiles Design + +**Status:** Approved by SourceSensei on 2026-08-23 + +## Goal + +Deliver Juntly Slice 2 as a privacy-safe supply foundation: database-configurable service taxonomy, a source-verifiable Portugal launch-area location hierarchy with PostGIS radius behavior, and owner-only provider profiles managed through the existing authenticated BFF → Go → PostgreSQL authority chain. + +## Scope decisions + +1. Taxonomy is database-configurable and product-readable in Slice 2. Taxonomy administration APIs and UI are deferred to Slice 11, where platform-admin authority is introduced. +2. Location reference data includes a source-verified launch-area subset rather than a schema-only delivery or full Portugal import. +3. Provider profiles remain owner-only until Slice 4 defines and tests a separate public projection. +4. No temporary Clerk metadata, operator secret, paid entitlement, or provider capability is treated as platform-admin authority. + +## Authority and request path + +```text +browser +→ localized Next.js UI +→ same-origin Next.js BFF +→ generated OpenAPI TypeScript client +→ Go REST transport +→ taxonomy / locations / providers application services +→ Ent and reviewed parameterized PostGIS repositories +→ project-owned Supabase PostgreSQL/PostGIS +``` + +Clerk remains identity authority. Go resolves the verified Clerk subject to the opaque internal user, checks `providerEnabled`, enforces ownership, validates every referenced category/location/language, and owns transactions. Browser state and hidden controls are presentation only. + +## Taxonomy model + +### `service_categories` + +- `id uuid primary key` +- `parent_id uuid null references service_categories(id)` +- `slug text not null unique` +- `active boolean not null default true` +- `sort_order integer not null` +- `created_at timestamptz not null` +- `updated_at timestamptz not null` + +Rules: + +- Only two levels are permitted: category and subcategory. +- A category cannot parent itself. +- Inactive records remain durable but are omitted from new provider/listing choices. +- Slugs are stable lookup identities and are never translated. + +### `service_category_translations` + +- Composite primary key: `(category_id, locale)` +- `category_id uuid references service_categories(id) on delete cascade` +- `locale text` restricted initially to `pt-PT`, `en`, and `es` +- `name text not null` +- `description text null` + +Every seeded active category has all three locale rows. API output uses the requested supported locale and fails closed rather than mixing locales silently. + +### Initial category seed + +The initial database seed uses product-approved service concepts, not component constants: + +- `home-repairs`: plumbing, electrical work, construction, small repairs +- `home-and-garden`: cleaning, gardening +- `rural-and-transport`: agricultural assistance, transport +- `care-and-learning`: elderly assistance, animal care, private lessons +- `food-and-technology`: meal preparation, computer repair + +Seed IDs and ordering are deterministic. Later changes are forward migrations until Slice 11 provides server-authorized administration. + +## Language reference model + +### `spoken_languages` + +- `code text primary key` using stable BCP 47 codes +- `active boolean not null default true` +- `sort_order integer not null` + +### `spoken_language_translations` + +- Composite primary key: `(language_code, locale)` +- Localized display name in `pt-PT`, `en`, and `es` + +The initial active values are `pt-PT`, `en`, and `es`. Provider code validates against active database records; UI code does not own the vocabulary. + +## Location model + +### Administrative hierarchy + +`administrative_areas` stores: + +- opaque UUID +- `source` (`caop` for the initial hierarchy) +- `source_version` (`2025`) +- stable external administrative code +- `kind`: country, district, municipality, or parish +- official name +- nullable parent UUID +- active flag +- timestamps + +The unique identity is `(source, external_code)`. Parent-kind transitions are closed: + +```text +country → district → municipality → parish +``` + +DGT describes CAOP as Portugal's official administrative-boundary record, maintains it, publishes CAOP 2025 as the current version, provides a GeoPackage download, and publishes a PostgreSQL/PostGIS-adapted conceptual model.[1] INE independently lists the relevant Idanha-a-Nova municipal/parish entities, including Penha Garcia, Monsanto and Idanha-a-Velha, and Zebreira and Segura.[2] + +### Search localities + +`localities` stores: + +- opaque UUID +- stable slug +- display name +- parent parish UUID +- PostGIS `geography(Point, 4326)` reference center +- source name +- source element identifier +- source version/retrieval date +- active flag +- timestamps + +Initial localities are: + +- Castelo Branco +- Idanha-a-Nova +- Zebreira +- Penha Garcia +- Monsanto + +The containing administrative hierarchy comes from CAOP 2025. Locality center points are a reviewed one-time OpenStreetMap extraction because villages/towns are place features rather than CAOP administrative boundaries. The import tool uses the public Nominatim service only during development, serially at no more than one request per second, with an identifying User-Agent, and caches the reviewed result; it is never a product runtime dependency.[3] Committed provenance retains OSM type/element ID and the UI/reference API includes OpenStreetMap attribution because OSM data is ODbL-licensed and requires credit.[4] + +No downloaded national archive, Nominatim response dump, or generated temporary database is committed. The reviewed launch seed manifest is small, deterministic, source-attributed product reference data—not mock marketplace data. + +### Radius query + +The locations repository uses a reviewed parameterized SQL query: + +```text +ST_DWithin(candidate.center, origin.center, radius_meters) +ORDER BY ST_Distance(candidate.center, origin.center), candidate.id +``` + +Rules: + +- Radius is an integer from 1 to 200 kilometres at public/application boundaries. +- Only active localities participate. +- Origin must be an active locality. +- The UUID tie-breaker makes equal-distance ordering deterministic. +- Real PostgreSQL tests include at least three localities, multiple in-range results, one out-of-range result, boundary behavior, and deterministic ordering. + +## Provider profile model + +### `provider_profiles` + +- `internal_user_id uuid primary key references user_accounts(internal_user_id) on delete cascade` +- `display_name text not null`, trimmed length 2–100 +- `provider_type text not null`: individual, professional, or business +- `bio text not null`, trimmed maximum 1,000 characters +- `primary_locality_id uuid references localities(id)` +- `max_travel_distance_km integer not null`, range 0–200 +- `travels_to_customer boolean not null` +- `receives_customer boolean not null` +- `remote_services boolean not null` +- `created_at timestamptz not null` +- `updated_at timestamptz not null` + +At least one service mode is true. A zero travel radius is allowed only when a non-travel mode remains enabled. + +### `provider_service_localities` + +- Composite primary key: `(internal_user_id, locality_id)` +- Profile FK and locality FK with cascade/restrict behavior appropriate to ownership/reference data +- The list contains 1–20 unique active localities and must contain the primary locality. + +### `provider_spoken_languages` + +- Composite primary key: `(internal_user_id, language_code)` +- The list contains 1–10 unique active language codes. + +### Privacy boundary + +Slice 2 does not collect or return: + +- email, phone, WhatsApp, or preferred contact method +- exact address or provider-specific coordinates +- identity/business documents +- verification claims +- payment/subscription data +- listings, portfolio media, reviews, response statistics, or availability +- Clerk subject, token/session values, or internal-user UUID + +A provider profile has no public route or publication state in Slice 2. Slice 4 must create an explicit allowlisted public DTO and SEO/privacy tests before any provider data becomes public. + +## Provider capability and ownership + +- A verified internal user must have `providerEnabled=true` before profile GET/PUT is available. +- A provider-disabled account receives `403 FORBIDDEN` and no profile row is created. +- The caller cannot submit an owner/internal-user ID. +- GET and PUT always derive ownership from verified identity. +- Cross-user lookup and mutation are absent from the public contract. +- Concurrent first PUT operations resolve through the unique owner key and one transactional winner. + +## Application service and transaction + +The provider service consumes: + +```go +type AccountAuthorizer interface { + RequireProvider(context.Context, users.VerifiedIdentity) (users.InternalUser, error) +} +``` + +The service validates the full replacement request before opening a transaction. One transaction: + +1. loads or creates the owner profile; +2. replaces scalar profile fields; +3. replaces service-locality links; +4. replaces spoken-language links; +5. rereads and returns the canonical owner-only profile. + +Any invalid reference or failed child replacement rolls back the complete mutation. A repeat with identical input is idempotent in durable state. No raw request body is logged or persisted. + +## API contract + +### Reference reads + +```text +GET /api/v1/catalog/categories?locale=pt-PT +GET /api/v1/reference/localities?locale=pt-PT +GET /api/v1/reference/localities?locale=pt-PT&nearLocalityId=&radiusKm=<1..200> +GET /api/v1/reference/languages?locale=pt-PT +``` + +These endpoints expose only active reference rows, stable public UUIDs/slugs, localized names, hierarchy labels, distance where requested, and required OSM attribution metadata. They expose no provider/user records. + +### Owner profile + +```text +GET /api/v1/me/provider-profile +PUT /api/v1/me/provider-profile +``` + +GET returns either a closed nullable profile envelope or the current complete owner-only profile. PUT is strict full replacement and accepts exactly: + +```json +{ + "displayName": "string", + "providerType": "individual | professional | business", + "bio": "string", + "primaryLocalityId": "uuid", + "serviceLocalityIds": ["uuid"], + "maxTravelDistanceKm": 25, + "travelsToCustomer": true, + "receivesCustomer": false, + "remoteServices": false, + "languageCodes": ["pt-PT"] +} +``` + +Unknown properties, omitted required fields, explicit nulls, duplicate IDs/codes, invalid bounds, inactive/missing references, primary locality omission, and invalid service-mode combinations return correlated `400 INVALID_REQUEST` without service invocation. Authentication failures return `401`, provider-disabled access returns `403`, and dependency failures return generic `503`. + +## Same-origin BFF + +The Next.js BFF mirrors the Go/OpenAPI boundaries: + +- obtains Clerk session/token server-side; +- never accepts browser ownership/subject/token fields; +- strictly validates PUT before generated-client invocation; +- forwards bearer and request ID only; +- validates exact upstream response keys and correlation parity; +- maps topology/upstream/malformed failures to generic `503`; +- preserves `401`, `403`, and local `400` allowlisted envelopes. + +Reference endpoints are same-origin and use generated clients but require no Clerk bearer. + +## Localized UI + +Protected route: + +```text +/:locale/account/provider-profile +``` + +The account capability page links to it only when provider capability is enabled. The page remains protected server-side and owner-only. + +The mobile-first form provides: + +- short profile fields; +- database-loaded provider type copy, languages, primary locality, and service localities; +- three accessible service-mode controls; +- bounded travel-radius input; +- loading, empty, save, success, validation, and safe retry states; +- keyboard/focus support and 44×44px minimum controls; +- pt-PT and English copy with Spanish structural parity; +- visible OpenStreetMap attribution near locality reference use. + +No browser component renders or stores internal owner IDs, precise addresses, contact details, or upstream diagnostics. + +## Source import and reproducibility + +A developer-only source tool: + +1. downloads CAOP 2025 GeoPackage to an OS temporary path; +2. verifies a pinned SHA-256 recorded in the source manifest; +3. extracts only required district/municipality/parish codes and parent relations; +4. queries the five locality centers serially under the Nominatim policy; +5. writes a deterministic reviewed manifest containing only approved names, codes, points, source IDs, versions, retrieval dates, and attribution; +6. deletes downloaded/temp artifacts after verification. + +The application and tests never call Nominatim or download CAOP at runtime. Updating source versions requires a reviewed forward migration and regenerated manifest. + +## Error and logging policy + +- Public errors contain only allowlisted code/message/request ID. +- Logs may contain route, status, duration, operation, and correlation ID. +- Logs must not include profile biography, display name, token, subject, internal user ID, exact input arrays, database URL, source response bodies, or raw coordinates tied to a user. +- Reference import failures report source stage and count only, never downloaded payload fragments. + +## TDD and acceptance evidence + +Every task follows RED → GREEN → REFACTOR. Slice completion requires: + +1. migration/schema contracts and complete forward migration chain; +2. real PostgreSQL/PostGIS hierarchy and radius tests with multiple localities; +3. provider-disabled and cross-owner negative tests; +4. transactional profile create/update/child-replacement/concurrency proof; +5. strict Go and BFF contract matrices for unknown/null/duplicate/bound violations; +6. OpenAPI regeneration and drift check; +7. localized component/page/i18n tests; +8. full Go test/race/vet/build and frontend verify gates; +9. Compose config and whitespace/secret/privacy scans; +10. live reference HTTP evidence; +11. real approved Clerk browser profile create → read → replace → read proof showing only status, correlation parity, response shape, service-area/language counts, capability state, and stable owner identity without emitting values; +12. frozen candidate review and local commit before the next slice. + +## Explicit deferrals + +- Taxonomy mutation APIs/UI and platform-admin role: Slice 11. +- Public provider DTO/pages and SEO: Slice 4. +- Listings/category ownership: Slice 3. +- Contact methods/hours and contact reveal: Slice 5. +- Verification, documents, moderation UI, analytics, subscriptions, promotions, and payments: later approved slices. + +## Sources + +[1] https://www.dgterritorio.gov.pt/atividades/cartografia/cartografia-tematica/caop — Carta Administrativa Oficial de Portugal — DGT +[2] https://www.ine.pt/ngt_server/attachfileu.jsp?look_parentBoui=456019385&att_display=n&att_download=y — 2025 Entidades do Setor Institucional das Administrações Públicas — INE +[3] https://operations.osmfoundation.org/policies/nominatim — Nominatim Usage Policy — OpenStreetMap Foundation +[4] https://www.openstreetmap.org/copyright — Copyright and License — OpenStreetMap diff --git a/docs/superpowers/specs/2026-08-24-contact-reveal-leads-design.md b/docs/superpowers/specs/2026-08-24-contact-reveal-leads-design.md new file mode 100644 index 0000000..82c252c --- /dev/null +++ b/docs/superpowers/specs/2026-08-24-contact-reveal-leads-design.md @@ -0,0 +1,128 @@ +# Contact Reveal and Lead Events Design + +**Status:** Approved under SourceSensei’s standing implementation authorization on 2026-08-24. + +## Goal + +Deliver Slice 5: an authenticated customer can reveal a provider-controlled phone or WhatsApp contact channel for an active listing. Every successful reveal is rate-limited and creates a durable lead event. Public listing/discovery responses remain permanently contact-free. + +## Policy vocabulary + +| Concept | Values / source of truth | +|---|---| +| Viewer identity | Verified Clerk identity reconciled to immutable internal user ID | +| Listing eligibility | Durable `listings.state = active` only | +| Channel | `phone`, `whatsapp` | +| Provider control | Per-channel enabled flag + explicit reveal consent stored server-side | +| Contact custody | Server-only encrypted ciphertext, nonce, key version; never profile/public DTOs | +| Lead event | Customer/listing/provider/channel/timestamp only; never plaintext contact | +| Abuse limit | Maximum 10 successful reveals per customer per UTC day; at most one event per customer/listing/channel/UTC day | + +No subscription tier, provider metadata, browser role, client-supplied internal ID, Clerk subject, or UI state grants reveal authority. + +## Storage + +### `provider_contact_channels` + +One row per `(internal_user_id, channel)` with: + +```text +internal_user_id +channel +ciphertext +nonce +key_version +enabled +reveal_consent +created_at +updated_at +``` + +Contact values use AES-256-GCM. The 32-byte key is decoded only from server-side `JUNTLY_CONTACT_ENCRYPTION_KEY`; configuration fails closed when the key is missing/malformed. Ciphertext and nonce are never returned by any HTTP/BFF/UI route or logged. + +### `contact_reveal_daily_limits` + +```text +customer_internal_user_id +utc_day +successful_count +``` + +A conditional UPSERT increments only below 10 in one transaction. This provides durable, race-safe UTC-day rate enforcement without a browser or Redis authority. + +### `contact_reveal_events` + +```text +id +customer_internal_user_id +provider_internal_user_id +listing_id +channel +utc_day +revealed_at +``` + +Unique `(customer_internal_user_id, listing_id, channel, utc_day)` makes repeated same-channel reveals idempotent per UTC day: return the same allowed contact but create no duplicate lead event/count increment. + +## Reveal transaction + +```text +verified Clerk identity +→ reconcile durable customer ID +→ reject self-reveal +→ load active listing and owner +→ load enabled+consented provider channel +→ acquire customer/day limit atomically +→ check same customer/listing/channel/day event +→ decrypt contact only after all authorization/rate checks +→ append event + increment daily count in one transaction +→ return channel + contact to authenticated caller +``` + +For an existing same-day idempotent event, decrypt/return the channel but do not increment or insert another event. Invalid listing, inactive listing, absent/disabled/unconsented channel, self-reveal, or exhausted rate limit returns a generic permission-safe error and never decrypts. + +## APIs and BFFs + +Owner-only configuration: + +```text +GET/PUT /api/v1/me/contact-channels +``` + +Uses verified identity → provider capability → durable owner ID. PUT is closed, bounded, validates contact format per channel, and accepts explicit `enabled` and `revealConsent` booleans. It never returns contact plaintext after write; GET returns channel statuses only. + +Authenticated customer reveal: + +```text +POST /api/v1/listings/{listingId}/contact-reveals +{ "channel": "phone" | "whatsapp" } +``` + +Browser path remains same-origin: + +```text +POST /api/v1/listings/{listingId}/contact-reveals +``` + +The BFF obtains Clerk bearer server-side. Public discovery/detail APIs stay unauthenticated and contractually exclude all contact fields. + +## Errors + +- `401 UNAUTHORIZED`: no verified identity. +- `403 FORBIDDEN`: authenticated but non-eligible, self-reveal, unconsented/disabled channel, or rate limit reached. No reason distinguishes provider settings from anti-abuse policy. +- `404 NOT_FOUND`: non-public/missing listing. +- `400 INVALID_REQUEST`: malformed closed body/channel/config request. +- `503 SERVICE_UNAVAILABLE`: vault/persistence dependency/configuration unavailable. + +## UI + +1. Provider account adds a private contact-channel configuration card with enabled/consent state and masked status only. +2. Public detail retains no contact value in HTML. A contact reveal control is a follow-up authenticated client action; signed-out users are sent to sign-in, not given a public fallback contact value. +3. Successful reveal is rendered only in the authenticated viewer session and never inserted into page metadata, query strings, local storage, or public cache. + +## Acceptance + +1. RED→GREEN unit/PostgreSQL proofs cover encryption round-trip, malformed key fail-closed, disabled/consent/self/non-active denial before decrypt, idempotent same-day event, 10/day cap under concurrency, and no plaintext in event rows. +2. OpenAPI/Go/BFF tests prove closed bodies, server-side bearer acquisition, private contact status owner-only, public contract redaction, and generic forbidden errors. +3. Browser proof saves a provider channel, reveals it as a different authenticated customer, proves one lead event, repeats without duplicate event, and confirms public page/HTML has no contact value. +4. Synthetic records, decrypted values, and local processes are removed before final commit/integration. diff --git a/docs/superpowers/specs/2026-08-24-listings-media-moderation-design.md b/docs/superpowers/specs/2026-08-24-listings-media-moderation-design.md new file mode 100644 index 0000000..d0a9aae --- /dev/null +++ b/docs/superpowers/specs/2026-08-24-listings-media-moderation-design.md @@ -0,0 +1,127 @@ +# Listings, Media, and Moderation Design + +**Status:** Approved under SourceSensei’s standing implementation authorization on 2026-08-24 + +## Goal + +Deliver Slice 3: provider-owned listing drafts, auditable moderation-controlled publication, pause/archive controls, and a storage-provider-neutral media upload boundary. This slice creates supply for Slice 4 without exposing listings, provider details, contact data, or storage authority publicly. + +## Scope decisions + +1. A persisted `moderator` role is introduced only for listing review. It is assigned by direct server-side operational data; there is no browser role assignment, Clerk metadata role, administrator UI, or general platform administration before Slice 11. +2. Every listing starts `draft`. Providers may submit and pause/archive only their own listings. Only an authorized moderator may transition a `pending_review` listing to `active` or `rejected`. +3. Listings stay owner-only in Slice 3. Slice 4 must define the public projection and search visibility explicitly. +4. Media storage is an adapter boundary. Browser responses may contain an opaque, short-lived upload capability returned by the configured adapter, but never bucket names, storage credentials, signing keys, provider account identifiers, or private object URLs. +5. Listings use EUR minor units; no floating-point prices. Quote and negotiable listings carry no price amount; fixed/hourly/daily listings require a positive amount. + +## Authority chain + +```text +browser +→ localized Next BFF +→ generated OpenAPI client +→ Go verified identity / moderator gate +→ listing application service +→ Ent transaction + Postgres audit event +→ project-owned Supabase PostgreSQL +``` + +Go derives owner and moderator authority from the verified identity and persisted records. Browser input never supplies owner IDs, role claims, review state, storage keys, or audit actors. + +## Persisted model + +### `platform_roles` + +- opaque UUID primary key +- `internal_user_id uuid references internal_users(id)` +- `role text` restricted initially to `moderator` +- `granted_at timestamptz` +- unique `(internal_user_id, role)` + +Slice 3 exposes no role mutation API. Operations may seed/grant a moderator through audited server-side data until Slice 11 owns broader role administration. + +### `listings` + +- opaque UUID primary key +- `internal_user_id` owner FK +- `category_id` active category FK +- `primary_locality_id` active locality FK +- title 2–140 characters; description 20–4,000 characters +- `price_type`: `fixed`, `hourly`, `daily`, `quote`, `negotiable` +- nullable `price_minor`; `currency` fixed to `EUR` +- service-mode booleans (`travels_to_customer`, `receives_customer`, `remote_services`) +- lifecycle state: `draft`, `pending_review`, `active`, `rejected`, `paused`, `archived` +- optimistic `revision`, creation/update timestamps + +A listing requires an existing provider-enabled owner profile. Its service modes must be a subset of that profile’s enabled modes. The listing locality must be one of its owner profile’s service localities. This prevents a listing from silently expanding provider reach. + +### `listing_events` + +Each create or lifecycle transition writes one normalized event in the same transaction: + +- listing ID, actor internal-user ID, event type, previous and next state, revision, timestamp; +- review rejection reason only when bounded to 500 characters; +- no raw request payload, contact value, credential, or storage response. + +### `listing_media` + +- opaque UUID primary key +- listing ID, bounded ordinal, safe content type, byte size, SHA-256 checksum, opaque adapter object reference, state (`pending_upload`, `ready`, `deleted`), timestamps + +The database never stores a storage secret. `object_reference` is not exposed to browser/public DTOs. + +## Lifecycle + +```text +draft → pending_review → active + └────→ rejected +active → paused → pending_review +active|paused|draft|rejected → archived +``` + +- Owner edits only `draft`, `rejected`, or `paused` records; an edit of rejected/paused resets it to `draft` and records an event. +- Owner submits `draft` or `rejected` to `pending_review`. +- Moderator approves/rejects only `pending_review` through a compare-and-set revision transaction. +- Owner pauses only `active`; owner archives any non-archived listing. +- Archived records are immutable. +- A repeated target transition returns the committed listing without duplicating the event; incompatible or cross-owner transition fails closed. + +## API and UI boundary + +Owner endpoints: + +```text +GET /api/v1/me/listings +POST /api/v1/me/listings +GET /api/v1/me/listings/{listingId} +PUT /api/v1/me/listings/{listingId} +POST /api/v1/me/listings/{listingId}/submit +POST /api/v1/me/listings/{listingId}/pause +POST /api/v1/me/listings/{listingId}/archive +POST /api/v1/me/listings/{listingId}/media/upload-intents +``` + +Moderator endpoints: + +```text +GET /api/v1/moderation/listings?state=pending_review +POST /api/v1/moderation/listings/{listingId}/approve +POST /api/v1/moderation/listings/{listingId}/reject +``` + +All mutations require the expected `revision` in the strict JSON body. Unknown properties, nulls, second JSON values, bounds failures, unauthorized roles, and lifecycle conflicts fail closed with correlated allowlisted errors. The BFF validates exact response shapes and keeps Go origin/bearer credentials server-side. + +Localized owner UI is `/[locale]/account/listings`; moderator review is a protected, non-discoverable internal route pending Slice 11 navigation work. Neither is publicly indexable in Slice 3. + +## Acceptance evidence + +1. migration, Ent-schema, lifecycle, and role contract tests go RED then GREEN; +2. real PostgreSQL proves atomic state/event writes, CAS rejection, idempotent retries, owner isolation, and concurrent moderation only yields one outcome; +3. media adapter tests prove no storage secret/object reference reaches API/BFF DTOs; +4. OpenAPI generation, Go handler, BFF, and localized UI tests pass; +5. browser proof shows provider draft → submit and authorized moderator approve/reject with only sanitized state/count/correlation evidence; +6. full Go race/vet/build, frontend verify, Compose, source/privacy scans, frozen review, local commit, and cleanup pass. + +## Explicit deferrals + +Public discovery/SEO, contact reveal, chat, quotations, bookings, reviews, paid promotion, full administration UI, generic role management, actual production storage provider configuration, and payments remain in their assigned later slices. diff --git a/docs/superpowers/specs/2026-08-24-public-discovery-search-design.md b/docs/superpowers/specs/2026-08-24-public-discovery-search-design.md new file mode 100644 index 0000000..7f27511 --- /dev/null +++ b/docs/superpowers/specs/2026-08-24-public-discovery-search-design.md @@ -0,0 +1,77 @@ +# Public Discovery and Search Design + +**Status:** Approved under SourceSensei’s standing implementation authorization on 2026-08-24 + +## Goal + +Deliver Slice 4: active-listing-only public discovery and SEO-safe listing pages for the Portugal launch area without exposing owner identity, contact data, exact provider location, moderation records, storage metadata, or private lifecycle history. + +## Scope decisions + +1. Public projection reads only `active` listings. Draft, pending-review, rejected, paused, and archived rows are indistinguishable from absent rows. +2. Public locality is an approximate named launch locality, not a raw coordinate, address, provider service area list, or distance-to-provider claim. +3. Discovery is database-backed and fixed-query: category, text, locality radius, price type, and service-mode filters. Availability is explicitly deferred because it has no durable data model yet. +4. Public pages expose provider display name/type only when a separate allowlisted provider projection is joined from an active listing; no profile page is introduced until the page contract is proved. +5. No sponsored/promoted ranking exists in Slice 4. Organic ordering is deterministic: exact category relevance, locality/radius relevance, active recency, stable ID tie-break. + +## Public projection + +`PublicListing` contains only: + +```text +id, slug, title, description, category name/slug, +primary locality name/slug, price type, nullable price minor, currency, +service modes, provider display name/type, published/updated timestamp +``` + +The owner profile’s internal UUID, service localities, languages, biography, contact methods, media object references, listing events, review reason, created draft timestamp, and any coordinates remain excluded. + +## Discovery contract + +```text +GET /api/v1/discovery/listings?locale=pt-PT + &categoryId= + &q=<2..80 chars> + &nearLocalityId=&radiusKm=<1..200> + &priceType=fixed|hourly|daily|quote|negotiable + &serviceMode=travels_to_customer|receives_customer|remote_services + +GET /api/v1/public/listings/{listingId} +``` + +All filters are optional but exact-key allowlisted. Paired locality/radius values are mandatory together. Query text is whitespace-normalized, parameterized, and never injected into SQL identifiers. + +## Repository and ordering + +The repository joins `listings`, active categories/localities, and provider profiles. Radius uses the existing reviewed PostGIS locality center semantics: + +```text +ST_DWithin(listing_locality.center, origin.center, radiusKm * 1000) +ORDER BY category relevance, radius distance, listing.updated_at DESC, listing.id +``` + +The query never selects provider coordinate/address columns. `active` state is a mandatory predicate, not a UI convention. + +## SEO and UI + +Public route: + +```text +/:locale/listings/[listingId] +``` + +Discovery route: + +```text +/:locale/discover +``` + +Metadata uses listing title, category/locality names, and a plain description. It includes no contact values, internal identifiers, precise location, review/moderation history, or non-active listing content. Low-bandwidth view uses text-first cards and no mandatory media. + +## Acceptance + +1. RED→GREEN database tests prove inactive-state exclusion, category/text/filter semantics, radius ordering, deterministic ties, and no private selected columns. +2. Go/OpenAPI/BFF tests prove strict query allowlists, safe `400/503`, and public DTO redaction. +3. Public routes/BFF do not use Clerk tokens. +4. Browser proof finds an active nearby listing and proves draft/pending/rejected/paused/archived routes return no public content. +5. Full Go/PostgreSQL/frontend/Compose/CodeGraph gates, frozen privacy scan, local commit, and cleanup pass. diff --git a/docs/superpowers/specs/2026-08-25-marketplace-ui-redesign.md b/docs/superpowers/specs/2026-08-25-marketplace-ui-redesign.md new file mode 100644 index 0000000..1d0fd7d --- /dev/null +++ b/docs/superpowers/specs/2026-08-25-marketplace-ui-redesign.md @@ -0,0 +1,43 @@ +# Marketplace UI Redesign — Reference Mapping + +**Status:** Approved visual direction from SourceSensei on 2026-08-25 + +## Intent + +Translate the supplied browse-screen reference into Juntly's own visual system across public and protected marketplace surfaces. The redesign is presentation-only: locale structure, same-origin BFF calls, authorization, privacy boundaries, contact reveal behavior, and OpenAPI contracts remain unchanged. + +## Evidence and mapping + +The supplied `freelance-match-browse.png` reference is a 1440×1214 marketplace browse composition with a high-key white/cool-gray surface family and a dark plum-charcoal emphasis family. Juntly adopts the layout principles, not reference branding, content, imagery, or assets. + +| Reference cue | Juntly decision | +| --------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| Strong marketplace top bar | Shared responsive header with Juntly brand, discovery entry point, account/provider routes, and Clerk account controls. | +| Bright browse canvas | Off-white canvas, cool mist control surfaces, white cards, hairline blue-gray borders, dark plum ink. | +| Search/filter-led discovery | Search field becomes a prominent, full-width toolbar with filter-context chips. Existing fixed-query behavior remains unchanged. | +| Editorial service cards | Listing cards use a media-safe decorative category tile, compact metadata, provider/locality information, and a clear detail CTA. No object reference or private media data is introduced. | +| Rounded controls | Buttons, fields, and chips share rounded-rectangle geometry, clear selected/disabled states, 44px minimum targets, and keyboard focus rings. | +| Dense but calm browsing | Wider desktop grid; single-column mobile collapse; content-first hierarchy; no required animation. | + +## Scope + +1. Add semantic tokens and reusable CSS component classes. +2. Add shared locale-aware navigation across the app. +3. Restyle home, discovery, public detail, account, provider, listings, contact-channel, and moderation page containers plus their repeated controls/cards. +4. Preserve plain public data boundaries and all existing accessible landmarks/labels. + +## Explicit non-goals + +- No Figma/reference logo, copy, images, or assets. +- No API, database, auth, contact reveal, moderation, or persistence changes. +- No faux listing media or sample marketplace data. +- No additional filters whose server contract does not already exist. +- No motion beyond existing reduced-motion-safe microinteraction patterns. + +## Acceptance + +- Every route has a shared, keyboard-accessible marketplace navigation surface. +- Public discovery and details remain contact-free before explicit authenticated reveal. +- Existing localized strings are retained; new navigation labels are localized in pt-PT, English, and Spanish. +- Desktop and narrow mobile layouts have no horizontal overflow and retain 44px controls/focus visibility. +- Full frontend quality gate passes and runtime proof checks current routes without source/security boundary changes. diff --git a/docs/superpowers/specs/2026-09-01-marketplace-design-modernization.md b/docs/superpowers/specs/2026-09-01-marketplace-design-modernization.md new file mode 100644 index 0000000..a779b76 --- /dev/null +++ b/docs/superpowers/specs/2026-09-01-marketplace-design-modernization.md @@ -0,0 +1,53 @@ +# Juntly Marketplace Design Modernization + +**Status:** Approved for autonomous implementation by SourceSensei on 2026-09-01. + +## Design read + +Juntly is a trust-first local-services marketplace for mobile-first customers and providers in Portugal. The redesign uses a calm editorial marketplace language: off-white canvas, white and cool-mist surfaces, plum-charcoal text, one restrained berry accent, generous hierarchy, and practical responsive behavior. + +Design dials: `DESIGN_VARIANCE 6`, `MOTION_INTENSITY 3`, `VISUAL_DENSITY 5`. + +## Mode and boundaries + +This is a preserve-mode product redesign. Keep every route, navigation label, form field, API call, state transition, authentication boundary, privacy rule, and localization contract unchanged. Change presentation, hierarchy, responsive layout, and accessible interaction feedback only. + +## Visual system + +- Use Geist as the existing product family with tighter display tracking and calmer body rhythm. +- Keep the approved off-white, cool-mist, white-card, plum-charcoal, and berry system, but increase contrast and reduce the pale-blue startup feel. +- Use a consistent 14-18px soft radius system: controls 14px, cards 18px, large panels 24px. Pills are reserved for compact status metadata. +- Replace generic card stacks with grouped surfaces, asymmetric grids, stronger whitespace, and clear section headings. +- Add system dark-mode tokens while preserving the same hierarchy and single accent. +- Use real craft photography on the landing page. Remove the fake product screenshot, blurred mesh decoration, decorative scroll arrow, and decorative status dot. + +## Shared shell + +- Keep the desktop navigation under 72px with one-line labels. +- On narrow screens, prioritize brand, discovery, and primary auth/account access without overflow. +- Introduce semantic page-header, toolbar, alert, empty-state, data-list, and form-section utilities. +- Use a subtle canvas texture based on CSS color layers, not gradients that imitate AI mesh art. + +## Public marketplace + +- Landing: split editorial hero with real local-craft photography, concise CTA, API availability as functional status, and a cleaner statement section. +- Discovery: prominent search toolbar, honest filter affordances, responsive two-column results, stronger provider/location/price hierarchy, clear promoted treatment, and composed empty/error states. +- Listing detail: content-first service summary, visible metadata, stronger provider action rail, and responsive stacking. + +## Account and operations + +- Account hub: clear overview, capability grouping, and scan-friendly destination links. +- Forms: consistent labels, section grouping, check controls, action rows, and feedback states. +- Messaging: inbox-like split layout on desktop and stacked conversation flow on mobile. +- Bookings, quotations, reviews, subscriptions, moderation, and administration: consistent metric groups, state chips, action hierarchy, readable responsive cards, and horizontal containment. + +## Motion and accessibility + +- Use CSS-only transform/opacity entrance and hover feedback where it communicates hierarchy or action. +- Content remains visible without JavaScript. +- Disable entrances, hover lifts, and smooth scrolling under `prefers-reduced-motion`. +- Maintain 44px targets, visible focus, strong form contrast, keyboard access, and zero unintended horizontal overflow. + +## Verification + +Run formatting, lint, component tests, localization tests, typecheck, dependency audit, and production build. Verify landing, discovery, account shell, and representative protected surfaces at desktop and true mobile widths, including focus, reduced motion, overflow, and console errors. diff --git a/frontend/.dockerignore b/frontend/.dockerignore new file mode 100644 index 0000000..bc0010b --- /dev/null +++ b/frontend/.dockerignore @@ -0,0 +1,8 @@ +.git +.next +node_modules +coverage +.env +.env.* +!.env.example +*.log diff --git a/frontend/.env.example b/frontend/.env.example new file mode 100644 index 0000000..87fae8f --- /dev/null +++ b/frontend/.env.example @@ -0,0 +1,6 @@ +# Server-side origin used only by the Next.js BFF. Do not use a NEXT_PUBLIC_ prefix. +JUNTLY_API_ORIGIN=http://127.0.0.1:8080 + +# Clerk values belong only in ignored frontend/.env.local. +NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY= +CLERK_SECRET_KEY= diff --git a/frontend/.gitignore b/frontend/.gitignore index 5ef6a52..7b8da95 100644 --- a/frontend/.gitignore +++ b/frontend/.gitignore @@ -32,6 +32,7 @@ yarn-error.log* # env files (can opt-in for committing if needed) .env* +!.env.example # vercel .vercel diff --git a/frontend/.prettierignore b/frontend/.prettierignore index b322dee..e8652f7 100644 --- a/frontend/.prettierignore +++ b/frontend/.prettierignore @@ -8,3 +8,4 @@ next-env.d.ts package-lock.json AGENTS.md CLAUDE.md +src/shared/api/generated/ diff --git a/frontend/Dockerfile b/frontend/Dockerfile new file mode 100644 index 0000000..4bfaa27 --- /dev/null +++ b/frontend/Dockerfile @@ -0,0 +1,24 @@ +FROM node:24.13.1-alpine AS build + +WORKDIR /app + +COPY package.json package-lock.json ./ +RUN npm ci + +COPY . ./ +RUN npm run build + +FROM node:24.13.1-alpine AS runtime + +ENV NODE_ENV=production +WORKDIR /app + +COPY package.json package-lock.json ./ +RUN npm ci --omit=dev --ignore-scripts +COPY --from=build /app/.next ./.next + +USER node +EXPOSE 3000 +HEALTHCHECK --interval=15s --timeout=3s --start-period=15s --retries=5 CMD wget -q -O /dev/null http://127.0.0.1:3000/pt-PT || exit 1 + +CMD ["npm", "run", "start", "--", "--hostname", "0.0.0.0", "--port", "3000"] diff --git a/frontend/README.md b/frontend/README.md index 3d6aa5e..67d8aca 100644 --- a/frontend/README.md +++ b/frontend/README.md @@ -4,14 +4,18 @@ Localized, mobile-first Next.js shell for Juntly. ## Current scope -This bootstrap intentionally provides only: +This foundation currently provides: - pt-PT default routing, English support, and Spanish-ready translations. - Localized metadata and route boundaries. - A responsive, accessible product-introduction shell. +- A same-origin `/api/v1/health` BFF route backed by the generated OpenAPI client. +- A same-origin, Clerk-aware `POST /api/v1/auth/reconcile` BFF route that returns only an opaque internal mapping ID and creation time. +- A localized client-side health indicator. +- Clerk account entry, locale-aware in-app authentication forms, and a fail-closed session boundary at `/:locale/account`. - Test, format, lint, type, build, dependency-audit, and CI foundations. -Authentication, listings, search, chat, quotations, bookings, payments, the Go API, and external infrastructure are not implemented yet. +Provider/customer profiles, listings, search, chat, quotations, bookings, payments, and external infrastructure are not implemented yet. The Go API also has a protected internal-user reconciliation endpoint; authenticated browser proof requires an approved development test user and is recorded separately. ## Requirements @@ -27,13 +31,48 @@ npm test npm run verify ``` +The OpenAPI artifact is checked with: + +```bash +npm run codegen:check +``` + +## Local Clerk configuration + +The Clerk CLI links the local checkout and writes credentials only to ignored +`frontend/.env.local`. Copy `frontend/.env.example` only when configuring a +separate development environment; never commit real key values. The app uses +first-party `/:locale/sign-in` and `/:locale/sign-up` routes, and the protected +`/:locale/account` route independently verifies the server-side session. + +The source-level integration and signed-out runtime route behavior pass local +verification. Use `localhost` consistently for the browser and Next.js bind +hostname: mixing it with `127.0.0.1` can make Clerk continuation rewrites look +external and recurse through Next's proxy. An authenticated browser session still +requires a real test user; a successful build alone is not auth proof. + For a production runtime probe: ```bash npm run build -npm run start -- --hostname 127.0.0.1 --port 4200 +npm run start:local +``` + +Then open `http://localhost:4200/`; locale routing redirects to pt-PT by default. + +For local frontend/API proof, run from the repository root: + +```bash +docker compose up --build ``` -Then open `http://127.0.0.1:4200/`; locale routing redirects to pt-PT by default. +The compose topology supplies the server-only BFF origin. The API requires +server-only `DATABASE_URL`, Clerk verification material (`CLERK_SECRET_KEY` or +`CLERK_JWT_KEY`), and exact `CLERK_AUTHORIZED_PARTIES`; copy +`backend/.env.example` to ignored backend runtime configuration or set the same +variables in the service environment. For a native frontend runtime, copy +`.env.example` to an ignored `.env.local` and start the Go API on +`127.0.0.1:8080`. Do not add a `NEXT_PUBLIC_` prefix to `JUNTLY_API_ORIGIN` or +any backend/Clerk credential variable. Project-wide architecture and product rules live in `../context/`. diff --git a/frontend/eslint.config.mjs b/frontend/eslint.config.mjs index 05e726d..3cddb2e 100644 --- a/frontend/eslint.config.mjs +++ b/frontend/eslint.config.mjs @@ -12,6 +12,7 @@ const eslintConfig = defineConfig([ "out/**", "build/**", "next-env.d.ts", + "src/shared/api/generated/**", ]), ]); diff --git a/frontend/messages/en.json b/frontend/messages/en.json index d8d0d9f..1a4c74e 100644 --- a/frontend/messages/en.json +++ b/frontend/messages/en.json @@ -1,18 +1,421 @@ { "Metadata": { - "title": "Juntly — Local services, real skills", - "description": "Find local people and services near you. Juntly brings communities, customers and trusted providers closer together." + "title": "Vila: Local services, real skills", + "description": "Find local people and services near you. Vila brings communities, customers and trusted providers closer together." }, "Landing": { "eyebrow": "Local people. Real skills.", "tagline": "Find someone who knows how.", "heading": "Local services, closer to home.", - "description": "A simple way to find people with real skills in your community — starting in rural Portugal.", - "statusLabel": "The platform is taking shape.", + "description": "A simple way to find people with real skills in your community, starting in rural Portugal.", "visionLinkLabel": "Discover the vision", + "discoverLinkLabel": "Explore services", "visionTitle": "Built for genuine local connections", - "visionDescription": "Discovery, contact and trust without taking choice away from people. Juntly connects those who need help with those who know how.", - "footerLabel": "Juntly — born in Portugal." + "visionDescription": "Discovery, contact and trust without taking choice away from people. Vila connects those who need help with those who know how.", + "showcaseTitle": "Local, made easier.", + "how": { + "title": "From need to solution, without the friction.", + "description": "Vila brings search, comparison and contact into one clear journey.", + "discover": { + "title": "Search for what you need", + "description": "Find active services by category and location, with enough context to choose your next step." + }, + "compare": { + "title": "Compare with confidence", + "description": "Review profiles, proposals, availability and feedback connected to real completed work." + }, + "contact": { + "title": "Talk when it makes sense", + "description": "Send a message or reveal contact details only when you are ready to move forward." + } + }, + "audience": { + "customer": { + "title": "For people who need help", + "description": "Discover local professionals, request proposals, book services and follow everything from your account.", + "action": "Find a service" + }, + "provider": { + "title": "For people who know how", + "description": "Build a clear profile, publish services, answer requests and grow a reputation through completed work.", + "action": "Set up a profile", + "imageAlt": "Professional working with wood in a workshop" + } + }, + "trust": { + "title": "Trust built into the product.", + "description": "The experience brings people closer without exposing more information than necessary.", + "privacy": { + "title": "Contact details stay controlled", + "description": "Contact information remains private until an authorized action reveals it." + }, + "local": { + "title": "Local context comes first", + "description": "Categories, locations and service areas keep marketplace results genuinely relevant." + }, + "reputation": { + "title": "History that means something", + "description": "Completed bookings support verified reviews and transparent provider responses." + } + }, + "closing": { + "title": "Start with your community.", + "description": "Create an account to request help, offer your skills, or follow every conversation and booking in one place.", + "action": "Create account" + }, + "footerLabel": "Vila, born in Portugal." + }, + "Auth": { + "signIn": "Sign in", + "signUp": "Create account" + }, + "Navigation": { + "skipToContent": "Skip to content", + "discover": "Find services", + "createService": "Create service", + "account": "My account", + "accountNavigation": "Account options", + "marketplace": "Marketplace navigation" + }, + "Account": { + "title": "Vila account", + "description": "Your session is active. Choose how you want to use Vila.", + "capabilities": { + "title": "How you use Vila", + "description": "Your account can find services and, if you choose, offer them too.", + "customerLabel": "Find services", + "customerDescription": "Your account is always ready to find and contact providers.", + "providerLabel": "Offer services", + "providerDescription": "Enable this option to prepare your provider profile in the next steps.", + "enabled": "Enabled", + "disabled": "Disabled", + "loading": "Loading account capabilities…", + "saving": "Saving…", + "loadError": "We could not load your account capabilities.", + "retry": "Try again", + "manageProvider": "Manage provider profile", + "manageListings": "Manage listings", + "manageMessages": "View messages", + "manageNotifications": "Manage notifications", + "manageQuotations": "Requests and proposals", + "manageBookings": "Manage bookings", + "manageReviews": "Reviews", + "manageEntitlements": "Plans and promotions", + "manageModeration": "Approve listings", + "managePayouts": "Payments and payouts", + "managePaymentsAdmin": "Manage payments and disputes" + } + }, + "ProviderProfile": { + "title": "Provider profile", + "description": "Prepare your private profile details before marketplace publication.", + "displayName": "Display name", + "providerType": "Provider type", + "individual": "Individual", + "professional": "Professional", + "business": "Business", + "bio": "Introduction", + "primaryLocality": "Primary locality", + "serviceLocalities": "Service areas", + "languages": "Languages", + "travelRadius": "Maximum travel distance in kilometres", + "serviceModes": "Service modes", + "travels": "Travels to the customer", + "receives": "Receives customers", + "remote": "Works remotely", + "save": "Save profile", + "saving": "Saving…", + "loading": "Loading profile…", + "error": "We could not load or save the profile.", + "retry": "Try again", + "saved": "Profile saved." + }, + "Listings": { + "title": "My listings", + "description": "Create and track private listings before publication.", + "newListing": "New listing", + "create": "Save draft", + "submit": "Submit for review", + "pause": "Pause", + "archive": "Archive", + "loading": "Loading listings…", + "error": "We could not load or save listings.", + "retry": "Try again", + "empty": "You do not have listings yet.", + "saved": "Draft saved.", + "titleLabel": "Title", + "descriptionLabel": "Description", + "categoryLabel": "Category", + "localityLabel": "Locality", + "priceLabel": "Price in cents" + }, + "Moderation": { + "title": "Listing review", + "loading": "Loading review…", + "empty": "There are no pending listings.", + "error": "We could not load the review queue.", + "retry": "Try again", + "approve": "Approve", + "reject": "Reject" + }, + "Discovery": { + "title": "Find services", + "description": "Browse active listings from local providers.", + "loading": "Searching for services…", + "empty": "We could not find services matching those filters.", + "error": "We could not search for services.", + "retry": "Try again", + "searchLabel": "Search", + "searchButton": "Search", + "categoryLabel": "Category", + "localityLabel": "Locality", + "radiusLabel": "Radius", + "priceLabel": "Price type", + "modeLabel": "Service mode", + "details": "View listing", + "marketplaceLabel": "Vila marketplace", + "locationContextLabel": "Portugal", + "filtersLabel": "Available filters", + "promoted": "Promoted", + "allCategories": "All categories", + "allLocalities": "All localities", + "anyPrice": "Any price", + "anyMode": "Any service mode", + "priceFixed": "Fixed price", + "priceHourly": "Hourly", + "priceDaily": "Daily", + "priceQuote": "Quote", + "priceNegotiable": "Negotiable", + "modeTravels": "Travels to customer", + "modeReceives": "Receives customers", + "modeRemote": "Remote", + "applyFilters": "Apply filters" + }, + "PublicListing": { + "loading": "Loading listing…", + "error": "We could not load this listing.", + "retry": "Try again", + "provider": "Provider", + "locality": "Locality", + "category": "Category", + "phone": "Reveal phone", + "whatsapp": "Reveal WhatsApp", + "revealError": "We could not reveal this contact.", + "message": "Send message", + "messageError": "We could not start the conversation.", + "ownListing": "This listing is yours. Contact and messaging actions are for customers.", + "manageListing": "Manage listing" + }, + "ContactChannels": { + "title": "Contact channels", + "description": "Control how authenticated customers can contact you.", + "loading": "Loading channels…", + "error": "We could not load or save contact channels.", + "retry": "Try again", + "phone": "Phone", + "whatsapp": "WhatsApp", + "contact": "Contact", + "formatHint": "Use international format, for example +351912345678.", + "enabled": "Enabled", + "consent": "Allow reveal", + "save": "Save channel", + "saved": "Channel saved." + }, + "Messaging": { + "title": "Messages", + "description": "Talk privately with customers and providers.", + "empty": "You do not have any conversations yet.", + "selectConversation": "Select a conversation", + "messageLabel": "Message", + "send": "Send", + "sending": "Sending...", + "loading": "Loading conversations...", + "error": "We could not load your messages.", + "conversation": "Conversation" + }, + "Notifications": { + "title": "Notifications", + "description": "Choose how you receive Vila updates.", + "empty": "You do not have any notifications.", + "loading": "Loading notifications...", + "error": "We could not load or save notifications.", + "inApp": "In-app notifications", + "email": "Email notifications", + "save": "Save preferences", + "saved": "Preferences saved.", + "markRead": "Mark as read", + "conversationStarted": "New conversation started", + "messageReceived": "New message received", + "conversationReported": "Conversation reported", + "requestPublished": "New quotation request published", + "proposalReceived": "New proposal received", + "proposalAccepted": "Your proposal was accepted", + "proposalRejected": "Your proposal was not selected", + "bookingCreated": "New booking created", + "bookingUpdated": "Booking status updated", + "reviewReceived": "New review received", + "reviewResponse": "The provider responded to your review", + "subscriptionUpdated": "Subscription updated", + "promotionUpdated": "Promotion updated" + }, + "Quotations": { + "title": "Requests and proposals", + "description": "Request local quotes, compare private proposals, and choose a provider.", + "customerRequests": "My requests", + "opportunities": "Provider opportunities", + "newRequest": "New quotation request", + "requestTitle": "Title", + "requestDescription": "Work description", + "category": "Category", + "locality": "Locality", + "budget": "Budget in cents (optional)", + "deadline": "Proposal deadline", + "publish": "Publish request", + "emptyRequests": "You have not published any requests yet.", + "emptyOpportunities": "There are no matching requests right now.", + "viewProposals": "View proposals", + "proposals": "Private proposals", + "proposalPrice": "Price in cents", + "proposalMessage": "Proposal message", + "availableAt": "Available from", + "estimatedMinutes": "Estimated minutes (optional)", + "submitProposal": "Submit proposal", + "accept": "Accept proposal", + "loading": "Loading requests...", + "error": "We could not load or save quotation requests.", + "created": "Request published.", + "submitted": "Proposal submitted.", + "accepted": "Proposal accepted." + }, + "Bookings": { + "title": "Bookings", + "description": "Track agreements, dates, private locations, and service status.", + "newBooking": "New booking", + "sourceType": "Agreement source", + "proposal": "Accepted proposal", + "listing": "Listing", + "direct": "Direct agreement", + "sourceId": "Proposal or listing identifier", + "selectListing": "Select service", + "loadingListings": "Loading services...", + "emptyListings": "No services available", + "providerId": "Provider identifier for a direct agreement", + "scheduledAt": "Date and time", + "privateLocation": "Private service location", + "agreedPrice": "Agreed price in cents", + "create": "Create booking", + "loading": "Loading bookings...", + "error": "We could not load or update bookings.", + "empty": "You do not have any bookings yet.", + "price": "Price", + "confirm": "Confirm", + "schedule": "Schedule", + "start": "Start service", + "complete": "Complete", + "cancel": "Cancel", + "dispute": "Open dispute", + "refund": "Mark refunded", + "preparePayment": "Prepare payment", + "continuePayment": "Continue to secure payment", + "paymentStatus": "Payment status", + "platformFee": "Platform fee", + "providerNet": "Expected provider payout", + "paymentUnavailable": "Secure payment is currently unavailable.", + "paymentTerms": "Review payment, cancellation, and refund terms" + }, + "Payouts": { + "title": "Provider payouts", + "description": "Complete Stripe-hosted verification before Vila can route customer payments to you. Vila never collects your bank details.", + "loading": "Loading payout readiness…", + "unavailable": "Payout onboarding is currently unavailable.", + "notStarted": "Payout onboarding has not started.", + "ready": "Your account is ready to accept payments and receive payouts.", + "incomplete": "Stripe still needs information before payments can be enabled.", + "charges": "Accept payments", + "payouts": "Receive payouts", + "details": "Identity and business details", + "start": "Start secure Stripe onboarding", + "continue": "Continue Stripe onboarding" + }, + "PaymentAdministration": { + "title": "Payments and disputes", + "description": "Monitor protected payments, disputes, and full refunds.", + "loading": "Loading payments…", + "error": "Payments could not be loaded or updated.", + "empty": "There are no protected payments.", + "booking": "Booking", + "gross": "Total", + "fee": "Vila fee", + "providerNet": "Provider amount", + "refund": "Refund", + "refundConfirm": "Confirm the full refund to the original payment method?", + "refundPending": "The refund was submitted to Stripe.", + "state": "State" + }, + "Reviews": { + "title": "Reviews", + "description": "Review completed services and respond to reviews you receive.", + "newReview": "Review a completed booking", + "bookingId": "Booking identifier", + "rating": "Rating", + "body": "Review", + "submit": "Publish review", + "received": "Reviews received", + "empty": "You have not received any reviews yet.", + "response": "Provider response", + "respond": "Respond", + "loading": "Loading reviews...", + "error": "We could not load or save reviews.", + "created": "Review published.", + "verified": "Verified booking" + }, + "Entitlements": { + "title": "Plans and promotions", + "description": "Review account limits, choose a professional plan, and promote active listings.", + "access": "Current access", + "activeListings": "Active listings", + "photos": "Photos per listing", + "analytics": "Analytics", + "enabled": "Enabled", + "disabled": "Disabled", + "plans": "Professional plans", + "choosePlan": "Choose plan", + "promotion": "Promote a listing", + "listingId": "Listing identifier", + "selectListing": "Select active listing", + "loadingListings": "Loading listings...", + "emptyListings": "No active listings available", + "period": "Period", + "promote": "Request promotion", + "current": "Current subscription", + "none": "No subscription", + "loading": "Loading plans and access...", + "error": "We could not load or save plans and promotions.", + "requested": "Request recorded.", + "pending": "Pending", + "active": "Active", + "cancelled": "Cancelled", + "expired": "Expired" + }, + "Administration": { + "title": "Administration", + "description": "Aggregated operational metrics and moderation queues with audited actions.", + "loading": "Loading administration...", + "error": "We could not load or update administration.", + "users": "Users", + "providers": "Providers", + "listings": "Active listings", + "bookings": "Completed bookings", + "reviews": "Published reviews", + "reports": "Open reports", + "reportQueue": "Reports", + "reviewQueue": "Reviews", + "empty": "No pending items.", + "reason": "Action reason", + "resolve": "Resolve report", + "hide": "Hide review", + "publish": "Publish review", + "saved": "Action recorded." }, "Errors": { "title": "Something went wrong", @@ -25,6 +428,6 @@ "home": "Back to home" }, "Loading": { - "label": "Loading Juntly…" + "label": "Loading Vila…" } } diff --git a/frontend/messages/es.json b/frontend/messages/es.json index e95883e..50d4cdd 100644 --- a/frontend/messages/es.json +++ b/frontend/messages/es.json @@ -1,18 +1,421 @@ { "Metadata": { - "title": "Juntly — Servicios locales, habilidades reales", - "description": "Encuentra personas y servicios locales cerca de ti. Juntly acerca comunidades, clientes y profesionales de confianza." + "title": "Vila: Servicios locales, habilidades reales", + "description": "Encuentra personas y servicios locales cerca de ti. Vila acerca comunidades, clientes y profesionales de confianza." }, "Landing": { "eyebrow": "Local people. Real skills.", "tagline": "Encuentra a quien sabe hacerlo.", "heading": "Servicios locales, más cerca de ti.", - "description": "Una forma sencilla de encontrar personas con habilidades reales en tu comunidad — empezando en el Portugal rural.", - "statusLabel": "La plataforma está tomando forma.", - "visionLinkLabel": "Descubrir la visión", + "description": "Una forma sencilla de encontrar personas con habilidades reales en tu comunidad, empezando en el Portugal rural.", + "visionLinkLabel": "Conocer la visión", + "discoverLinkLabel": "Explorar servicios", "visionTitle": "Creada para conexiones locales reales", - "visionDescription": "Descubrimiento, contacto y confianza sin quitar elección a las personas. Juntly conecta a quien necesita ayuda con quien sabe hacerla.", - "footerLabel": "Juntly — nacida en Portugal." + "visionDescription": "Descubrimiento, contacto y confianza sin quitar elección a las personas. Vila conecta a quien necesita ayuda con quien sabe hacerla.", + "showcaseTitle": "Lo local, más fácil.", + "how": { + "title": "De la necesidad a la solución, sin complicaciones.", + "description": "Vila reúne búsqueda, comparación y contacto en un recorrido claro.", + "discover": { + "title": "Busca lo que necesitas", + "description": "Encuentra servicios activos por categoría y localidad, con contexto suficiente para decidir." + }, + "compare": { + "title": "Compara con confianza", + "description": "Consulta perfiles, propuestas, disponibilidad y reseñas vinculadas a trabajos reales." + }, + "contact": { + "title": "Habla cuando tenga sentido", + "description": "Envía un mensaje o revela un contacto solo cuando estés listo para avanzar." + } + }, + "audience": { + "customer": { + "title": "Para quien necesita ayuda", + "description": "Descubre profesionales locales, pide propuestas, reserva servicios y sigue todo desde tu cuenta.", + "action": "Encontrar un servicio" + }, + "provider": { + "title": "Para quien sabe hacerlo", + "description": "Crea un perfil claro, publica servicios, responde solicitudes y construye reputación con trabajo completado.", + "action": "Preparar perfil", + "imageAlt": "Profesional trabajando madera en un taller" + } + }, + "trust": { + "title": "Confianza integrada en el producto.", + "description": "La experiencia acerca a las personas sin exponer más información de la necesaria.", + "privacy": { + "title": "Contactos bajo control", + "description": "Los datos de contacto siguen privados hasta que una acción autorizada los revela." + }, + "local": { + "title": "El contexto local primero", + "description": "Categorías, localidades y zonas de servicio mantienen los resultados realmente relevantes." + }, + "reputation": { + "title": "Un historial con significado", + "description": "Las reservas completadas respaldan reseñas verificadas y respuestas transparentes." + } + }, + "closing": { + "title": "Empieza por tu comunidad.", + "description": "Crea una cuenta para pedir ayuda, ofrecer tus habilidades o seguir conversaciones y reservas en un solo lugar.", + "action": "Crear cuenta" + }, + "footerLabel": "Vila, nacida en Portugal." + }, + "Auth": { + "signIn": "Iniciar sesión", + "signUp": "Crear cuenta" + }, + "Navigation": { + "skipToContent": "Saltar al contenido", + "discover": "Encontrar servicios", + "createService": "Crear servicio", + "account": "Mi cuenta", + "accountNavigation": "Opciones de la cuenta", + "marketplace": "Navegación del mercado" + }, + "Account": { + "title": "Cuenta Vila", + "description": "Tu sesión está activa. Elige cómo quieres utilizar Vila.", + "capabilities": { + "title": "Cómo utilizas Vila", + "description": "Tu cuenta puede encontrar servicios y, si quieres, también ofrecerlos.", + "customerLabel": "Encontrar servicios", + "customerDescription": "Tu cuenta siempre está lista para encontrar y contactar profesionales.", + "providerLabel": "Ofrecer servicios", + "providerDescription": "Activa esta opción para preparar tu perfil profesional en los próximos pasos.", + "enabled": "Activo", + "disabled": "Inactivo", + "loading": "Cargando las capacidades de la cuenta…", + "saving": "Guardando…", + "loadError": "No hemos podido cargar las capacidades de tu cuenta.", + "retry": "Intentar de nuevo", + "manageProvider": "Gestionar perfil profesional", + "manageListings": "Gestionar anuncios", + "manageMessages": "Ver mensajes", + "manageNotifications": "Gestionar notificaciones", + "manageQuotations": "Solicitudes y propuestas", + "manageBookings": "Gestionar reservas", + "manageReviews": "Reseñas", + "manageEntitlements": "Planes y promociones", + "manageModeration": "Aprobar anuncios", + "managePayouts": "Pagos y cobros", + "managePaymentsAdmin": "Gestionar pagos y disputas" + } + }, + "ProviderProfile": { + "title": "Perfil profesional", + "description": "Prepara los datos privados de tu perfil antes de publicarlo en el marketplace.", + "displayName": "Nombre público", + "providerType": "Tipo de profesional", + "individual": "Particular", + "professional": "Profesional", + "business": "Empresa", + "bio": "Presentación", + "primaryLocality": "Localidad principal", + "serviceLocalities": "Áreas de servicio", + "languages": "Idiomas", + "travelRadius": "Distancia máxima en kilómetros", + "serviceModes": "Modos de servicio", + "travels": "Se desplaza al cliente", + "receives": "Recibe clientes", + "remote": "Trabaja a distancia", + "save": "Guardar perfil", + "saving": "Guardando…", + "loading": "Cargando perfil…", + "error": "No hemos podido cargar o guardar el perfil.", + "retry": "Intentar de nuevo", + "saved": "Perfil guardado." + }, + "Listings": { + "title": "Mis anuncios", + "description": "Crea y sigue anuncios privados antes de publicarlos.", + "newListing": "Nuevo anuncio", + "create": "Guardar borrador", + "submit": "Enviar a revisión", + "pause": "Pausar", + "archive": "Archivar", + "loading": "Cargando anuncios…", + "error": "No hemos podido cargar o guardar los anuncios.", + "retry": "Intentar de nuevo", + "empty": "Todavía no tienes anuncios.", + "saved": "Borrador guardado.", + "titleLabel": "Título", + "descriptionLabel": "Descripción", + "categoryLabel": "Categoría", + "localityLabel": "Localidad", + "priceLabel": "Precio en céntimos" + }, + "Moderation": { + "title": "Revisión de anuncios", + "loading": "Cargando revisión…", + "empty": "No hay anuncios pendientes.", + "error": "No hemos podido cargar la cola de revisión.", + "retry": "Intentar de nuevo", + "approve": "Aprobar", + "reject": "Rechazar" + }, + "Discovery": { + "title": "Encontrar servicios", + "description": "Explora anuncios activos de proveedores locales.", + "loading": "Buscando servicios…", + "empty": "No hemos encontrado servicios con esos filtros.", + "error": "No hemos podido buscar servicios.", + "retry": "Intentar de nuevo", + "searchLabel": "Buscar", + "searchButton": "Buscar", + "categoryLabel": "Categoría", + "localityLabel": "Localidad", + "radiusLabel": "Radio", + "priceLabel": "Tipo de precio", + "modeLabel": "Modo de servicio", + "details": "Ver anuncio", + "marketplaceLabel": "Marketplace Vila", + "locationContextLabel": "Portugal", + "filtersLabel": "Filtros disponibles", + "promoted": "Promocionado", + "allCategories": "Todas las categorías", + "allLocalities": "Todas las localidades", + "anyPrice": "Cualquier precio", + "anyMode": "Cualquier modo", + "priceFixed": "Precio fijo", + "priceHourly": "Por hora", + "priceDaily": "Por día", + "priceQuote": "Presupuesto", + "priceNegotiable": "Negociable", + "modeTravels": "Se desplaza al cliente", + "modeReceives": "Recibe clientes", + "modeRemote": "A distancia", + "applyFilters": "Aplicar filtros" + }, + "PublicListing": { + "loading": "Cargando anuncio…", + "error": "No hemos podido cargar este anuncio.", + "retry": "Intentar de nuevo", + "provider": "Proveedor", + "locality": "Localidad", + "category": "Categoría", + "phone": "Revelar teléfono", + "whatsapp": "Revelar WhatsApp", + "revealError": "No hemos podido revelar este contacto.", + "message": "Enviar mensaje", + "messageError": "No hemos podido iniciar la conversación.", + "ownListing": "Este anuncio es tuyo. Los contactos y mensajes son para clientes.", + "manageListing": "Gestionar anuncio" + }, + "ContactChannels": { + "title": "Canales de contacto", + "description": "Controla cómo pueden contactarte los clientes autenticados.", + "loading": "Cargando canales…", + "error": "No hemos podido cargar o guardar los canales.", + "retry": "Intentar de nuevo", + "phone": "Teléfono", + "whatsapp": "WhatsApp", + "contact": "Contacto", + "formatHint": "Usa el formato internacional, por ejemplo +351912345678.", + "enabled": "Activo", + "consent": "Permitir revelación", + "save": "Guardar canal", + "saved": "Canal guardado." + }, + "Messaging": { + "title": "Mensajes", + "description": "Habla en privado con clientes y profesionales.", + "empty": "Todavía no tienes conversaciones.", + "selectConversation": "Selecciona una conversación", + "messageLabel": "Mensaje", + "send": "Enviar", + "sending": "Enviando...", + "loading": "Cargando conversaciones...", + "error": "No hemos podido cargar los mensajes.", + "conversation": "Conversación" + }, + "Notifications": { + "title": "Notificaciones", + "description": "Elige cómo recibes las novedades de Vila.", + "empty": "No tienes notificaciones.", + "loading": "Cargando notificaciones...", + "error": "No hemos podido cargar o guardar las notificaciones.", + "inApp": "Notificaciones en la aplicación", + "email": "Notificaciones por correo", + "save": "Guardar preferencias", + "saved": "Preferencias guardadas.", + "markRead": "Marcar como leída", + "conversationStarted": "Nueva conversación iniciada", + "messageReceived": "Nuevo mensaje recibido", + "conversationReported": "Conversación denunciada", + "requestPublished": "Nueva solicitud de presupuesto publicada", + "proposalReceived": "Nueva propuesta recibida", + "proposalAccepted": "Tu propuesta ha sido aceptada", + "proposalRejected": "Tu propuesta no ha sido seleccionada", + "bookingCreated": "Nueva reserva creada", + "bookingUpdated": "Estado de la reserva actualizado", + "reviewReceived": "Nueva reseña recibida", + "reviewResponse": "El profesional respondió a tu reseña", + "subscriptionUpdated": "Suscripción actualizada", + "promotionUpdated": "Promoción actualizada" + }, + "Quotations": { + "title": "Solicitudes y propuestas", + "description": "Solicita presupuestos locales, compara propuestas privadas y elige un profesional.", + "customerRequests": "Mis solicitudes", + "opportunities": "Oportunidades profesionales", + "newRequest": "Nueva solicitud de presupuesto", + "requestTitle": "Título", + "requestDescription": "Descripción del trabajo", + "category": "Categoría", + "locality": "Localidad", + "budget": "Presupuesto en céntimos (opcional)", + "deadline": "Plazo para propuestas", + "publish": "Publicar solicitud", + "emptyRequests": "Todavía no has publicado solicitudes.", + "emptyOpportunities": "No hay solicitudes compatibles ahora mismo.", + "viewProposals": "Ver propuestas", + "proposals": "Propuestas privadas", + "proposalPrice": "Precio en céntimos", + "proposalMessage": "Mensaje de la propuesta", + "availableAt": "Disponible desde", + "estimatedMinutes": "Minutos estimados (opcional)", + "submitProposal": "Enviar propuesta", + "accept": "Aceptar propuesta", + "loading": "Cargando solicitudes...", + "error": "No hemos podido cargar o guardar las solicitudes.", + "created": "Solicitud publicada.", + "submitted": "Propuesta enviada.", + "accepted": "Propuesta aceptada." + }, + "Bookings": { + "title": "Reservas", + "description": "Gestiona acuerdos, fechas, ubicaciones privadas y estados del servicio.", + "newBooking": "Nueva reserva", + "sourceType": "Origen del acuerdo", + "proposal": "Propuesta aceptada", + "listing": "Anuncio", + "direct": "Acuerdo directo", + "sourceId": "Identificador de propuesta o anuncio", + "selectListing": "Seleccionar servicio", + "loadingListings": "Cargando servicios...", + "emptyListings": "No hay servicios disponibles", + "providerId": "Identificador del profesional para acuerdo directo", + "scheduledAt": "Fecha y hora", + "privateLocation": "Ubicación privada del servicio", + "agreedPrice": "Precio acordado en céntimos", + "create": "Crear reserva", + "loading": "Cargando reservas...", + "error": "No hemos podido cargar o actualizar las reservas.", + "empty": "Todavía no tienes reservas.", + "price": "Precio", + "confirm": "Confirmar", + "schedule": "Programar", + "start": "Iniciar servicio", + "complete": "Completar", + "cancel": "Cancelar", + "dispute": "Abrir disputa", + "refund": "Marcar reembolso", + "preparePayment": "Preparar pago", + "continuePayment": "Continuar al pago seguro", + "paymentStatus": "Estado del pago", + "platformFee": "Comisión de la plataforma", + "providerNet": "Importe previsto para el profesional", + "paymentUnavailable": "El pago seguro no está disponible en este momento.", + "paymentTerms": "Consultar pagos, cancelaciones y reembolsos" + }, + "Payouts": { + "title": "Cobros del profesional", + "description": "Completa la verificación alojada por Stripe antes de que Vila pueda enviarte pagos. Vila nunca recopila tus datos bancarios.", + "loading": "Cargando el estado de cobros…", + "unavailable": "La configuración de cobros no está disponible en este momento.", + "notStarted": "La configuración de cobros aún no ha comenzado.", + "ready": "Tu cuenta está lista para aceptar pagos y recibir cobros.", + "incomplete": "Stripe todavía necesita información antes de activar los pagos.", + "charges": "Aceptar pagos", + "payouts": "Recibir cobros", + "details": "Identidad y datos profesionales", + "start": "Iniciar configuración segura con Stripe", + "continue": "Continuar configuración con Stripe" + }, + "PaymentAdministration": { + "title": "Pagos y disputas", + "description": "Supervisa pagos protegidos, disputas y reembolsos íntegros.", + "loading": "Cargando pagos…", + "error": "No se pudieron cargar o actualizar los pagos.", + "empty": "No hay pagos protegidos.", + "booking": "Reserva", + "gross": "Total", + "fee": "Comisión de Vila", + "providerNet": "Importe del profesional", + "refund": "Reembolsar", + "refundConfirm": "¿Confirmar el reembolso íntegro al método de pago original?", + "refundPending": "El reembolso se ha enviado a Stripe.", + "state": "Estado" + }, + "Reviews": { + "title": "Reseñas", + "description": "Valora servicios completados y responde a las reseñas recibidas.", + "newReview": "Valorar una reserva completada", + "bookingId": "Identificador de la reserva", + "rating": "Valoración", + "body": "Reseña", + "submit": "Publicar reseña", + "received": "Reseñas recibidas", + "empty": "Todavía no has recibido reseñas.", + "response": "Respuesta del profesional", + "respond": "Responder", + "loading": "Cargando reseñas...", + "error": "No hemos podido cargar o guardar las reseñas.", + "created": "Reseña publicada.", + "verified": "Reserva verificada" + }, + "Entitlements": { + "title": "Planes y promociones", + "description": "Consulta los límites, elige un plan profesional y promociona anuncios activos.", + "access": "Acceso actual", + "activeListings": "Anuncios activos", + "photos": "Fotos por anuncio", + "analytics": "Analítica", + "enabled": "Activa", + "disabled": "Inactiva", + "plans": "Planes profesionales", + "choosePlan": "Elegir plan", + "promotion": "Promocionar un anuncio", + "listingId": "Identificador del anuncio", + "selectListing": "Seleccionar anuncio activo", + "loadingListings": "Cargando anuncios...", + "emptyListings": "No hay anuncios activos disponibles", + "period": "Periodo", + "promote": "Solicitar promoción", + "current": "Suscripción actual", + "none": "Sin suscripción", + "loading": "Cargando planes y acceso...", + "error": "No hemos podido cargar o guardar los planes y promociones.", + "requested": "Solicitud registrada.", + "pending": "Pendiente", + "active": "Activa", + "cancelled": "Cancelada", + "expired": "Expirada" + }, + "Administration": { + "title": "Administración", + "description": "Métricas operativas agregadas y colas de moderación con acciones auditadas.", + "loading": "Cargando administración...", + "error": "No hemos podido cargar o actualizar la administración.", + "users": "Usuarios", + "providers": "Profesionales", + "listings": "Anuncios activos", + "bookings": "Reservas completadas", + "reviews": "Reseñas publicadas", + "reports": "Denuncias abiertas", + "reportQueue": "Denuncias", + "reviewQueue": "Reseñas", + "empty": "No hay elementos pendientes.", + "reason": "Motivo de la acción", + "resolve": "Resolver denuncia", + "hide": "Ocultar reseña", + "publish": "Publicar reseña", + "saved": "Acción registrada." }, "Errors": { "title": "Algo salió mal", @@ -25,6 +428,6 @@ "home": "Volver al inicio" }, "Loading": { - "label": "Cargando Juntly…" + "label": "Cargando Vila…" } } diff --git a/frontend/messages/pt-PT.json b/frontend/messages/pt-PT.json index e5682b6..e39b92e 100644 --- a/frontend/messages/pt-PT.json +++ b/frontend/messages/pt-PT.json @@ -1,18 +1,421 @@ { "Metadata": { - "title": "Juntly — Serviços locais, competências reais", - "description": "Encontre pessoas e serviços locais perto de si. A Juntly aproxima comunidades, clientes e prestadores de confiança." + "title": "Vila: Serviços locais, competências reais", + "description": "Encontre pessoas e serviços locais perto de si. A Vila aproxima comunidades, clientes e prestadores de confiança." }, "Landing": { "eyebrow": "Local people. Real skills.", "tagline": "Encontra quem sabe fazer.", "heading": "Serviços locais, mais perto de si.", - "description": "Uma forma simples de encontrar pessoas com competências reais na sua comunidade — começando no Portugal rural.", - "statusLabel": "A plataforma está a nascer.", + "description": "Uma forma simples de encontrar pessoas com competências reais na sua comunidade, começando no Portugal rural.", "visionLinkLabel": "Conhecer a visão", + "discoverLinkLabel": "Explorar serviços", "visionTitle": "Criada para ligações locais reais", - "visionDescription": "Descoberta, contacto e confiança sem retirar a escolha às pessoas. A Juntly liga quem precisa a quem sabe fazer.", - "footerLabel": "Juntly — com origem em Portugal." + "visionDescription": "Descoberta, contacto e confiança sem retirar a escolha às pessoas. A Vila liga quem precisa a quem sabe fazer.", + "showcaseTitle": "Comércio local, mais simples.", + "how": { + "title": "Da necessidade à solução, sem complicações.", + "description": "A Vila organiza a procura, a comparação e o contacto num percurso claro.", + "discover": { + "title": "Procure pelo que precisa", + "description": "Encontre serviços ativos por categoria e localidade, com informação suficiente para decidir o próximo passo." + }, + "compare": { + "title": "Compare com confiança", + "description": "Consulte perfis, propostas, disponibilidade e avaliações ligadas a trabalhos reais." + }, + "contact": { + "title": "Fale quando fizer sentido", + "description": "Envie mensagens ou revele um contacto apenas quando estiver pronto para avançar." + } + }, + "audience": { + "customer": { + "title": "Para quem precisa de ajuda", + "description": "Descubra profissionais locais, peça propostas, reserve serviços e acompanhe tudo na sua conta.", + "action": "Encontrar um serviço" + }, + "provider": { + "title": "Para quem sabe fazer", + "description": "Crie um perfil claro, publique serviços, responda a pedidos e construa reputação através de trabalho concluído.", + "action": "Preparar perfil", + "imageAlt": "Profissional a trabalhar madeira numa oficina" + } + }, + "trust": { + "title": "Confiança construída no próprio produto.", + "description": "A experiência foi pensada para aproximar pessoas sem expor mais informação do que o necessário.", + "privacy": { + "title": "Contactos sob controlo", + "description": "Os dados de contacto permanecem privados até existir uma ação autorizada para os revelar." + }, + "local": { + "title": "Contexto local primeiro", + "description": "Categorias, localidades e áreas de serviço ajudam a encontrar opções realmente relevantes." + }, + "reputation": { + "title": "Histórico com significado", + "description": "Reservas concluídas sustentam avaliações verificadas e respostas transparentes dos prestadores." + } + }, + "closing": { + "title": "Comece pela sua comunidade.", + "description": "Crie uma conta para pedir ajuda, disponibilizar competências ou acompanhar cada conversa e reserva num só lugar.", + "action": "Criar conta" + }, + "footerLabel": "Vila, com origem em Portugal." + }, + "Auth": { + "signIn": "Entrar", + "signUp": "Criar conta" + }, + "Navigation": { + "skipToContent": "Saltar para o conteúdo", + "discover": "Encontrar serviços", + "createService": "Criar serviço", + "account": "A minha conta", + "accountNavigation": "Opções da conta", + "marketplace": "Navegação do mercado" + }, + "Account": { + "title": "Conta Vila", + "description": "A sua sessão está ativa. Configure como pretende utilizar a Vila.", + "capabilities": { + "title": "Como utiliza a Vila", + "description": "A sua conta pode encontrar serviços e, se quiser, também disponibilizá-los.", + "customerLabel": "Encontrar serviços", + "customerDescription": "A sua conta está sempre pronta para procurar e contactar prestadores.", + "providerLabel": "Disponibilizar serviços", + "providerDescription": "Ative esta opção para preparar o seu perfil de prestador nos próximos passos.", + "enabled": "Ativo", + "disabled": "Inativo", + "loading": "A carregar as capacidades da conta…", + "saving": "A guardar…", + "loadError": "Não foi possível carregar as capacidades da conta.", + "retry": "Tentar novamente", + "manageProvider": "Gerir perfil de prestador", + "manageListings": "Gerir anúncios", + "manageMessages": "Ver mensagens", + "manageNotifications": "Gerir notificações", + "manageQuotations": "Pedidos e propostas", + "manageBookings": "Gerir reservas", + "manageReviews": "Avaliações", + "manageEntitlements": "Planos e promoções", + "manageModeration": "Aprovar anúncios", + "managePayouts": "Pagamentos e recebimentos", + "managePaymentsAdmin": "Gerir pagamentos e disputas" + } + }, + "ProviderProfile": { + "title": "Perfil de prestador", + "description": "Prepare os dados privados do seu perfil antes da publicação no marketplace.", + "displayName": "Nome de apresentação", + "providerType": "Tipo de prestador", + "individual": "Particular", + "professional": "Profissional", + "business": "Empresa", + "bio": "Apresentação", + "primaryLocality": "Localidade principal", + "serviceLocalities": "Áreas de serviço", + "languages": "Idiomas", + "travelRadius": "Distância máxima em quilómetros", + "serviceModes": "Modos de serviço", + "travels": "Desloca-se ao cliente", + "receives": "Recebe clientes", + "remote": "Trabalha à distância", + "save": "Guardar perfil", + "saving": "A guardar…", + "loading": "A carregar perfil…", + "error": "Não foi possível carregar ou guardar o perfil.", + "retry": "Tentar novamente", + "saved": "Perfil guardado." + }, + "Listings": { + "title": "Os meus anúncios", + "description": "Crie e acompanhe anúncios privados antes da publicação.", + "newListing": "Novo anúncio", + "create": "Guardar rascunho", + "submit": "Enviar para revisão", + "pause": "Pausar", + "archive": "Arquivar", + "loading": "A carregar anúncios…", + "error": "Não foi possível carregar ou guardar os anúncios.", + "retry": "Tentar novamente", + "empty": "Ainda não tem anúncios.", + "saved": "Rascunho guardado.", + "titleLabel": "Título", + "descriptionLabel": "Descrição", + "categoryLabel": "Categoria", + "localityLabel": "Localidade", + "priceLabel": "Preço em cêntimos" + }, + "Moderation": { + "title": "Revisão de anúncios", + "loading": "A carregar revisão…", + "empty": "Não há anúncios pendentes.", + "error": "Não foi possível carregar a revisão.", + "retry": "Tentar novamente", + "approve": "Aprovar", + "reject": "Rejeitar" + }, + "Discovery": { + "title": "Encontrar serviços", + "description": "Explore anúncios ativos de prestadores locais.", + "loading": "A procurar serviços…", + "empty": "Não encontrámos serviços com estes critérios.", + "error": "Não foi possível procurar serviços.", + "retry": "Tentar novamente", + "searchLabel": "Procurar", + "searchButton": "Procurar", + "categoryLabel": "Categoria", + "localityLabel": "Localidade", + "radiusLabel": "Raio", + "priceLabel": "Tipo de preço", + "modeLabel": "Modo de serviço", + "details": "Ver anúncio", + "marketplaceLabel": "Marketplace Vila", + "locationContextLabel": "Portugal", + "filtersLabel": "Filtros disponíveis", + "promoted": "Promovido", + "allCategories": "Todas as categorias", + "allLocalities": "Todas as localidades", + "anyPrice": "Qualquer preço", + "anyMode": "Qualquer modo", + "priceFixed": "Preço fixo", + "priceHourly": "À hora", + "priceDaily": "Ao dia", + "priceQuote": "Sob orçamento", + "priceNegotiable": "Negociável", + "modeTravels": "Desloca-se ao cliente", + "modeReceives": "Recebe clientes", + "modeRemote": "À distância", + "applyFilters": "Aplicar filtros" + }, + "PublicListing": { + "loading": "A carregar anúncio…", + "error": "Não foi possível carregar este anúncio.", + "retry": "Tentar novamente", + "provider": "Prestador", + "locality": "Localidade", + "category": "Categoria", + "phone": "Revelar telefone", + "whatsapp": "Revelar WhatsApp", + "revealError": "Não foi possível revelar o contacto.", + "message": "Enviar mensagem", + "messageError": "Não foi possível iniciar a conversa.", + "ownListing": "Este anúncio é seu. Os contactos e mensagens destinam-se aos clientes.", + "manageListing": "Gerir anúncio" + }, + "ContactChannels": { + "title": "Canais de contacto", + "description": "Controle como os clientes autenticados o podem contactar.", + "loading": "A carregar canais…", + "error": "Não foi possível carregar ou guardar os canais.", + "retry": "Tentar novamente", + "phone": "Telefone", + "whatsapp": "WhatsApp", + "contact": "Contacto", + "formatHint": "Use o formato internacional, por exemplo +351912345678.", + "enabled": "Ativo", + "consent": "Autorizar revelação", + "save": "Guardar canal", + "saved": "Canal guardado." + }, + "Messaging": { + "title": "Mensagens", + "description": "Converse em privado com clientes e prestadores.", + "empty": "Ainda não tem conversas.", + "selectConversation": "Selecione uma conversa", + "messageLabel": "Mensagem", + "send": "Enviar", + "sending": "A enviar...", + "loading": "A carregar conversas...", + "error": "Não foi possível carregar as mensagens.", + "conversation": "Conversa" + }, + "Notifications": { + "title": "Notificações", + "description": "Escolha como recebe atualizações da Vila.", + "empty": "Não tem notificações.", + "loading": "A carregar notificações...", + "error": "Não foi possível carregar ou guardar as notificações.", + "inApp": "Notificações na aplicação", + "email": "Notificações por email", + "save": "Guardar preferências", + "saved": "Preferências guardadas.", + "markRead": "Marcar como lida", + "conversationStarted": "Nova conversa iniciada", + "messageReceived": "Nova mensagem recebida", + "conversationReported": "Conversa denunciada", + "requestPublished": "Novo pedido de orçamento publicado", + "proposalReceived": "Nova proposta recebida", + "proposalAccepted": "A sua proposta foi aceite", + "proposalRejected": "A sua proposta não foi selecionada", + "bookingCreated": "Nova reserva criada", + "bookingUpdated": "Estado da reserva atualizado", + "reviewReceived": "Nova avaliação recebida", + "reviewResponse": "O prestador respondeu à sua avaliação", + "subscriptionUpdated": "Subscrição atualizada", + "promotionUpdated": "Promoção atualizada" + }, + "Quotations": { + "title": "Pedidos e propostas", + "description": "Peça orçamentos locais, compare propostas privadas e escolha um prestador.", + "customerRequests": "Os meus pedidos", + "opportunities": "Oportunidades para prestar serviços", + "newRequest": "Novo pedido de orçamento", + "requestTitle": "Título", + "requestDescription": "Descrição do trabalho", + "category": "Categoria", + "locality": "Localidade", + "budget": "Orçamento em cêntimos (opcional)", + "deadline": "Prazo para propostas", + "publish": "Publicar pedido", + "emptyRequests": "Ainda não publicou pedidos.", + "emptyOpportunities": "Não há pedidos compatíveis neste momento.", + "viewProposals": "Ver propostas", + "proposals": "Propostas privadas", + "proposalPrice": "Preço em cêntimos", + "proposalMessage": "Mensagem da proposta", + "availableAt": "Disponível a partir de", + "estimatedMinutes": "Duração estimada em minutos (opcional)", + "submitProposal": "Enviar proposta", + "accept": "Aceitar proposta", + "loading": "A carregar pedidos...", + "error": "Não foi possível carregar ou guardar os pedidos.", + "created": "Pedido publicado.", + "submitted": "Proposta enviada.", + "accepted": "Proposta aceite." + }, + "Bookings": { + "title": "Reservas", + "description": "Acompanhe acordos, datas, locais privados e estados do serviço.", + "newBooking": "Nova reserva", + "sourceType": "Origem do acordo", + "proposal": "Proposta aceite", + "listing": "Anúncio", + "direct": "Acordo direto", + "sourceId": "Identificador da proposta ou anúncio", + "selectListing": "Selecionar serviço", + "loadingListings": "A carregar serviços...", + "emptyListings": "Sem serviços disponíveis", + "providerId": "Identificador do prestador para acordo direto", + "scheduledAt": "Data e hora", + "privateLocation": "Local privado do serviço", + "agreedPrice": "Preço acordado em cêntimos", + "create": "Criar reserva", + "loading": "A carregar reservas...", + "error": "Não foi possível carregar ou atualizar as reservas.", + "empty": "Ainda não tem reservas.", + "price": "Preço", + "confirm": "Confirmar", + "schedule": "Agendar", + "start": "Iniciar serviço", + "complete": "Concluir", + "cancel": "Cancelar", + "dispute": "Abrir disputa", + "refund": "Marcar reembolso", + "preparePayment": "Preparar pagamento", + "continuePayment": "Continuar para o pagamento seguro", + "paymentStatus": "Estado do pagamento", + "platformFee": "Taxa da plataforma", + "providerNet": "Valor previsto para o prestador", + "paymentUnavailable": "O pagamento seguro não está disponível neste momento.", + "paymentTerms": "Consultar pagamentos, cancelamentos e reembolsos" + }, + "Payouts": { + "title": "Recebimentos do prestador", + "description": "Conclua a verificação alojada pela Stripe antes de a Vila encaminhar pagamentos de clientes. A Vila nunca recolhe os seus dados bancários.", + "loading": "A carregar o estado dos recebimentos…", + "unavailable": "A configuração de recebimentos não está disponível neste momento.", + "notStarted": "A configuração de recebimentos ainda não começou.", + "ready": "A sua conta está pronta para aceitar pagamentos e receber valores.", + "incomplete": "A Stripe ainda precisa de informação antes de ativar pagamentos.", + "charges": "Aceitar pagamentos", + "payouts": "Receber valores", + "details": "Identidade e dados profissionais", + "start": "Iniciar configuração segura na Stripe", + "continue": "Continuar configuração na Stripe" + }, + "PaymentAdministration": { + "title": "Pagamentos e disputas", + "description": "Acompanhe pagamentos protegidos, disputas e reembolsos integrais.", + "loading": "A carregar pagamentos…", + "error": "Não foi possível carregar ou atualizar os pagamentos.", + "empty": "Não há pagamentos protegidos.", + "booking": "Reserva", + "gross": "Total", + "fee": "Taxa Vila", + "providerNet": "Valor do prestador", + "refund": "Reembolsar", + "refundConfirm": "Confirmar o reembolso integral para o método de pagamento original?", + "refundPending": "O reembolso foi enviado à Stripe.", + "state": "Estado" + }, + "Reviews": { + "title": "Avaliações", + "description": "Avalie serviços concluídos e responda às avaliações recebidas.", + "newReview": "Avaliar uma reserva concluída", + "bookingId": "Identificador da reserva", + "rating": "Classificação", + "body": "Comentário", + "submit": "Publicar avaliação", + "received": "Avaliações recebidas", + "empty": "Ainda não recebeu avaliações.", + "response": "Resposta do prestador", + "respond": "Responder", + "loading": "A carregar avaliações...", + "error": "Não foi possível carregar ou guardar as avaliações.", + "created": "Avaliação publicada.", + "verified": "Reserva verificada" + }, + "Entitlements": { + "title": "Planos e promoções", + "description": "Consulte os limites da conta, escolha um plano profissional e promova anúncios ativos.", + "access": "Acesso atual", + "activeListings": "Anúncios ativos", + "photos": "Fotografias por anúncio", + "analytics": "Análises", + "enabled": "Ativas", + "disabled": "Inativas", + "plans": "Planos profissionais", + "choosePlan": "Escolher plano", + "promotion": "Promover um anúncio", + "listingId": "Identificador do anúncio", + "selectListing": "Selecionar anúncio ativo", + "loadingListings": "A carregar anúncios...", + "emptyListings": "Sem anúncios ativos disponíveis", + "period": "Período", + "promote": "Pedir promoção", + "current": "Subscrição atual", + "none": "Sem subscrição", + "loading": "A carregar planos e acesso...", + "error": "Não foi possível carregar ou guardar os planos e promoções.", + "requested": "Pedido registado.", + "pending": "Pendente", + "active": "Ativa", + "cancelled": "Cancelada", + "expired": "Expirada" + }, + "Administration": { + "title": "Administração", + "description": "Métricas operacionais agregadas e filas de moderação com ações auditadas.", + "loading": "A carregar administração...", + "error": "Não foi possível carregar ou atualizar a administração.", + "users": "Utilizadores", + "providers": "Prestadores", + "listings": "Anúncios ativos", + "bookings": "Reservas concluídas", + "reviews": "Avaliações publicadas", + "reports": "Denúncias abertas", + "reportQueue": "Denúncias", + "reviewQueue": "Avaliações", + "empty": "Sem itens pendentes.", + "reason": "Motivo da ação", + "resolve": "Resolver denúncia", + "hide": "Ocultar avaliação", + "publish": "Publicar avaliação", + "saved": "Ação registada." }, "Errors": { "title": "Algo não correu bem", @@ -25,6 +428,6 @@ "home": "Voltar ao início" }, "Loading": { - "label": "A carregar a Juntly…" + "label": "A carregar a Vila…" } } diff --git a/frontend/next.config.ts b/frontend/next.config.ts index 3abd081..3e76e92 100644 --- a/frontend/next.config.ts +++ b/frontend/next.config.ts @@ -4,6 +4,30 @@ import createNextIntlPlugin from "next-intl/plugin"; const nextConfig: NextConfig = { poweredByHeader: false, reactStrictMode: true, + async headers() { + return [ + { + source: "/:path*", + headers: [ + { key: "X-Content-Type-Options", value: "nosniff" }, + { key: "X-Frame-Options", value: "DENY" }, + { key: "Referrer-Policy", value: "strict-origin-when-cross-origin" }, + { + key: "Permissions-Policy", + value: "camera=(), microphone=(), geolocation=()", + }, + { + key: "Cross-Origin-Opener-Policy", + value: "same-origin-allow-popups", + }, + { + key: "Strict-Transport-Security", + value: "max-age=31536000; includeSubDomains", + }, + ], + }, + ]; + }, }; const withNextIntl = createNextIntlPlugin(); diff --git a/frontend/openapi-ts.config.ts b/frontend/openapi-ts.config.ts new file mode 100644 index 0000000..38bcdd1 --- /dev/null +++ b/frontend/openapi-ts.config.ts @@ -0,0 +1,7 @@ +import { defineConfig } from "@hey-api/openapi-ts"; + +export default defineConfig({ + input: "../openapi/juntly-api.v1.yaml", + output: "src/shared/api/generated", + plugins: ["@hey-api/typescript", "@hey-api/sdk", "@hey-api/client-fetch"], +}); diff --git a/frontend/package-lock.json b/frontend/package-lock.json index a1a6baa..3963576 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -8,12 +8,15 @@ "name": "frontend", "version": "0.1.0", "dependencies": { + "@clerk/localizations": "4.15.4", + "@clerk/nextjs": "7.7.9", "next": "16.3.1", "next-intl": "4.13.6", "react": "19.2.8", "react-dom": "19.2.8" }, "devDependencies": { + "@hey-api/openapi-ts": "0.99.0", "@tailwindcss/postcss": "^4", "@testing-library/jest-dom": "7.0.1", "@testing-library/react": "16.3.2", @@ -365,6 +368,97 @@ "specificity": "bin/cli.js" } }, + "node_modules/@clerk/backend": { + "version": "3.16.9", + "resolved": "https://registry.npmjs.org/@clerk/backend/-/backend-3.16.9.tgz", + "integrity": "sha512-x+kyjPTAMNEJIVuRMEXRSZUJPnB2PAoljSQ5gh/O/nVmRPIF1fU+tMQYEHKiCg3Ab/JUSywOsgamoEufyJj8dA==", + "license": "MIT", + "dependencies": { + "@clerk/shared": "^4.29.2", + "standardwebhooks": "^1.0.0", + "tslib": "2.8.1" + }, + "engines": { + "node": ">=20.9.0" + } + }, + "node_modules/@clerk/localizations": { + "version": "4.15.4", + "resolved": "https://registry.npmjs.org/@clerk/localizations/-/localizations-4.15.4.tgz", + "integrity": "sha512-CX78op+NLxAPigWH6oq9PF98ATOz3q3zVzE0msg5AfiCu6xNgzOLTqTKLNGF8DaFwsAxQ18pCRBaB7UAU4fXDA==", + "license": "MIT", + "dependencies": { + "@clerk/shared": "^4.29.2" + }, + "engines": { + "node": ">=20.9.0" + } + }, + "node_modules/@clerk/nextjs": { + "version": "7.7.9", + "resolved": "https://registry.npmjs.org/@clerk/nextjs/-/nextjs-7.7.9.tgz", + "integrity": "sha512-6niXLthsXYDwmH2pD0qL7Ut499p6/ucect1Ks9WQp+rVC5va+rTYGEy9pkKKWWePnO4oYf7ncmzJmjFYZXM0Hg==", + "license": "MIT", + "dependencies": { + "@clerk/backend": "^3.16.9", + "@clerk/react": "^6.14.4", + "@clerk/shared": "^4.29.2", + "server-only": "0.0.1", + "tslib": "2.8.1" + }, + "engines": { + "node": ">=20.9.0" + }, + "peerDependencies": { + "next": "^15.2.8 || ^15.3.8 || ^15.4.10 || ^15.5.9 || ^15.6.0-0 || ^16.0.10 || ^16.1.0-0", + "react": "^18.0.0 || ~19.0.3 || ~19.1.4 || ~19.2.3 || ~19.3.0-0", + "react-dom": "^18.0.0 || ~19.0.3 || ~19.1.4 || ~19.2.3 || ~19.3.0-0" + } + }, + "node_modules/@clerk/react": { + "version": "6.14.4", + "resolved": "https://registry.npmjs.org/@clerk/react/-/react-6.14.4.tgz", + "integrity": "sha512-vMv3SU8dvo/b09/FAY7A0xaAe+YhZ2u4nvkRUUdqmOi8u3HNSJ3s2k2PIb0cNHsLurMvDzw/dD2Hbk97MPnN/A==", + "license": "MIT", + "dependencies": { + "@clerk/shared": "^4.29.2", + "tslib": "2.8.1" + }, + "engines": { + "node": ">=20.9.0" + }, + "peerDependencies": { + "react": "^18.0.0 || ~19.0.3 || ~19.1.4 || ~19.2.3 || ~19.3.0-0", + "react-dom": "^18.0.0 || ~19.0.3 || ~19.1.4 || ~19.2.3 || ~19.3.0-0" + } + }, + "node_modules/@clerk/shared": { + "version": "4.29.2", + "resolved": "https://registry.npmjs.org/@clerk/shared/-/shared-4.29.2.tgz", + "integrity": "sha512-9c9Mc1oqumsqo+JY5R37O1ipwcG3RmwPK9oadBLL9E4VxZXthXVaFI9u+1/I4BSFGA/B9BO+17tkVDL4G0Wpbg==", + "license": "MIT", + "dependencies": { + "@tanstack/query-core": "^5.100.6", + "dequal": "2.0.3", + "glob-to-regexp": "0.4.1", + "js-cookie": "3.0.7" + }, + "engines": { + "node": ">=20.9.0" + }, + "peerDependencies": { + "react": "^18.0.0 || ~19.0.3 || ~19.1.4 || ~19.2.3 || ~19.3.0-0", + "react-dom": "^18.0.0 || ~19.0.3 || ~19.1.4 || ~19.2.3 || ~19.3.0-0" + }, + "peerDependenciesMeta": { + "react": { + "optional": true + }, + "react-dom": { + "optional": true + } + } + }, "node_modules/@csstools/color-helpers": { "version": "6.1.1", "resolved": "https://registry.npmjs.org/@csstools/color-helpers/-/color-helpers-6.1.1.tgz", @@ -730,6 +824,143 @@ "@formatjs/fast-memoize": "3.1.7" } }, + "node_modules/@hey-api/codegen-core": { + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/@hey-api/codegen-core/-/codegen-core-0.9.1.tgz", + "integrity": "sha512-s97jL1dgTMuiMHv2BZ1X4Tgd99Mf9GOvGdNqNcGwIMmnR+PgYNoraj4Zvp134MKsNCap/m7k0r0vKKnl56pj4w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@hey-api/types": "0.1.4", + "ansi-colors": "4.1.3", + "c12": "3.3.4", + "color-support": "1.1.3" + }, + "engines": { + "node": ">=22.18.0" + }, + "funding": { + "url": "https://github.com/sponsors/hey-api" + } + }, + "node_modules/@hey-api/json-schema-ref-parser": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/@hey-api/json-schema-ref-parser/-/json-schema-ref-parser-1.4.4.tgz", + "integrity": "sha512-otmd+zCxbYVBIp/mlMTnGkvlNYLkVKgs3VOIq0kSnenhB1+fRwLPQIeSwyWM6E51oXhUedkYjVsVpkVexeuJOA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jsdevtools/ono": "7.1.3", + "@types/json-schema": "7.0.15", + "js-yaml": "4.2.0" + }, + "engines": { + "node": ">=22.18.0" + }, + "funding": { + "url": "https://github.com/sponsors/hey-api" + } + }, + "node_modules/@hey-api/openapi-ts": { + "version": "0.99.0", + "resolved": "https://registry.npmjs.org/@hey-api/openapi-ts/-/openapi-ts-0.99.0.tgz", + "integrity": "sha512-SePU/5oEWWkvUBYmvzdYRctseoLuskyhs4ET0RvLIcmzc8yLQoA2R+KtBIQ8bPsoSUB0m4E5SmBnl6aGSA0szQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@hey-api/codegen-core": "0.9.1", + "@hey-api/json-schema-ref-parser": "1.4.4", + "@hey-api/shared": "0.5.0", + "@hey-api/spec-types": "0.2.0", + "@hey-api/types": "0.1.4", + "@lukeed/ms": "2.0.2", + "ansi-colors": "4.1.3", + "color-support": "1.1.3", + "commander": "15.0.0", + "get-tsconfig": "4.14.0" + }, + "bin": { + "openapi-ts": "bin/run.js" + }, + "engines": { + "node": ">=22.18.0" + }, + "funding": { + "url": "https://github.com/sponsors/hey-api" + }, + "peerDependencies": { + "typescript": ">=5.5.3 || >=6.0.0 || 6.0.1-rc" + } + }, + "node_modules/@hey-api/openapi-ts/node_modules/get-tsconfig": { + "version": "4.14.0", + "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.14.0.tgz", + "integrity": "sha512-yTb+8DXzDREzgvYmh6s9vHsSVCHeC0G3PI5bEXNBHtmshPnO+S5O7qgLEOn0I5QvMy6kpZN8K1NKGyilLb93wA==", + "dev": true, + "license": "MIT", + "dependencies": { + "resolve-pkg-maps": "^1.0.0" + }, + "funding": { + "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1" + } + }, + "node_modules/@hey-api/shared": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/@hey-api/shared/-/shared-0.5.0.tgz", + "integrity": "sha512-JN/j4Ebh4cJGYIQ5cwWuqe7GeSUyQoz7oC51WqyhKOcrejK6DKZMDkshc5d1eKTRuRL+rjozuRcoUaZZn2DGPw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@hey-api/codegen-core": "0.9.1", + "@hey-api/json-schema-ref-parser": "1.4.4", + "@hey-api/spec-types": "0.2.0", + "@hey-api/types": "0.1.4", + "ansi-colors": "4.1.3", + "cross-spawn": "7.0.6", + "open": "11.0.0", + "semver": "7.8.4" + }, + "engines": { + "node": ">=22.18.0" + }, + "funding": { + "url": "https://github.com/sponsors/hey-api" + } + }, + "node_modules/@hey-api/shared/node_modules/semver": { + "version": "7.8.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.4.tgz", + "integrity": "sha512-rUCObTnP32Q08R2uuIrt7r9PlEonuTmtuXYcW6s5kjdlj3xbnwe+21yXptAUYcMAABLkYYTtnmzb3w3EDZfueA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@hey-api/spec-types": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/@hey-api/spec-types/-/spec-types-0.2.0.tgz", + "integrity": "sha512-ibQ8Is7evMavzr8GNyJCcTg975d8DpaMUyLmOrQ85UBdy1l6t1KuRAwgChAbesJsIlNV6gjmlXruWyegDX18Fg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@hey-api/types": "0.1.4" + }, + "funding": { + "url": "https://github.com/sponsors/hey-api" + } + }, + "node_modules/@hey-api/types": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/@hey-api/types/-/types-0.1.4.tgz", + "integrity": "sha512-thWfawrDIP7wSI9ioT13I5soaaqB5vAPIiZmgD8PbeEVKNrkonc0N/Sjj97ezl7oQgusZmaNphGdMKipPO6IBg==", + "dev": true, + "license": "MIT" + }, "node_modules/@humanfs/core": { "version": "0.19.2", "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.2.tgz", @@ -1347,6 +1578,23 @@ "@jridgewell/sourcemap-codec": "^1.4.14" } }, + "node_modules/@jsdevtools/ono": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/@jsdevtools/ono/-/ono-7.1.3.tgz", + "integrity": "sha512-4JQNk+3mVzK3xh2rqd6RB4J46qUR19azEHBneZyTZM+c456qOrbbM/5xcR8huNCCcbVt7+UmizG6GuUvPvKUYg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@lukeed/ms": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@lukeed/ms/-/ms-2.0.2.tgz", + "integrity": "sha512-9I2Zn6+NJLfaGoz9jN3lpwDgAYvfGeNYdbAIjJOqzs4Tpc+VU3Jqq4IofSUBKajiDS8k9fZIg18/z13mpk1bsA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/@napi-rs/wasm-runtime": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.2.3.tgz", @@ -2148,6 +2396,12 @@ "integrity": "sha512-bXHSaW5jRTmke9Vd0h5P7BtWZG9Znqb8gSDxZnxaGSJnGwPLDPfS+3g0BKzeWqzgZPsIVZkM7m2tbo18cm5HBw==", "license": "MIT" }, + "node_modules/@stablelib/base64": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@stablelib/base64/-/base64-1.0.1.tgz", + "integrity": "sha512-1bnPQqSxSuc3Ii6MhBysoWCg58j97aUjuCSZrGSmDxNqtytIi0k8utUenAwTZN4V5mXXYGsVUI9zeBqy+jBOSQ==", + "license": "MIT" + }, "node_modules/@standard-schema/spec": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.1.0.tgz", @@ -2682,6 +2936,16 @@ "tailwindcss": "4.3.3" } }, + "node_modules/@tanstack/query-core": { + "version": "5.101.4", + "resolved": "https://registry.npmjs.org/@tanstack/query-core/-/query-core-5.101.4.tgz", + "integrity": "sha512-gNwcvOJcRbLWPOLG/2OBm+zM+Yv+MKsXKEOWC57USuZDEsI71hEErQsiEGx5wX9rzWWkfwM0fVSPoiIFSsxfiw==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + } + }, "node_modules/@testing-library/dom": { "version": "10.4.1", "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-10.4.1.tgz", @@ -3432,6 +3696,16 @@ "url": "https://github.com/sponsors/epoberezkin" } }, + "node_modules/ansi-colors": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz", + "integrity": "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/ansi-regex": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", @@ -3786,6 +4060,51 @@ "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" } }, + "node_modules/bundle-name": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bundle-name/-/bundle-name-4.1.0.tgz", + "integrity": "sha512-tjwM5exMg6BGRI+kNmTntNsvdZS1X8BFYS6tnJ2hdH0kVxM6/eVZ2xy+FqStSWvYmtfFMDLIxurorHwDKfDz5Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "run-applescript": "^7.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/c12": { + "version": "3.3.4", + "resolved": "https://registry.npmjs.org/c12/-/c12-3.3.4.tgz", + "integrity": "sha512-cM0ApFQSBXuourJejzwv/AuPRvAxordTyParRVcHjjtXirtkzM0uK2L9TTn9s0cXZbG7E55jCivRQzoxYmRAlA==", + "dev": true, + "license": "MIT", + "dependencies": { + "chokidar": "^5.0.0", + "confbox": "^0.2.4", + "defu": "^6.1.6", + "dotenv": "^17.3.1", + "exsolve": "^1.0.8", + "giget": "^3.2.0", + "jiti": "^2.6.1", + "ohash": "^2.0.11", + "pathe": "^2.0.3", + "perfect-debounce": "^2.1.0", + "pkg-types": "^2.3.0", + "rc9": "^3.0.1" + }, + "peerDependencies": { + "magicast": "*" + }, + "peerDependenciesMeta": { + "magicast": { + "optional": true + } + } + }, "node_modules/call-bind": { "version": "1.0.9", "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.9.tgz", @@ -3893,6 +4212,22 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, + "node_modules/chokidar": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-5.0.0.tgz", + "integrity": "sha512-TQMmc3w+5AxjpL8iIiwebF73dRDF4fBIieAqGn9RGCWaEVwQ6Fb2cGe31Yns0RRIzii5goJ1Y7xbMwo1TxMplw==", + "dev": true, + "license": "MIT", + "dependencies": { + "readdirp": "^5.0.0" + }, + "engines": { + "node": ">= 20.19.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, "node_modules/client-only": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/client-only/-/client-only-0.0.1.tgz", @@ -3919,6 +4254,26 @@ "dev": true, "license": "MIT" }, + "node_modules/color-support": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz", + "integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==", + "dev": true, + "license": "ISC", + "bin": { + "color-support": "bin.js" + } + }, + "node_modules/commander": { + "version": "15.0.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-15.0.0.tgz", + "integrity": "sha512-z67u4ZhzCL/Tydu1lJARtEZYWbWaN7oYLHbsuzocr6y4N6WZAagG3RQ4FW61V1/0+jImpj293XfrcYnd1qxtPg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=22.12.0" + } + }, "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", @@ -3926,6 +4281,13 @@ "dev": true, "license": "MIT" }, + "node_modules/confbox": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/confbox/-/confbox-0.2.4.tgz", + "integrity": "sha512-ysOGlgTFbN2/Y6Cg3Iye8YKulHw+R2fNXHrgSmXISQdMnomY6eNDprVdW9R5xBguEqI954+S6709UyiO7B+6OQ==", + "dev": true, + "license": "MIT" + }, "node_modules/convert-source-map": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", @@ -4083,6 +4445,36 @@ "dev": true, "license": "MIT" }, + "node_modules/default-browser": { + "version": "5.5.1", + "resolved": "https://registry.npmjs.org/default-browser/-/default-browser-5.5.1.tgz", + "integrity": "sha512-m1pAzaJgZ/gssEqlOhJkPJp8Xly7QyW6xcrkUa2KKcDeDSEMP7X8xipU3snUcfisTQx0w1AGae+9UtJSfVnXGw==", + "dev": true, + "license": "MIT", + "dependencies": { + "bundle-name": "^4.1.0", + "default-browser-id": "^5.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/default-browser-id": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/default-browser-id/-/default-browser-id-5.0.1.tgz", + "integrity": "sha512-x1VCxdX4t+8wVfd1so/9w+vQ4vx7lKd2Qp5tDRutErwmR85OgmfX7RlLRMWafRMY7hbEiXIbudNrjOAPa/hL8Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/define-data-property": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", @@ -4101,6 +4493,19 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/define-lazy-prop": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-3.0.0.tgz", + "integrity": "sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/define-properties": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz", @@ -4119,17 +4524,29 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/defu": { + "version": "6.1.7", + "resolved": "https://registry.npmjs.org/defu/-/defu-6.1.7.tgz", + "integrity": "sha512-7z22QmUWiQ/2d0KkdYmANbRUVABpZ9SNYyH5vx6PZ+nE5bcC0l7uFvEfHlyld/HcGBFTL536ClDt3DEcSlEJAQ==", + "dev": true, + "license": "MIT" + }, "node_modules/dequal": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==", - "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=6" } }, + "node_modules/destr": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/destr/-/destr-2.0.5.tgz", + "integrity": "sha512-ugFTXCtDZunbzasqBxrK93Ik/DRYsO6S/fedkWEMKqt04xZ4csmnmwGDBAb07QWNaGMAmnTIemsYZCksjATwsA==", + "dev": true, + "license": "MIT" + }, "node_modules/detect-libc": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", @@ -4160,6 +4577,19 @@ "license": "MIT", "peer": true }, + "node_modules/dotenv": { + "version": "17.4.2", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-17.4.2.tgz", + "integrity": "sha512-nI4U3TottKAcAD9LLud4Cb7b2QztQMUEfHbvhTH09bqXTxnSie8WnjPALV/WMCrJZ6UV/qHJ6L03OqO3LcdYZw==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" + } + }, "node_modules/dunder-proto": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", @@ -5127,6 +5557,13 @@ "node": ">=12.0.0" } }, + "node_modules/exsolve": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/exsolve/-/exsolve-1.1.1.tgz", + "integrity": "sha512-9U/jZUgjnSGyntRr6y5Muu1MJcwFl6kPu7k8qLF0IMNfLqvw0NZ4nnVDq0RVoZ0RvCyumib4Ez3KYrVfilrw+g==", + "dev": true, + "license": "MIT" + }, "node_modules/fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", @@ -5178,6 +5615,12 @@ "dev": true, "license": "MIT" }, + "node_modules/fast-sha256": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/fast-sha256/-/fast-sha256-1.3.0.tgz", + "integrity": "sha512-n11RGP/lrWEFI/bWdygLxhI+pVeo1ZYIVwvvPkW7azl/rOy+F3HYRZ2K5zeE9mmkhQppyv9sQFx0JM9UabnpPQ==", + "license": "Unlicense" + }, "node_modules/fastq": { "version": "1.20.1", "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.20.1.tgz", @@ -5417,6 +5860,16 @@ "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1" } }, + "node_modules/giget": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/giget/-/giget-3.3.1.tgz", + "integrity": "sha512-r+mvuDjrjMpsdw46Kmeydb8bdHm7wOKw8wNBtTndkjbPjgAp5oUJUxRE76wZFknxIPokfWvep2qSXK37aXE6zg==", + "dev": true, + "license": "MIT", + "bin": { + "giget": "dist/cli.mjs" + } + }, "node_modules/glob-parent": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", @@ -5430,6 +5883,12 @@ "node": ">=10.13.0" } }, + "node_modules/glob-to-regexp": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz", + "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==", + "license": "BSD-2-Clause" + }, "node_modules/globals": { "version": "14.0.0", "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", @@ -5849,6 +6308,22 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-docker": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-3.0.0.tgz", + "integrity": "sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ==", + "dev": true, + "license": "MIT", + "bin": { + "is-docker": "cli.js" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/is-document.all": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-document.all/-/is-document.all-1.0.0.tgz", @@ -5922,6 +6397,38 @@ "node": ">=0.10.0" } }, + "node_modules/is-in-ssh": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-in-ssh/-/is-in-ssh-1.0.0.tgz", + "integrity": "sha512-jYa6Q9rH90kR1vKB6NM7qqd1mge3Fx4Dhw5TVlK1MUBqhEOuCagrEHMevNuCcbECmXZ0ThXkRm+Ymr51HwEPAw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-inside-container": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-inside-container/-/is-inside-container-1.0.0.tgz", + "integrity": "sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-docker": "^3.0.0" + }, + "bin": { + "is-inside-container": "cli.js" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/is-map": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.3.tgz", @@ -6127,6 +6634,22 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-wsl": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-3.1.1.tgz", + "integrity": "sha512-e6rvdUCiQCAuumZslxRJWR/Doq4VpPR82kqclvcS0efgt430SlGIk05vdCN58+VrzgtIcfNODjozVielycD4Sw==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-inside-container": "^1.0.0" + }, + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/isarray": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", @@ -6169,6 +6692,15 @@ "jiti": "lib/jiti-cli.mjs" } }, + "node_modules/js-cookie": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/js-cookie/-/js-cookie-3.0.7.tgz", + "integrity": "sha512-z/wZZgDrkNV1eA0ULjM/F9/50Ya8fbzgKneSpoPsXSGd0KnpdtHfOZWK+GcwLk+EZbS4F9RBhU+K2RgzuDaItw==", + "license": "MIT", + "engines": { + "node": ">=20" + } + }, "node_modules/js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", @@ -7106,6 +7638,34 @@ "node": ">=12.20.0" } }, + "node_modules/ohash": { + "version": "2.0.12", + "resolved": "https://registry.npmjs.org/ohash/-/ohash-2.0.12.tgz", + "integrity": "sha512-65S/5gk9YSsaRjcyf7Nfa6h/d3E8/1gslpXfI4W7Dxn/oap8IKRuNT5VXkLQ1YFKIEg4apRY4Pj6aiwFzrDdmw==", + "dev": true, + "license": "MIT" + }, + "node_modules/open": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/open/-/open-11.0.0.tgz", + "integrity": "sha512-smsWv2LzFjP03xmvFoJ331ss6h+jixfA4UUV/Bsiyuu4YJPfN+FIQGOIiv4w9/+MoHkfkJ22UIaQWRVFRfH6Vw==", + "dev": true, + "license": "MIT", + "dependencies": { + "default-browser": "^5.4.0", + "define-lazy-prop": "^3.0.0", + "is-in-ssh": "^1.0.0", + "is-inside-container": "^1.0.0", + "powershell-utils": "^0.1.0", + "wsl-utils": "^0.3.0" + }, + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/optionator": { "version": "0.9.4", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", @@ -7235,6 +7795,13 @@ "dev": true, "license": "MIT" }, + "node_modules/perfect-debounce": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/perfect-debounce/-/perfect-debounce-2.1.0.tgz", + "integrity": "sha512-LjgdTytVFXeUgtHZr9WYViYSM/g8MkcTPYDlPa3cDqMirHjKiSZPYd6DoL7pK8AJQr+uWkQvCjHNdiMqsrJs+g==", + "dev": true, + "license": "MIT" + }, "node_modules/picocolors": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", @@ -7254,6 +7821,18 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, + "node_modules/pkg-types": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-2.3.1.tgz", + "integrity": "sha512-y+ichcgc2LrADuhLNAx8DFjVfgz91pRxfZdI3UDhxHvcVEZsenLO+7XaU5vOp0u/7V/wZ+plyuQxtrDlZJ+yeg==", + "dev": true, + "license": "MIT", + "dependencies": { + "confbox": "^0.2.4", + "exsolve": "^1.0.8", + "pathe": "^2.0.3" + } + }, "node_modules/po-parser": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/po-parser/-/po-parser-2.2.0.tgz", @@ -7299,6 +7878,19 @@ "node": "^10 || ^12 || >=14" } }, + "node_modules/powershell-utils": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/powershell-utils/-/powershell-utils-0.1.0.tgz", + "integrity": "sha512-dM0jVuXJPsDN6DvRpea484tCUaMiXWjuCn++HGTqUWzGDjv5tZkEZldAJ/UMlqRYGFrD/etByo4/xOuC/snX2A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/prelude-ls": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", @@ -7485,6 +8077,17 @@ ], "license": "MIT" }, + "node_modules/rc9": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/rc9/-/rc9-3.0.1.tgz", + "integrity": "sha512-gMDyleLWVE+i6Sgtc0QbbY6pEKqYs97NGi6isHQPqYlLemPoO8dxQ3uGi0f4NiP98c+jMW6cG1Kx9dDwfvqARQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "defu": "^6.1.6", + "destr": "^2.0.5" + } + }, "node_modules/react": { "version": "19.2.8", "resolved": "https://registry.npmjs.org/react/-/react-19.2.8.tgz", @@ -7513,6 +8116,20 @@ "dev": true, "license": "MIT" }, + "node_modules/readdirp": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-5.1.1.tgz", + "integrity": "sha512-Kko+Y5XQ6fM+Ce3dq3m9YGxnacYZYl9cA1wZjaF3Vbry2L3i1qVg8+CAgNPsXRArPMUMCaOR7oa9Nqntc43JKA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 20.19.0" + }, + "funding": { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + }, "node_modules/redent": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/redent/-/redent-3.0.0.tgz", @@ -7669,6 +8286,19 @@ "@rolldown/binding-win32-x64-msvc": "1.2.4" } }, + "node_modules/run-applescript": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/run-applescript/-/run-applescript-7.1.0.tgz", + "integrity": "sha512-DPe5pVFaAsinSaV6QjQ6gdiedWDcRCbUuiQfQa2wmWV7+xC9bGulGI8+TdRmoFkAPaBXk8CrAbnlY2ISniJ47Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/run-parallel": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", @@ -7777,6 +8407,12 @@ "semver": "bin/semver.js" } }, + "node_modules/server-only": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/server-only/-/server-only-0.0.1.tgz", + "integrity": "sha512-qepMx2JxAa5jjfzxG79yPPq+8BuFToHd1hm7kI+Z4zAq1ftQiP7HcxMhDDItrbtwVeLg/cY2JnKnrcFkmiswNA==", + "license": "MIT" + }, "node_modules/set-function-length": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", @@ -8018,6 +8654,16 @@ "dev": true, "license": "MIT" }, + "node_modules/standardwebhooks": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/standardwebhooks/-/standardwebhooks-1.0.0.tgz", + "integrity": "sha512-BbHGOQK9olHPMvQNHWul6MYlrRTAOKn03rOe4A8O3CLWhNf4YHBqq2HJKKC+sfqpxiBY52pNeesD6jIiLDz8jg==", + "license": "MIT", + "dependencies": { + "@stablelib/base64": "^1.0.0", + "fast-sha256": "^1.3.0" + } + }, "node_modules/std-env": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/std-env/-/std-env-4.2.0.tgz", @@ -9321,6 +9967,23 @@ "node": ">=0.10.0" } }, + "node_modules/wsl-utils": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/wsl-utils/-/wsl-utils-0.3.1.tgz", + "integrity": "sha512-g/eziiSUNBSsdDJtCLB8bdYEUMj4jR7AGeUo96p/3dTafgjHhpF4RiCFPiRILwjQoDXx5MqkBr4fwWtR3Ky4Wg==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-wsl": "^3.1.0", + "powershell-utils": "^0.1.0" + }, + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/xml-name-validator": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-5.0.0.tgz", diff --git a/frontend/package.json b/frontend/package.json index 9d45f49..82babe1 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -4,24 +4,31 @@ "private": true, "type": "module", "scripts": { + "codegen": "openapi-ts", + "codegen:check": "node scripts/check-codegen.mjs", "dev": "next dev", + "dev:local": "next dev --hostname localhost --port 4200", "build": "next build", "start": "next start", + "start:local": "next start --hostname localhost --port 4200", "format": "prettier --write .", "format:check": "prettier --check .", "test": "vitest run", "test:watch": "vitest", "lint": "eslint . --max-warnings=0", "typecheck": "next typegen && tsc --noEmit", - "verify": "npm run format:check && npm test && npm run lint && npm run typecheck && npm run build && npm audit --audit-level=high" + "verify": "npm run codegen:check && npm run format:check && npm test && npm run lint && npm run typecheck && npm run build && npm audit --audit-level=high" }, "dependencies": { + "@clerk/localizations": "4.15.4", + "@clerk/nextjs": "7.7.9", "next": "16.3.1", "next-intl": "4.13.6", "react": "19.2.8", "react-dom": "19.2.8" }, "devDependencies": { + "@hey-api/openapi-ts": "0.99.0", "@tailwindcss/postcss": "^4", "@testing-library/jest-dom": "7.0.1", "@testing-library/react": "16.3.2", @@ -37,5 +44,8 @@ "tailwindcss": "^4", "typescript": "5.9.3", "vitest": "4.1.10" + }, + "overrides": { + "js-yaml": "4.3.1" } } diff --git a/frontend/public/images/README.md b/frontend/public/images/README.md new file mode 100644 index 0000000..c5b0ea5 --- /dev/null +++ b/frontend/public/images/README.md @@ -0,0 +1,5 @@ +# Image attribution + +`local-craft.jpg` is a free Unsplash photograph by Jean-Baptiste D. (`@jbonunsplash`), sourced from https://unsplash.com/photos/WCicoBA3Us8 for the Juntly marketplace landing page. + +`local-provider.jpg` is a free Unsplash photograph by Melody Zimmerman (`@prairiepilgrim`), sourced from https://unsplash.com/photos/98IjTHNYv88 for the provider journey on the Juntly landing page. diff --git a/frontend/public/images/local-craft.jpg b/frontend/public/images/local-craft.jpg new file mode 100644 index 0000000..c3fd98c Binary files /dev/null and b/frontend/public/images/local-craft.jpg differ diff --git a/frontend/public/images/local-provider.jpg b/frontend/public/images/local-provider.jpg new file mode 100644 index 0000000..973152d Binary files /dev/null and b/frontend/public/images/local-provider.jpg differ diff --git a/frontend/scripts/check-codegen.mjs b/frontend/scripts/check-codegen.mjs new file mode 100644 index 0000000..107516c --- /dev/null +++ b/frontend/scripts/check-codegen.mjs @@ -0,0 +1,72 @@ +import { createHash } from "node:crypto"; +import { readdirSync, readFileSync, statSync } from "node:fs"; +import { relative, resolve } from "node:path"; +import { spawnSync } from "node:child_process"; + +const generatedDir = resolve("src/shared/api/generated"); + +function listFiles(dir) { + try { + return readdirSync(dir, { withFileTypes: true }).flatMap((entry) => { + const fullPath = resolve(dir, entry.name); + + if (entry.isDirectory()) { + return listFiles(fullPath); + } + + if (entry.isFile()) { + return [fullPath]; + } + + return []; + }); + } catch (error) { + if (error?.code === "ENOENT") { + return []; + } + + throw error; + } +} + +function hashGeneratedTree() { + const hash = createHash("sha256"); + const files = listFiles(generatedDir).sort(); + + for (const file of files) { + const stats = statSync(file); + hash.update(relative(generatedDir, file)); + hash.update(String(stats.size)); + hash.update(readFileSync(file)); + } + + return hash.digest("hex"); +} + +const before = hashGeneratedTree(); +const result = + process.platform === "win32" + ? spawnSync("cmd.exe", ["/d", "/s", "/c", "npm.cmd run codegen"], { + cwd: process.cwd(), + stdio: "inherit", + }) + : spawnSync("npm", ["run", "codegen"], { + cwd: process.cwd(), + stdio: "inherit", + }); + +if (result.error) { + process.stderr.write(`${result.error.message}\n`); + process.exit(1); +} + +if (result.status !== 0) { + process.exit(result.status ?? 1); +} + +const after = hashGeneratedTree(); + +if (before !== after) { + process.stderr.write("Generated OpenAPI client is out of date.\n"); + process.exit(1); +} diff --git a/frontend/src/app/[locale]/account/bookings/page.tsx b/frontend/src/app/[locale]/account/bookings/page.tsx new file mode 100644 index 0000000..1977c20 --- /dev/null +++ b/frontend/src/app/[locale]/account/bookings/page.tsx @@ -0,0 +1,60 @@ +import { hasLocale } from "next-intl"; +import { getTranslations } from "next-intl/server"; +import { notFound } from "next/navigation"; +import { requireAuthenticatedUser } from "@/features/auth/require-session"; +import { BookingDashboard } from "@/features/bookings/booking-dashboard"; +import { routing } from "@/i18n/routing"; +export const dynamic = "force-dynamic"; +export default async function BookingsPage({ + params, +}: PageProps<"/[locale]/account/bookings">) { + const { locale } = await params; + if (!hasLocale(routing.locales, locale)) notFound(); + await requireAuthenticatedUser(locale); + const t = await getTranslations({ locale, namespace: "Bookings" }); + return ( +
+
+ +
+
+ ); +} diff --git a/frontend/src/app/[locale]/account/contact-channels/page.tsx b/frontend/src/app/[locale]/account/contact-channels/page.tsx new file mode 100644 index 0000000..6d8ddab --- /dev/null +++ b/frontend/src/app/[locale]/account/contact-channels/page.tsx @@ -0,0 +1,41 @@ +import { hasLocale } from "next-intl"; +import { getTranslations } from "next-intl/server"; +import { notFound } from "next/navigation"; + +import { requireAuthenticatedUser } from "@/features/auth/require-session"; +import { ContactChannelsCard } from "@/features/contact/contact-channels-card"; +import { routing } from "@/i18n/routing"; + +export const dynamic = "force-dynamic"; + +export default async function ContactChannelsPage({ + params, +}: PageProps<"/[locale]/account/contact-channels">) { + const { locale } = await params; + if (!hasLocale(routing.locales, locale)) notFound(); + await requireAuthenticatedUser(locale); + const t = await getTranslations({ locale, namespace: "ContactChannels" }); + return ( +
+
+ +
+
+ ); +} diff --git a/frontend/src/app/[locale]/account/entitlements/page.tsx b/frontend/src/app/[locale]/account/entitlements/page.tsx new file mode 100644 index 0000000..8dd0548 --- /dev/null +++ b/frontend/src/app/[locale]/account/entitlements/page.tsx @@ -0,0 +1,52 @@ +import { hasLocale } from "next-intl"; +import { getTranslations } from "next-intl/server"; +import { notFound } from "next/navigation"; +import { requireAuthenticatedUser } from "@/features/auth/require-session"; +import { EntitlementsDashboard } from "@/features/entitlements/entitlements-dashboard"; +import { routing } from "@/i18n/routing"; +export const dynamic = "force-dynamic"; +export default async function EntitlementsPage({ + params, +}: PageProps<"/[locale]/account/entitlements">) { + const { locale } = await params; + if (!hasLocale(routing.locales, locale)) notFound(); + await requireAuthenticatedUser(locale); + const t = await getTranslations({ locale, namespace: "Entitlements" }); + return ( +
+
+ +
+
+ ); +} diff --git a/frontend/src/app/[locale]/account/listings/page.test.tsx b/frontend/src/app/[locale]/account/listings/page.test.tsx new file mode 100644 index 0000000..0ab3aec --- /dev/null +++ b/frontend/src/app/[locale]/account/listings/page.test.tsx @@ -0,0 +1,32 @@ +import { render, screen } from "@testing-library/react"; +import { afterEach, expect, it, vi } from "vitest"; +const mocks = vi.hoisted(() => ({ + getTranslations: vi.fn(), + requireAuthenticatedUser: vi.fn(), +})); +vi.mock("next-intl/server", () => ({ getTranslations: mocks.getTranslations })); +vi.mock("@/features/auth/require-session", () => ({ + requireAuthenticatedUser: mocks.requireAuthenticatedUser, +})); +vi.mock("@/features/listings/listing-dashboard", () => ({ + ListingDashboard: ({ locale }: { locale: string }) => ( +
{locale}
+ ), +})); +import ListingsPage, { dynamic } from "./page"; +afterEach(() => { + mocks.getTranslations.mockReset(); + mocks.requireAuthenticatedUser.mockReset(); +}); +it("protects and renders dynamic localized listings page", async () => { + mocks.requireAuthenticatedUser.mockResolvedValue("verified"); + mocks.getTranslations.mockResolvedValue((key: string) => key); + render( + await ListingsPage({ + params: Promise.resolve({ locale: "pt-PT" }), + } as never), + ); + expect(dynamic).toBe("force-dynamic"); + expect(mocks.requireAuthenticatedUser).toHaveBeenCalledWith("pt-PT"); + expect(screen.getByTestId("listing-dashboard")).toHaveTextContent("pt-PT"); +}); diff --git a/frontend/src/app/[locale]/account/listings/page.tsx b/frontend/src/app/[locale]/account/listings/page.tsx new file mode 100644 index 0000000..f007675 --- /dev/null +++ b/frontend/src/app/[locale]/account/listings/page.tsx @@ -0,0 +1,52 @@ +import { hasLocale } from "next-intl"; +import { getTranslations } from "next-intl/server"; +import { notFound } from "next/navigation"; +import { requireAuthenticatedUser } from "@/features/auth/require-session"; +import { ListingDashboard } from "@/features/listings/listing-dashboard"; +import { routing } from "@/i18n/routing"; +export const dynamic = "force-dynamic"; +export default async function ListingsPage({ + params, +}: { + params: Promise<{ locale: string }>; +}) { + const { locale } = await params; + if (!hasLocale(routing.locales, locale)) notFound(); + await requireAuthenticatedUser(locale); + const t = await getTranslations("Listings"); + const keys = [ + "title", + "description", + "newListing", + "create", + "submit", + "pause", + "archive", + "loading", + "error", + "retry", + "empty", + "saved", + "titleLabel", + "descriptionLabel", + "categoryLabel", + "localityLabel", + "priceLabel", + ] as const; + const copy = Object.fromEntries(keys.map((k) => [k, t(k)])) as Record< + (typeof keys)[number], + string + >; + return ( +
+
+ +
+
+ ); +} diff --git a/frontend/src/app/[locale]/account/messages/page.tsx b/frontend/src/app/[locale]/account/messages/page.tsx new file mode 100644 index 0000000..048d724 --- /dev/null +++ b/frontend/src/app/[locale]/account/messages/page.tsx @@ -0,0 +1,37 @@ +import { hasLocale } from "next-intl"; +import { getTranslations } from "next-intl/server"; +import { notFound } from "next/navigation"; + +import { requireAuthenticatedUser } from "@/features/auth/require-session"; +import { MessagingInbox } from "@/features/messaging/messaging-inbox"; +import { routing } from "@/i18n/routing"; + +export const dynamic = "force-dynamic"; +export default async function MessagesPage({ + params, +}: PageProps<"/[locale]/account/messages">) { + const { locale } = await params; + if (!hasLocale(routing.locales, locale)) notFound(); + await requireAuthenticatedUser(locale); + const t = await getTranslations({ locale, namespace: "Messaging" }); + return ( +
+
+ +
+
+ ); +} diff --git a/frontend/src/app/[locale]/account/notifications/page.tsx b/frontend/src/app/[locale]/account/notifications/page.tsx new file mode 100644 index 0000000..d487eb0 --- /dev/null +++ b/frontend/src/app/[locale]/account/notifications/page.tsx @@ -0,0 +1,50 @@ +import { hasLocale } from "next-intl"; +import { getTranslations } from "next-intl/server"; +import { notFound } from "next/navigation"; + +import { requireAuthenticatedUser } from "@/features/auth/require-session"; +import { NotificationsPanel } from "@/features/messaging/notifications-panel"; +import { routing } from "@/i18n/routing"; + +export const dynamic = "force-dynamic"; +export default async function NotificationsPage({ + params, +}: PageProps<"/[locale]/account/notifications">) { + const { locale } = await params; + if (!hasLocale(routing.locales, locale)) notFound(); + await requireAuthenticatedUser(locale); + const t = await getTranslations({ locale, namespace: "Notifications" }); + return ( +
+
+ +
+
+ ); +} diff --git a/frontend/src/app/[locale]/account/page.test.tsx b/frontend/src/app/[locale]/account/page.test.tsx new file mode 100644 index 0000000..5d9748f --- /dev/null +++ b/frontend/src/app/[locale]/account/page.test.tsx @@ -0,0 +1,90 @@ +import { render, screen } from "@testing-library/react"; +import { afterEach, describe, expect, it, vi } from "vitest"; + +const mocks = vi.hoisted(() => ({ + currentUserIsSoleAdministrator: vi.fn(), + getTranslations: vi.fn(), + requireAuthenticatedUser: vi.fn(), +})); + +vi.mock("next-intl/server", () => ({ + getTranslations: mocks.getTranslations, +})); +vi.mock("@/features/auth/require-session", () => ({ + requireAuthenticatedUser: mocks.requireAuthenticatedUser, +})); +vi.mock("@/features/auth/sole-administrator", () => ({ + currentUserIsSoleAdministrator: mocks.currentUserIsSoleAdministrator, +})); +vi.mock("@/features/account/account-capabilities-card", () => ({ + AccountCapabilitiesCard: ({ copy }: { copy: { providerLabel: string } }) => ( +
{copy.providerLabel}
+ ), +})); + +import AccountPage, { dynamic } from "./page"; + +afterEach(() => { + mocks.getTranslations.mockReset(); + mocks.currentUserIsSoleAdministrator.mockReset(); + mocks.requireAuthenticatedUser.mockReset(); +}); + +describe("AccountPage", () => { + it("is explicitly dynamically rendered because it resolves request-scoped identity", () => { + expect(dynamic).toBe("force-dynamic"); + }); + + it("requires a verified session before rendering the localized account confirmation", async () => { + mocks.requireAuthenticatedUser.mockResolvedValue("user_verified_subject"); + mocks.currentUserIsSoleAdministrator.mockResolvedValue(true); + mocks.getTranslations.mockResolvedValue( + (key: string) => + ({ + "capabilities.customerDescription": + "A sua conta pode sempre procurar prestadores.", + "capabilities.customerLabel": "Encontrar serviços", + "capabilities.description": + "Escolha se também pretende disponibilizar serviços.", + "capabilities.disabled": "Inativo", + "capabilities.enabled": "Ativo", + "capabilities.loadError": + "Não foi possível carregar as capacidades da conta.", + "capabilities.loading": "A carregar as capacidades da conta…", + "capabilities.manageProvider": "Gerir perfil de prestador", + "capabilities.manageModeration": "Aprovar anúncios", + "capabilities.managePaymentsAdmin": "Gerir pagamentos e disputas", + "capabilities.managePayouts": "Pagamentos e recebimentos", + "capabilities.providerDescription": + "Ative esta opção para preparar o seu perfil de prestador.", + "capabilities.providerLabel": "Disponibilizar serviços", + "capabilities.retry": "Tentar novamente", + "capabilities.saving": "A guardar…", + "capabilities.title": "Como utiliza a Vila", + description: "A sua sessão está ativa.", + title: "Conta Vila", + })[key], + ); + + render( + await AccountPage({ + params: Promise.resolve({ locale: "pt-PT" }), + } as never), + ); + + expect(mocks.requireAuthenticatedUser).toHaveBeenCalledWith("pt-PT"); + expect( + screen.getByRole("heading", { name: "Conta Vila" }), + ).toBeInTheDocument(); + expect(screen.getByText("A sua sessão está ativa.")).toBeInTheDocument(); + expect(screen.getByTestId("account-capabilities-card")).toHaveTextContent( + "Disponibilizar serviços", + ); + expect( + screen.getByRole("link", { name: "Aprovar anúncios" }), + ).toHaveAttribute("href", "/pt-PT/admin/listings"); + expect( + screen.getByRole("link", { name: "Gerir pagamentos e disputas" }), + ).toHaveAttribute("href", "/pt-PT/admin/payments"); + }); +}); diff --git a/frontend/src/app/[locale]/account/page.tsx b/frontend/src/app/[locale]/account/page.tsx new file mode 100644 index 0000000..6eef1a8 --- /dev/null +++ b/frontend/src/app/[locale]/account/page.tsx @@ -0,0 +1,128 @@ +import { getTranslations } from "next-intl/server"; +import { hasLocale } from "next-intl"; +import { notFound } from "next/navigation"; + +import { AccountCapabilitiesCard } from "@/features/account/account-capabilities-card"; +import { requireAuthenticatedUser } from "@/features/auth/require-session"; +import { currentUserIsSoleAdministrator } from "@/features/auth/sole-administrator"; +import { routing } from "@/i18n/routing"; + +export const dynamic = "force-dynamic"; + +type AccountPageProps = { + params: Promise<{ locale: string }>; +}; + +export default async function AccountPage({ params }: AccountPageProps) { + const { locale } = await params; + + if (!hasLocale(routing.locales, locale)) { + notFound(); + } + + const userId = await requireAuthenticatedUser(locale); + const soleAdministrator = await currentUserIsSoleAdministrator(userId); + const t = await getTranslations("Account"); + const capabilityCopy = { + title: t("capabilities.title"), + description: t("capabilities.description"), + customerLabel: t("capabilities.customerLabel"), + customerDescription: t("capabilities.customerDescription"), + providerLabel: t("capabilities.providerLabel"), + providerDescription: t("capabilities.providerDescription"), + enabled: t("capabilities.enabled"), + disabled: t("capabilities.disabled"), + loading: t("capabilities.loading"), + saving: t("capabilities.saving"), + loadError: t("capabilities.loadError"), + retry: t("capabilities.retry"), + manageProvider: t("capabilities.manageProvider"), + manageListings: t("capabilities.manageListings"), + }; + + return ( +
+
+
+

Vila

+

+ {t("title")} +

+

{t("description")}

+
+ +
+
+ ); +} diff --git a/frontend/src/app/[locale]/account/payouts/page.tsx b/frontend/src/app/[locale]/account/payouts/page.tsx new file mode 100644 index 0000000..e5622c4 --- /dev/null +++ b/frontend/src/app/[locale]/account/payouts/page.tsx @@ -0,0 +1,41 @@ +import { hasLocale } from "next-intl"; +import { getTranslations } from "next-intl/server"; +import { notFound } from "next/navigation"; + +import { requireAuthenticatedUser } from "@/features/auth/require-session"; +import { PayoutDashboard } from "@/features/payments/payout-dashboard"; +import { routing } from "@/i18n/routing"; + +export const dynamic = "force-dynamic"; + +export default async function PayoutsPage({ + params, +}: PageProps<"/[locale]/account/payouts">) { + const { locale } = await params; + if (!hasLocale(routing.locales, locale)) notFound(); + await requireAuthenticatedUser(locale); + const t = await getTranslations({ locale, namespace: "Payouts" }); + return ( +
+
+ +
+
+ ); +} diff --git a/frontend/src/app/[locale]/account/provider-profile/page.test.tsx b/frontend/src/app/[locale]/account/provider-profile/page.test.tsx new file mode 100644 index 0000000..01484d0 --- /dev/null +++ b/frontend/src/app/[locale]/account/provider-profile/page.test.tsx @@ -0,0 +1,33 @@ +import { render, screen } from "@testing-library/react"; +import { afterEach, expect, it, vi } from "vitest"; + +const mocks = vi.hoisted(() => ({ + getTranslations: vi.fn(), + requireAuthenticatedUser: vi.fn(), +})); +vi.mock("next-intl/server", () => ({ getTranslations: mocks.getTranslations })); +vi.mock("@/features/auth/require-session", () => ({ + requireAuthenticatedUser: mocks.requireAuthenticatedUser, +})); +vi.mock("@/features/provider/provider-profile-form", () => ({ + ProviderProfileForm: ({ locale }: { locale: string }) => ( +
{locale}
+ ), +})); +import ProviderProfilePage, { dynamic } from "./page"; +afterEach(() => { + mocks.getTranslations.mockReset(); + mocks.requireAuthenticatedUser.mockReset(); +}); +it("protects and renders the localized dynamic provider profile page", async () => { + mocks.requireAuthenticatedUser.mockResolvedValue("verified"); + mocks.getTranslations.mockResolvedValue((key: string) => key); + render( + await ProviderProfilePage({ + params: Promise.resolve({ locale: "pt-PT" }), + } as never), + ); + expect(dynamic).toBe("force-dynamic"); + expect(mocks.requireAuthenticatedUser).toHaveBeenCalledWith("pt-PT"); + expect(screen.getByTestId("provider-form")).toHaveTextContent("pt-PT"); +}); diff --git a/frontend/src/app/[locale]/account/provider-profile/page.tsx b/frontend/src/app/[locale]/account/provider-profile/page.tsx new file mode 100644 index 0000000..dbb3365 --- /dev/null +++ b/frontend/src/app/[locale]/account/provider-profile/page.tsx @@ -0,0 +1,54 @@ +import { hasLocale } from "next-intl"; +import { getTranslations } from "next-intl/server"; +import { notFound } from "next/navigation"; + +import { requireAuthenticatedUser } from "@/features/auth/require-session"; +import { ProviderProfileForm } from "@/features/provider/provider-profile-form"; +import { routing } from "@/i18n/routing"; + +export const dynamic = "force-dynamic"; +export default async function ProviderProfilePage({ + params, +}: { + params: Promise<{ locale: string }>; +}) { + const { locale } = await params; + if (!hasLocale(routing.locales, locale)) notFound(); + await requireAuthenticatedUser(locale); + const t = await getTranslations("ProviderProfile"); + const keys = [ + "title", + "description", + "displayName", + "providerType", + "individual", + "professional", + "business", + "bio", + "primaryLocality", + "serviceLocalities", + "languages", + "travelRadius", + "serviceModes", + "travels", + "receives", + "remote", + "save", + "saving", + "loading", + "error", + "retry", + "saved", + ] as const; + const copy = Object.fromEntries(keys.map((key) => [key, t(key)])) as Record< + (typeof keys)[number], + string + >; + return ( +
+
+ +
+
+ ); +} diff --git a/frontend/src/app/[locale]/account/quotations/page.tsx b/frontend/src/app/[locale]/account/quotations/page.tsx new file mode 100644 index 0000000..2264ec2 --- /dev/null +++ b/frontend/src/app/[locale]/account/quotations/page.tsx @@ -0,0 +1,53 @@ +import { hasLocale } from "next-intl"; +import { getTranslations } from "next-intl/server"; +import { notFound } from "next/navigation"; +import { requireAuthenticatedUser } from "@/features/auth/require-session"; +import { QuotationsDashboard } from "@/features/quotations/quotations-dashboard"; +import { routing } from "@/i18n/routing"; +export const dynamic = "force-dynamic"; +export default async function QuotationsPage({ + params, +}: PageProps<"/[locale]/account/quotations">) { + const { locale } = await params; + if (!hasLocale(routing.locales, locale)) notFound(); + await requireAuthenticatedUser(locale); + const t = await getTranslations({ locale, namespace: "Quotations" }); + return ( +
+
+ +
+
+ ); +} diff --git a/frontend/src/app/[locale]/account/reviews/page.tsx b/frontend/src/app/[locale]/account/reviews/page.tsx new file mode 100644 index 0000000..e11ce17 --- /dev/null +++ b/frontend/src/app/[locale]/account/reviews/page.tsx @@ -0,0 +1,40 @@ +import { hasLocale } from "next-intl"; +import { getTranslations } from "next-intl/server"; +import { notFound } from "next/navigation"; +import { requireAuthenticatedUser } from "@/features/auth/require-session"; +import { ReviewsDashboard } from "@/features/reviews/reviews-dashboard"; +import { routing } from "@/i18n/routing"; +export const dynamic = "force-dynamic"; +export default async function ReviewsPage({ + params, +}: PageProps<"/[locale]/account/reviews">) { + const { locale } = await params; + if (!hasLocale(routing.locales, locale)) notFound(); + await requireAuthenticatedUser(locale); + const t = await getTranslations({ locale, namespace: "Reviews" }); + return ( +
+
+ +
+
+ ); +} diff --git a/frontend/src/app/[locale]/admin/listings/page.tsx b/frontend/src/app/[locale]/admin/listings/page.tsx new file mode 100644 index 0000000..b5f6339 --- /dev/null +++ b/frontend/src/app/[locale]/admin/listings/page.tsx @@ -0,0 +1,5 @@ +import ModerationListingsPage from "../../moderation/listings/page"; + +export const dynamic = "force-dynamic"; + +export default ModerationListingsPage; diff --git a/frontend/src/app/[locale]/admin/page.tsx b/frontend/src/app/[locale]/admin/page.tsx new file mode 100644 index 0000000..85bd1ed --- /dev/null +++ b/frontend/src/app/[locale]/admin/page.tsx @@ -0,0 +1,43 @@ +import { hasLocale } from "next-intl"; +import { getTranslations } from "next-intl/server"; +import { notFound } from "next/navigation"; +import { requireAuthenticatedUser } from "@/features/auth/require-session"; +import { AdministrationDashboardView } from "@/features/administration/administration-dashboard"; +import { routing } from "@/i18n/routing"; +export const dynamic = "force-dynamic"; +export default async function AdministrationPage({ + params, +}: PageProps<"/[locale]/admin">) { + const { locale } = await params; + if (!hasLocale(routing.locales, locale)) notFound(); + await requireAuthenticatedUser(locale); + const t = await getTranslations({ locale, namespace: "Administration" }); + return ( +
+
+ +
+
+ ); +} diff --git a/frontend/src/app/[locale]/admin/payments/page.tsx b/frontend/src/app/[locale]/admin/payments/page.tsx new file mode 100644 index 0000000..fcfcc46 --- /dev/null +++ b/frontend/src/app/[locale]/admin/payments/page.tsx @@ -0,0 +1,45 @@ +import { hasLocale } from "next-intl"; +import { getTranslations } from "next-intl/server"; +import { notFound } from "next/navigation"; + +import { requireSoleAdministrator } from "@/features/auth/sole-administrator"; +import { PaymentAdministration } from "@/features/payments/payment-administration"; +import { routing } from "@/i18n/routing"; + +export const dynamic = "force-dynamic"; + +export default async function AdministrativePaymentsPage({ + params, +}: PageProps<"/[locale]/admin/payments">) { + const { locale } = await params; + if (!hasLocale(routing.locales, locale)) notFound(); + await requireSoleAdministrator(locale); + const t = await getTranslations({ + locale, + namespace: "PaymentAdministration", + }); + return ( +
+
+ +
+
+ ); +} diff --git a/frontend/src/app/[locale]/discover/page.tsx b/frontend/src/app/[locale]/discover/page.tsx new file mode 100644 index 0000000..b05774e --- /dev/null +++ b/frontend/src/app/[locale]/discover/page.tsx @@ -0,0 +1,58 @@ +import { hasLocale } from "next-intl"; +import { getTranslations } from "next-intl/server"; +import { notFound } from "next/navigation"; + +import { PublicDiscovery } from "@/features/discovery/public-discovery"; +import { routing } from "@/i18n/routing"; + +export const dynamic = "force-dynamic"; + +export default async function DiscoverPage({ + params, +}: PageProps<"/[locale]/discover">) { + const { locale } = await params; + if (!hasLocale(routing.locales, locale)) notFound(); + const t = await getTranslations({ locale, namespace: "Discovery" }); + return ( +
+
+ +
+
+ ); +} diff --git a/frontend/src/app/[locale]/error.tsx b/frontend/src/app/[locale]/error.tsx index 457e9ac..cf73db5 100644 --- a/frontend/src/app/[locale]/error.tsx +++ b/frontend/src/app/[locale]/error.tsx @@ -16,20 +16,16 @@ export default function ErrorPage({ error, reset }: ErrorPageProps) { }, [error]); return ( -
-
+
+

- Juntly + Vila

{t("title")}

{t("description")}

-
diff --git a/frontend/src/app/[locale]/icon.svg b/frontend/src/app/[locale]/icon.svg index ccb1f74..a78608d 100644 --- a/frontend/src/app/[locale]/icon.svg +++ b/frontend/src/app/[locale]/icon.svg @@ -1,5 +1,5 @@ - - - - + + + + diff --git a/frontend/src/app/[locale]/layout.tsx b/frontend/src/app/[locale]/layout.tsx index 519fb4f..aa9ea01 100644 --- a/frontend/src/app/[locale]/layout.tsx +++ b/frontend/src/app/[locale]/layout.tsx @@ -1,9 +1,12 @@ +import { ClerkProvider } from "@clerk/nextjs"; import type { Metadata } from "next"; import { Geist, Geist_Mono } from "next/font/google"; import { hasLocale, NextIntlClientProvider } from "next-intl"; import { getMessages, getTranslations } from "next-intl/server"; import { notFound } from "next/navigation"; +import { getClerkLocalization } from "@/features/auth/clerk-localization"; +import { MarketplaceNavigation } from "@/features/navigation/marketplace-navigation"; import { routing } from "@/i18n/routing"; import "../globals.css"; @@ -36,7 +39,7 @@ export async function generateMetadata({ return { title: t("title"), description: t("description"), - applicationName: "Juntly", + applicationName: "Vila", alternates: { languages: Object.fromEntries( routing.locales.map((supportedLocale) => [ @@ -59,6 +62,8 @@ export default async function LocaleLayout({ } const messages = await getMessages({ locale }); + const navigation = await getTranslations({ locale, namespace: "Navigation" }); + const auth = await getTranslations({ locale, namespace: "Auth" }); return ( - - {children} - + + {navigation("skipToContent")} + + + + +
+ {children} +
+
+
); diff --git a/frontend/src/app/[locale]/legal/[document]/page.tsx b/frontend/src/app/[locale]/legal/[document]/page.tsx new file mode 100644 index 0000000..080720d --- /dev/null +++ b/frontend/src/app/[locale]/legal/[document]/page.tsx @@ -0,0 +1,330 @@ +import { hasLocale } from "next-intl"; +import { notFound } from "next/navigation"; + +import { routing, type AppLocale } from "@/i18n/routing"; + +const documents = [ + "terms", + "privacy", + "refund-policy", + "payment-policy", +] as const; +type Document = (typeof documents)[number]; + +type Policy = { + title: string; + updated: string; + intro: string; + sections: Array<{ title: string; body: string }>; +}; + +const policy: Record> = { + "pt-PT": { + terms: { + title: "Termos da Vila", + updated: "Atualizado em 2 de setembro de 2026", + intro: + "A Vila é um marketplace que aproxima clientes e prestadores independentes. Ao utilizar a plataforma, aceita estes termos e as políticas de pagamento e reembolso.", + sections: [ + { + title: "Papel da plataforma", + body: "A Vila facilita descoberta, comunicação, reservas e pagamentos. O prestador continua responsável pela descrição, legalidade, qualidade e execução do serviço. A Vila não é o empregador nem o executante do serviço.", + }, + { + title: "Contas e anúncios", + body: "As informações devem ser verdadeiras, atuais e não enganosas. Anúncios são sujeitos a revisão e podem ser rejeitados, suspensos ou removidos por segurança, fraude, ilegalidade ou incumprimento.", + }, + { + title: "Reservas", + body: "Preço, data, local e âmbito devem ser confirmados antes do pagamento. Alterações materiais exigem acordo entre cliente e prestador. As ações ficam registadas para segurança e resolução de litígios.", + }, + { + title: "Lei e contacto", + body: "Aplicam-se os direitos imperativos do consumidor e a legislação portuguesa e europeia aplicável. Questões podem ser enviadas para source.sensei1205@gmail.com. A identificação legal e fiscal completa do operador deve constar do aviso comercial antes da ativação pública de pagamentos.", + }, + ], + }, + privacy: { + title: "Privacidade", + updated: "Atualizado em 2 de setembro de 2026", + intro: + "A Vila minimiza os dados que recolhe e separa identidade, contactos privados, pagamentos e informação pública.", + sections: [ + { + title: "Dados tratados", + body: "Tratamos identidade de conta, perfil, anúncios, conversas, reservas, eventos de moderação e referências de pagamento. Não armazenamos números de cartão nem dados bancários; a Clerk processa identidade e a Stripe processa pagamentos e verificação de recebimentos.", + }, + { + title: "Finalidades e conservação", + body: "Os dados servem para prestar o serviço, prevenir fraude, cumprir obrigações legais, resolver litígios e manter registos financeiros. A conservação deve limitar-se ao período necessário e aos prazos legais aplicáveis.", + }, + { + title: "Direitos", + body: "Pode pedir acesso, correção, exportação, oposição ou eliminação quando legalmente possível através de source.sensei1205@gmail.com. Dados exigidos por obrigações financeiras, fiscais ou de segurança podem ter de ser conservados.", + }, + ], + }, + "refund-policy": { + title: "Cancelamentos e reembolsos", + updated: "Atualizado em 2 de setembro de 2026", + intro: + "Os pedidos são avaliados com base no estado da reserva, execução do serviço, acordo entre as partes e direitos legais aplicáveis.", + sections: [ + { + title: "Antes do serviço", + body: "Um cancelamento antes do início pode originar reembolso total ou parcial conforme custos já incorridos e termos claramente aceites na reserva. Nenhuma regra reduz direitos imperativos do consumidor.", + }, + { + title: "Serviço iniciado ou concluído", + body: "Reembolsos após o início exigem análise do trabalho executado, evidência e acordo ou decisão administrativa. Serviços personalizados ou iniciados com consentimento podem ter regras legais específicas.", + }, + { + title: "Processamento", + body: "Reembolsos aprovados são enviados à Stripe para o método de pagamento original. O prazo bancário depende do método e da instituição. Fraude, abuso e chargebacks podem suspender pagamentos e recebimentos durante a investigação.", + }, + ], + }, + "payment-policy": { + title: "Pagamentos e recebimentos", + updated: "Atualizado em 2 de setembro de 2026", + intro: + "A Vila utiliza Checkout e Connect alojados pela Stripe para que dados financeiros sensíveis não passem pelos formulários da Vila.", + sections: [ + { + title: "Preço e taxas", + body: "Antes de continuar para a Stripe, o cliente vê o valor total, a taxa da plataforma e o valor previsto para o prestador. Os valores são calculados no servidor em euros e não podem ser escolhidos pelo navegador.", + }, + { + title: "Métodos e impostos", + body: "Os métodos disponíveis, incluindo MB WAY quando elegível, dependem da conta Stripe, país, moeda, montante e configuração. A Stripe pode recolher morada e identificação fiscal e gerar documentação de pagamento; o cumprimento e entrega de declarações fiscais permanecem responsabilidade das partes aplicáveis.", + }, + { + title: "Recebimentos e litígios", + body: "Prestadores concluem verificação diretamente na Stripe. Pagamentos, reembolsos, disputas e chargebacks são sincronizados por webhooks assinados. A Vila pode suspender recebimentos enquanto uma disputa está aberta ou quando a Stripe limita a conta.", + }, + ], + }, + }, + en: {} as Record, + es: {} as Record, +}; +policy.en = translateEnglish(); +policy.es = translateSpanish(); + +export function generateStaticParams() { + return routing.locales.flatMap((locale) => + documents.map((document) => ({ locale, document })), + ); +} + +export default async function LegalPage({ + params, +}: PageProps<"/[locale]/legal/[document]">) { + const { locale, document } = await params; + if ( + !hasLocale(routing.locales, locale) || + !documents.includes(document as Document) + ) + notFound(); + const value = policy[locale][document as Document]; + return ( +
+
+

Vila

+

+ {value.title} +

+

{value.updated}

+

{value.intro}

+
+ {value.sections.map((section) => ( +
+

{section.title}

+

{section.body}

+
+ ))} +
+
+
+ ); +} + +function translateEnglish(): Record { + const updated = "Updated 2 September 2026"; + return { + terms: { + title: "Vila terms", + updated, + intro: + "Vila is a marketplace connecting customers with independent providers. By using it, you accept these terms and the payment and refund policies.", + sections: [ + { + title: "Platform role", + body: "Vila facilitates discovery, communication, bookings, and payments. Providers remain responsible for the description, legality, quality, and delivery of their services. Vila is neither the provider nor their employer.", + }, + { + title: "Accounts and listings", + body: "Information must be accurate, current, and not misleading. Listings are reviewed and may be rejected, suspended, or removed for safety, fraud, illegality, or breach.", + }, + { + title: "Bookings", + body: "Price, date, private location, and scope must be confirmed before payment. Material changes require agreement. Actions are recorded for safety and dispute resolution.", + }, + { + title: "Law and contact", + body: "Mandatory consumer rights and applicable Portuguese and European law remain in force. Contact source.sensei1205@gmail.com. The operator's complete legal and tax identity must appear in the commercial notice before public payment activation.", + }, + ], + }, + privacy: { + title: "Privacy", + updated, + intro: + "Vila minimizes data and separates identity, private contact details, payments, and public information.", + sections: [ + { + title: "Data processed", + body: "We process account identity, profiles, listings, conversations, bookings, moderation events, and payment references. We do not store card numbers or bank details; Clerk processes identity and Stripe processes payments and payout verification.", + }, + { + title: "Purpose and retention", + body: "Data is used to provide the service, prevent fraud, meet legal obligations, resolve disputes, and maintain financial records. Retention is limited to necessity and applicable legal periods.", + }, + { + title: "Your rights", + body: "Request access, correction, export, objection, or deletion where legally available through source.sensei1205@gmail.com. Financial, tax, fraud-prevention, and security records may need to be retained.", + }, + ], + }, + "refund-policy": { + title: "Cancellations and refunds", + updated, + intro: + "Requests are assessed from booking state, work performed, agreement between the parties, and applicable statutory rights.", + sections: [ + { + title: "Before work starts", + body: "Cancellation before work begins may qualify for a full or partial refund according to costs already incurred and terms clearly accepted in the booking. Mandatory consumer rights are not reduced.", + }, + { + title: "Started or completed work", + body: "Refunds after work starts require review of delivery, evidence, and agreement or an administrative decision. Customized services or services begun with consent may have specific legal rules.", + }, + { + title: "Processing", + body: "Approved refunds are submitted to Stripe for the original payment method. Bank timing depends on the method and institution. Fraud, abuse, and chargebacks can suspend payments and payouts during investigation.", + }, + ], + }, + "payment-policy": { + title: "Payments and payouts", + updated, + intro: + "Vila uses Stripe-hosted Checkout and Connect so sensitive financial details never pass through Vila forms.", + sections: [ + { + title: "Price and fees", + body: "Before continuing to Stripe, customers see the total, platform fee, and expected provider payout. Values are calculated server-side in euros and cannot be selected by the browser.", + }, + { + title: "Methods and tax", + body: "Available methods, including MB WAY when eligible, depend on Stripe account, country, currency, amount, and settings. Stripe can collect addresses and tax IDs and create payment documents; applicable filing and tax obligations remain with the relevant parties.", + }, + { + title: "Payouts and disputes", + body: "Providers complete verification directly with Stripe. Payments, refunds, disputes, and chargebacks synchronize through signed webhooks. Vila may suspend payouts while a dispute is open or Stripe restricts an account.", + }, + ], + }, + }; +} + +function translateSpanish(): Record { + const updated = "Actualizado el 2 de septiembre de 2026"; + return { + terms: { + title: "Términos de Vila", + updated, + intro: + "Vila es un marketplace que conecta clientes con profesionales independientes. Al utilizarlo, aceptas estos términos y las políticas de pago y reembolso.", + sections: [ + { + title: "Función de la plataforma", + body: "Vila facilita el descubrimiento, la comunicación, las reservas y los pagos. El profesional sigue siendo responsable de la descripción, legalidad, calidad y prestación del servicio. Vila no es el profesional ni su empleador.", + }, + { + title: "Cuentas y anuncios", + body: "La información debe ser veraz, actual y no engañosa. Los anuncios se revisan y pueden rechazarse, suspenderse o eliminarse por seguridad, fraude, ilegalidad o incumplimiento.", + }, + { + title: "Reservas", + body: "El precio, la fecha, la ubicación privada y el alcance deben confirmarse antes del pago. Los cambios importantes requieren acuerdo. Las acciones quedan registradas para seguridad y resolución de conflictos.", + }, + { + title: "Ley y contacto", + body: "Siguen vigentes los derechos obligatorios de los consumidores y la legislación portuguesa y europea aplicable. Contacto: source.sensei1205@gmail.com. La identidad legal y fiscal completa del operador debe aparecer en el aviso comercial antes de activar pagos públicos.", + }, + ], + }, + privacy: { + title: "Privacidad", + updated, + intro: + "Vila minimiza los datos y separa identidad, contactos privados, pagos e información pública.", + sections: [ + { + title: "Datos tratados", + body: "Tratamos identidad de cuenta, perfiles, anuncios, conversaciones, reservas, eventos de moderación y referencias de pago. No almacenamos números de tarjeta ni datos bancarios; Clerk trata la identidad y Stripe los pagos y la verificación de cobros.", + }, + { + title: "Finalidad y conservación", + body: "Los datos se usan para prestar el servicio, prevenir fraude, cumplir obligaciones legales, resolver conflictos y conservar registros financieros. La conservación se limita a lo necesario y a los plazos legales aplicables.", + }, + { + title: "Tus derechos", + body: "Puedes solicitar acceso, rectificación, exportación, oposición o supresión cuando sea legalmente posible mediante source.sensei1205@gmail.com. Puede ser necesario conservar datos financieros, fiscales, de prevención de fraude y seguridad.", + }, + ], + }, + "refund-policy": { + title: "Cancelaciones y reembolsos", + updated, + intro: + "Las solicitudes se evalúan según el estado de la reserva, el trabajo realizado, el acuerdo entre las partes y los derechos legales aplicables.", + sections: [ + { + title: "Antes de comenzar", + body: "Una cancelación antes del inicio puede dar derecho a un reembolso total o parcial según los costes ya incurridos y las condiciones aceptadas claramente en la reserva. No se reducen los derechos obligatorios del consumidor.", + }, + { + title: "Servicio iniciado o finalizado", + body: "Los reembolsos tras el inicio requieren revisar la prestación, las pruebas y el acuerdo o una decisión administrativa. Los servicios personalizados o iniciados con consentimiento pueden tener reglas legales específicas.", + }, + { + title: "Procesamiento", + body: "Los reembolsos aprobados se envían a Stripe para el método de pago original. El plazo bancario depende del método y la entidad. El fraude, abuso y los contracargos pueden suspender pagos y cobros durante la investigación.", + }, + ], + }, + "payment-policy": { + title: "Pagos y cobros", + updated, + intro: + "Vila utiliza Checkout y Connect alojados por Stripe para que los datos financieros sensibles no pasen por los formularios de Vila.", + sections: [ + { + title: "Precio y comisiones", + body: "Antes de continuar a Stripe, el cliente ve el total, la comisión de la plataforma y el cobro previsto del profesional. Los importes se calculan en el servidor en euros y el navegador no puede elegirlos.", + }, + { + title: "Métodos e impuestos", + body: "Los métodos disponibles, incluido MB WAY cuando corresponda, dependen de la cuenta Stripe, país, moneda, importe y configuración. Stripe puede recoger dirección e identificación fiscal y crear documentos de pago; las obligaciones fiscales aplicables siguen correspondiendo a las partes pertinentes.", + }, + { + title: "Cobros y conflictos", + body: "Los profesionales completan la verificación directamente con Stripe. Pagos, reembolsos, disputas y contracargos se sincronizan mediante webhooks firmados. Vila puede suspender cobros mientras exista una disputa o Stripe restrinja una cuenta.", + }, + ], + }, + }; +} diff --git a/frontend/src/app/[locale]/listings/[listingId]/page.tsx b/frontend/src/app/[locale]/listings/[listingId]/page.tsx new file mode 100644 index 0000000..e5ae842 --- /dev/null +++ b/frontend/src/app/[locale]/listings/[listingId]/page.tsx @@ -0,0 +1,45 @@ +import { hasLocale } from "next-intl"; +import { getTranslations } from "next-intl/server"; +import { notFound } from "next/navigation"; + +import { PublicListingDetail } from "@/features/discovery/public-listing-detail"; +import { routing } from "@/i18n/routing"; + +export const dynamic = "force-dynamic"; + +export default async function PublicListingPage({ + params, +}: PageProps<"/[locale]/listings/[listingId]">) { + const { locale, listingId } = await params; + if (!hasLocale(routing.locales, locale) || !uuid(listingId)) notFound(); + const t = await getTranslations({ locale, namespace: "PublicListing" }); + return ( +
+
+ +
+
+ ); +} + +function uuid(value: string): boolean { + return /^[0-9a-f]{8}(?:-[0-9a-f]{4}){3}-[0-9a-f]{12}$/i.test(value); +} diff --git a/frontend/src/app/[locale]/loading.tsx b/frontend/src/app/[locale]/loading.tsx index 3557d3e..b8fb5e5 100644 --- a/frontend/src/app/[locale]/loading.tsx +++ b/frontend/src/app/[locale]/loading.tsx @@ -5,13 +5,13 @@ export default async function LoadingPage() { return (
-
+