Compare commits

...

151 commits

Author SHA1 Message Date
mikestefanello
dfefe65ec0 Update dependencies.
Some checks are pending
Test / test (push) Waiting to run
2025-12-14 10:10:03 -05:00
mikestefanello
04f37f87e2 Re-generate ent code. 2025-08-15 09:58:39 -04:00
mikestefanello
db9fe90163 Update dependencies. 2025-08-15 09:56:50 -04:00
mikestefanello
acb12c393a Removed accidental user field inclusin. 2025-08-15 09:52:37 -04:00
mikestefanello
9d75c2a665 Fix enums in generated admin schema code. 2025-08-15 09:51:06 -04:00
mikestefanello
f154d42e67 Update dependencies. 2025-08-04 08:38:30 -04:00
Mike Stefanello
37104788e7
Generate ent schema in admin code. (#127) 2025-08-04 08:32:10 -04:00
mikestefanello
75c9c5be34 Support multiple forms on a single page. 2025-07-20 14:55:29 -04:00
mikestefanello
f634285909 Updated dependencies. 2025-07-18 09:01:48 -04:00
mikestefanello
1641ac51a6 Updated README. 2025-07-18 08:59:09 -04:00
Paulo Chaves
2eefb46e08
Set tailwindcss binary file name depending on current os (#125)
* Download tailwindcss arm64 binary if on macOS

* add tailwind binary download for macos x64 or arm64
2025-07-18 08:51:44 -04:00
mikestefanello
fd6218c0ad Fixed missing name attribute on select component. 2025-06-24 19:11:23 -04:00
Mike Stefanello
79a589da65
Swap Bulma for DaisyUI (Tailwind) (#111) 2025-06-17 20:19:58 -04:00
mikestefanello
2a546dfe3d Fix Safari cookies and CSRF. 2025-06-02 08:42:14 -04:00
Eduardo Zepeda
885a6ad7bf
Fix HTTPErrorHandler always returning 200 (#109) 2025-05-19 13:13:22 -04:00
mikestefanello
4c4a503cdc Added backlite UI to the admin panel. 2025-05-02 10:18:15 -04:00
mikestefanello
0d6e26ab39 Use memdb for in-memory sqlite dbs. 2025-04-22 09:09:56 -04:00
mikestefanello
e85ca0a93f Cleanup configuration. 2025-04-22 08:54:56 -04:00
mikestefanello
8d6f0b1261 Update dependencies. 2025-04-22 08:35:29 -04:00
Mike Stefanello
7dcf6bc039
Add dynamic admin panel for managing entities (#108) 2025-04-22 08:26:35 -04:00
mikestefanello
d91774313d Updated dependencies. 2025-04-10 09:11:44 -04:00
mikestefanello
efab433d5c Fixed HTMX listeners. 2025-04-01 10:23:32 -04:00
mikestefanello
9441014517 Cleaned up conditional rendering. 2025-03-29 09:40:04 -04:00
mikestefanello
fbb3fdc70d Log ui node cache failures. 2025-03-25 09:25:18 -04:00
mikestefanello
a7c6994062 Update dependencies. 2025-03-16 19:37:14 -04:00
Mike Stefanello
62d46c475c
Migrate from templates to Gomponents (#103) 2025-03-05 20:01:58 -05:00
mikestefanello
5cad4cbd33 Upgraded ent. 2025-02-18 19:27:00 -05:00
mikestefanello
633c60cdf5 Upgrade to Bulma 1.0.2. 2025-02-18 19:19:09 -05:00
mikestefanello
ca1de66033 Added file management. 2025-02-16 14:23:52 -05:00
mikestefanello
b808500a23 Use custom logger in main. 2025-02-16 09:47:15 -05:00
mikestefanello
b2ad889f56 Move web/task shutdown to container. Shutdown web before tasks. Add shutdown timeouts to config. 2025-02-16 09:39:29 -05:00
mikestefanello
a7006b3997 Start task runner before web server. 2025-02-12 22:01:46 -05:00
mikestefanello
d2d8c02a2f Use more secure cookie settings. 2025-02-12 22:01:30 -05:00
mikestefanello
02a58946f1 Updated test workflow go version. 2025-02-12 22:01:10 -05:00
mikestefanello
d736551869 Update dependencies. 2025-01-19 09:37:58 -05:00
Nick Krecklow
e3217fc852
use alpine v3 click.outside event name (#83)
Alpine v3 deprecates the v2 `click.away` event and marks it for possible future removal, replacing it with `click.outside`.

See v3 release notes: https://alpinejs.dev/upgrade-guide#away-replace-with-outside
2024-10-02 10:32:25 -04:00
Dimitri Balios
c244389971
Update Makefile (#80)
ent install -d flag is deprecated. -d=true is a no-op
2024-09-18 17:06:54 -04:00
mikestefanello
a9319559e1 Fixed pager page and pages value when no items present. 2024-09-13 09:16:20 -04:00
mikestefanello
f54d9f8b37 Switched to backlite for task queues. 2024-07-24 21:04:32 -04:00
mikestefanello
062d1f70be Updated documentation. 2024-06-24 19:03:42 -04:00
mikestefanello
73098499dd Upgrade to HTMX 2.0. 2024-06-22 11:07:43 -04:00
mikestefanello
6da0fcb7be Fixed doc code formatting. 2024-06-22 10:38:57 -04:00
Mike Stefanello
62c53a6b4d
Default to SQLite rather than Postgres & Redis (#72)
* Initial rough draft switch to sqlite.

* Rewrote cache implemenation.

* Provide typed tasks.

* Task cleanup.

* Use same db for tasks.

* Provide task queue registration and service container injection.

* Added optional delay to tasks. Pool buffers when encoding.

* Added tests for the task client and runner.

* Added handler examples for caching and tasks.

* Cleanup and documentation.

* Use make in workflow.

* Updated documentation.

* Updated documentation.
2024-06-22 10:34:26 -04:00
mikestefanello
11def45666 Log request URI rather than path. 2024-06-19 09:32:22 -04:00
mikestefanello
ca22f54c89 Added redirect package. 2024-06-16 11:30:11 -04:00
mikestefanello
6730b6a319 Added redirect support with query params. 2024-06-16 10:53:43 -04:00
mikestefanello
71f7de8771 Removed echo-contrib dependency. 2024-06-15 16:56:47 -04:00
Mike Stefanello
5531e0bec2
Move controller to the template renderer (#68) 2024-06-15 15:34:24 -04:00
mikestefanello
6a7070a729 Misc cleanup. 2024-06-15 09:09:36 -04:00
Mike Stefanello
9acf73a4d9
Replace Echo logger with slog. (#67)
* Replace Echo logger with slog.
2024-06-14 21:01:48 -04:00
Mike Stefanello
5ebd42d8f9
Improve form and template usage (#66)
* Improve form and template usage.
2024-06-14 12:35:35 -04:00
mikestefanello
7d85ff0b08 Updated documentation. 2024-06-10 08:28:00 -04:00
mikestefanello
afa8b5d2cc Fixed typo. 2024-06-09 21:42:43 -04:00
mikestefanello
28abc92e74 Easier form and form submission handling. 2024-06-09 21:39:04 -04:00
Mike Stefanello
400b9b36ba
Merge pull request #65 from mikestefanello/handlers-3
Switch from routes to self-registering handlers to group related routes.
2024-06-09 19:57:04 -04:00
mikestefanello
2c635b5c75 Switch from routes to self-registering handlers to group related routes. 2024-06-09 12:31:30 -04:00
mikestefanello
30389de16f Added radio form element example. 2024-05-18 16:19:27 -04:00
mikestefanello
0e204428b6 Updated actions go version. 2024-04-27 16:26:50 -04:00
mikestefanello
0783709b3c Update dependencies. 2024-04-27 16:23:44 -04:00
Mike Stefanello
a122851717
Merge pull request #60 from jordanstephens/patch-1
Fix a broken link
2024-04-01 08:54:28 -04:00
Jordan Stephens
114ed8444c
Fix a broken link 2024-03-30 18:47:33 -07:00
Mike Stefanello
f6a96e2025
Merge pull request #58 from saurori/cache-up
Update Cache related go modules go-redis to v9 and gocache to v4
2024-03-21 19:58:44 -04:00
Stefan Aurori
15739cc82e Rename lib_store and redis_store imports. Cache get remove redundant nil check. 2024-03-14 20:43:27 -04:00
Stefan Aurori
e8952e964c Update go-redis to v9 and gocache to v4 2024-03-10 17:23:03 -04:00
mikestefanello
b1c47426d9 Update crypto library. 2023-12-18 21:02:30 -05:00
mikestefanello
97cd9d0fee Move htmx template into layouts. 2023-12-18 20:59:40 -05:00
mikestefanello
c2b6928fb4 Use consts for route names and templates. 2023-12-16 11:07:20 -05:00
mikestefanello
5af18e2473 Added additional make commands. 2023-12-16 08:12:51 -05:00
mikestefanello
81c65fcc30 Restore local live reloading of templates. 2023-12-12 20:07:58 -05:00
mikestefanello
3df20c01a7 Replace template dir file path hack with embed directive. 2023-12-10 09:33:34 -05:00
mikestefanello
0879aaf21d Fix flaky context error test. 2023-11-15 20:14:00 -05:00
Mike Stefanello
ba96445704
Merge pull request #48 from gedw99/main
fixes Issue 45 - cant generate using ent and then run
2023-11-15 20:06:58 -05:00
ged wed
42c7957bf4 fixes 45 - cant generate 2023-11-14 12:46:57 +01:00
Mike Stefanello
44da53d99f
Merge pull request #44 from mips171/mips171/bump_bulma_htmx
bump Bulma and HTMX
2023-10-01 18:52:36 -04:00
Nicholas Smith
6a3f4b5ff2 bump Bulma and HTMX 2023-10-01 13:23:42 +10:00
mikestefanello
68b850c87b Merge branch 'main' of github.com:mikestefanello/pagoda into main 2023-09-17 11:38:26 -04:00
mikestefanello
4867b9bab8 Pin PG version to 15 to avoid broken schema migrations. 2023-09-17 11:38:10 -04:00
Mike Stefanello
7eae6309fd
Merge pull request #35 from testwill/ioutil
chore: remove refs to deprecated io/ioutil
2023-09-17 11:18:38 -04:00
guoguangwu
3026ec218e chore: remove refs to deprecated io/ioutil 2023-07-26 11:08:24 +08:00
Mike Stefanello
f688bff1d2
Merge pull request #33 from Jimmy99/patch-2
Update README.md
2023-05-25 15:55:50 -04:00
Dimitri Balios
575bf805e2
Update README.md
Fix typo
2023-05-25 21:50:20 +02:00
Mike Stefanello
1f42ce7db8
Merge pull request #32 from Jimmy99/patch-1
Update README.md for typo
2023-05-22 18:13:57 -04:00
Dimitri Balios
d875747e31
Update README.md for typo
Typo fix "faciliate" to "facilitate"
2023-05-22 23:10:28 +02:00
mikestefanello
fec8698526 Update dependencies. 2023-02-25 07:49:58 -05:00
Mike Stefanello
6d3dfe2531
Merge pull request #28 from Jimmy99/main
Update Makefile to fix deprecated ent init command
2023-02-25 07:47:55 -05:00
Dimitri Balios
842ddbdc9d
Update Makefile 2023-02-23 10:35:33 +02:00
Mike Stefanello
4f776b76b3
Merge pull request #26 from joshlemer/docker-compose-bind-localhost
Bind DB and Redis to 127.0.0.1
2023-02-05 13:05:48 -05:00
Josh Lemer
96799be6ec Bind DB and Redis to 127.0.0.1 2023-01-25 14:49:13 -08:00
Mike Stefanello
859925bc45
Merge pull request #25 from migeorge/update-test-workflow
Update Test Workflow
2023-01-18 09:01:42 -05:00
Mike George
911793ec61 move to v3 actions 2023-01-17 22:35:14 -05:00
Mike Stefanello
19315b894e
Merge pull request #24 from mips171/mips171/makefile
Makefile fix to run worker
2023-01-05 20:32:31 -05:00
mips171
5b73526436
Update Makefile 2023-01-06 10:53:34 +10:00
Nicholas Smith
baa65ebbfa feedback 2023-01-05 08:01:50 +10:00
Nicholas Smith
4527d3d2e5 regen entities on reset 2023-01-04 22:03:15 +10:00
Nicholas Smith
2ac7604b43 add make down 2023-01-04 22:02:53 +10:00
Nicholas Smith
713d3818bb fix worker target 2023-01-04 22:02:05 +10:00
mikestefanello
26e4d4d16a Fixed docker make targets. 2022-11-04 18:49:25 -04:00
mikestefanello
0f800ec3e1 Bump Go version. 2022-11-02 19:37:07 -04:00
mikestefanello
c2f1df1a83 Bump Go version. 2022-11-02 19:34:40 -04:00
mikestefanello
8c5582f720 Updated dependencies. 2022-11-02 19:27:42 -04:00
mikestefanello
72ce41c828 Reorganized directories and packages. 2022-11-02 19:23:26 -04:00
mikestefanello
1018d82d13 Switch to viper for config management. 2022-11-02 14:50:19 -04:00
Mike Stefanello
434d7b44b0
Merge pull request #14 from ahashim/dockerizing-db-and-cache
Dockerizing db and cache
2022-10-10 19:53:55 -04:00
Mike Stefanello
d85159c4f0
Merge pull request #15 from hbd/patch-1
fix: typo when logging fatal error
2022-10-10 13:41:46 -04:00
Zak
152e2e1037
fix: typo when logging fatal error
Small fix to format the error instead of printing `v`
2022-10-10 08:49:11 -07:00
Ahmed Hashim
44ab335cf5
docs: only removing the psql and redis-cli dependencies from README.md + trailing whitespace characters 2022-10-09 08:57:43 -04:00
Ahmed Hashim
936e6c9b18
docs: removing the psql and redis-cli dependencies from README.md because they are now provided through their docker containers 2022-10-09 08:13:34 -04:00
Ahmed Hashim
55d51c70b8
feat: connect to redis-cli from within docker container instead of users localhost 2022-10-09 08:08:32 -04:00
Ahmed Hashim
9f6fe5f0b7
feat: connect to psql from within docker container instead of users localhost 2022-10-09 08:07:12 -04:00
Ahmed Hashim
7f1e057ea2
feat: updgrading docker compose command to support the latest version 2022-10-09 08:06:14 -04:00
mikestefanello
12a177a215 Updated echo. 2022-10-01 21:32:14 -04:00
mikestefanello
c52544caf0 Updated dependencies. 2022-08-28 10:18:47 -04:00
mikestefanello
fb55c3453b Fix incorrect params when building controller redirect route url. 2022-07-08 09:58:42 -04:00
Mike Stefanello
0cc95d4c64
Merge pull request #11 from arrkiin/dont_cache_auth
Prevent caching pages if user is authenticated
2022-06-21 09:49:11 -04:00
Jens
9f1ed2e5f5 Prevent caching pages if user is authenticated 2022-06-20 21:47:07 +02:00
mikestefanello
8d2a6d9b7a Updated yaml.v3. 2022-05-29 09:15:29 -04:00
mikestefanello
aab3c04473 Updated dependencies. 2022-05-29 09:08:57 -04:00
mikestefanello
8fa6ebb8cf Fixed potential test failures due to timing. 2022-05-17 08:57:43 -04:00
mikestefanello
acebcf0ba0 Let error handler handle all error logic, logging, and canceling. 2022-05-17 08:45:18 -04:00
mikestefanello
cd1887124e Fixed redirect status code. 2022-04-12 21:08:00 -04:00
mikestefanello
0ca3c2b701 Fix auth token expiration test failures. 2022-04-12 21:05:19 -04:00
mikestefanello
5c51b72757 Allow HTMX to handle redirects if the request is boosted. 2022-04-12 20:56:00 -04:00
mikestefanello
8b1bc9b99e Upgraded libraries. 2022-03-08 19:35:55 -05:00
mikestefanello
089c4d7ee1 README update. 2022-03-08 19:30:37 -05:00
mikestefanello
51e44a57a1 Lint check adjustments. 2022-02-10 08:56:07 -05:00
mikestefanello
944a4d941a Added make command to check for dependency updates. 2022-02-10 08:21:07 -05:00
mikestefanello
c6a50b3bbf Fixed missing port in database connection string. 2022-02-08 20:21:05 -05:00
mikestefanello
83cdbc4395 Added task worker service and example task processor. 2022-02-08 08:58:22 -05:00
mikestefanello
726556e973 Added make target to connect to the test cache. 2022-02-07 21:23:09 -05:00
mikestefanello
156e578dd0 Use a separate cache db when running tests. 2022-02-06 10:07:25 -05:00
mikestefanello
c43f62a570 Added asynq and a task client to the container to faciliate task queues. 2022-02-02 21:24:52 -05:00
mikestefanello
a82ed9c6d0 Updated echo and ent. 2022-01-28 08:45:16 -05:00
mikestefanello
6546418052 Include password token entity ID in reset URL in order to prevent loading all tokens. 2022-01-27 08:44:12 -05:00
mikestefanello
7a1a01d43e Support CSRF on route test POST requests. 2022-01-27 07:54:05 -05:00
mikestefanello
107f2e3262 Fixed assert package import. 2022-01-20 18:02:14 -05:00
mikestefanello
cd4cc1693c Simplified template renderer parsing and execution. 2022-01-19 09:14:18 -05:00
mikestefanello
cb43e08183 Expanded mail client for easier email operations. 2022-01-14 15:42:32 -05:00
mikestefanello
b269e7d264 Remove need for slice when setting cache tags. Require cache key for get/set ops. 2022-01-14 13:07:19 -05:00
mikestefanello
e0a65ca007 Added custom cache client for much easier cache operations. 2022-01-13 21:13:41 -05:00
mikestefanello
09c6df7f52 Use entity update method rather than query. Fixed password reset form action. 2022-01-09 21:30:09 -05:00
mikestefanello
10c0a23c0a Handle context cancellations and avoid logged errors. 2022-01-09 00:23:26 -05:00
mikestefanello
a6e99058f4 Type message typo. 2022-01-08 15:35:02 -05:00
mikestefanello
c31f30ba5c Added user email verification support. 2022-01-08 15:32:18 -05:00
mikestefanello
6ec7118c3d Replaced hard-coded search path with URL generator. 2022-01-06 21:40:04 -05:00
mikestefanello
eda79b6982 Added encryption info to README. 2022-01-04 20:10:10 -05:00
mikestefanello
c391be9e4d Added route errors to README. 2022-01-01 13:44:09 -05:00
mikestefanello
2ece37eb9c Changed module name. 2022-01-01 10:44:18 -05:00
mikestefanello
328c1a3367 Updated README. 2022-01-01 10:40:10 -05:00
mikestefanello
182ddc9f52 Updated README. Set project name. 2021-12-31 17:26:42 -05:00
mikestefanello
17185bee70 Merge branch 'main' of github.com:mikestefanello/goweb into main 2021-12-31 10:09:57 -05:00
Mike Stefanello
a6e131119a
Create test.yml 2021-12-31 10:02:56 -05:00
208 changed files with 11621 additions and 8207 deletions

52
.air.toml Normal file
View file

@ -0,0 +1,52 @@
root = "."
testdata_dir = "testdata"
tmp_dir = "tmp"
[build]
args_bin = []
bin = "./tmp/main"
cmd = "make build"
delay = 1000
exclude_dir = ["assets", "tmp", "vendor", "testdata", "uploads", "dbs", "public"]
exclude_file = []
exclude_regex = ["_test.go"]
exclude_unchanged = false
follow_symlink = false
full_bin = ""
include_dir = []
include_ext = ["go", "tpl", "tmpl", "html", "css"]
include_file = []
kill_delay = "0s"
log = "build-errors.log"
poll = false
poll_interval = 0
post_cmd = []
pre_cmd = []
rerun = false
rerun_delay = 500
send_interrupt = false
stop_on_error = true
[color]
app = ""
build = "yellow"
main = "magenta"
runner = "green"
watcher = "cyan"
[log]
main_only = false
silent = false
time = false
[misc]
clean_on_exit = false
[proxy]
app_port = 0
enabled = false
proxy_port = 0
[screen]
clear_on_rebuild = false
keep_scroll = true

31
.github/workflows/test.yml vendored Normal file
View file

@ -0,0 +1,31 @@
name: Test
on:
push:
branches: [ main ]
pull_request:
branches: [ main ]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Set up Go
uses: actions/setup-go@v3
with:
go-version: 1.23
- uses: actions/cache@v3
with:
path: |
~/.cache/go-build
~/go/pkg/mod
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
restore-keys: |
${{ runner.os }}-go-
- name: Test
run: make test

5
.gitignore vendored
View file

@ -1 +1,6 @@
.idea .idea
dbs
uploads
tmp
tailwindcss
daisyui*

104
Makefile
View file

@ -1,52 +1,82 @@
# Connect to the primary database # Get the OS name in lowercase (linux, darwin)
.PHONY: db OS_SYSNAME := $(shell uname -s | tr A-Z a-z)
db: # Get the machine architecture (x86_64, arm64)
psql postgresql://admin:admin@localhost:5432/app OS_MACHINE := $(shell uname -m)
# Connect to the test database # If mac OS, use `macos-arm64` or `macos-x64`
.PHONY: db-test ifeq ($(OS_SYSNAME),darwin)
db-test: OS_SYSNAME = macos
psql postgresql://admin:admin@localhost:5432/app_test ifneq ($(OS_MACHINE),arm64)
OS_MACHINE = x64
endif
endif
# Connect to the cache # If Linux, use `linux-x64`
.PHONY: cache ifeq ($(OS_SYSNAME),linux)
cache: OS_MACHINE = x64
redis-cli endif
# The appropriate Tailwind package for your OS will attempt to be automatically determined.
# If this is not working, hard-code the package you want using these options:
# https://github.com/tailwindlabs/tailwindcss/releases/latest
TAILWIND_PACKAGE = tailwindcss-$(OS_SYSNAME)-$(OS_MACHINE)
.PHONY: help
help: ## Print make targets
@grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}'
.PHONY: install
install: ent-install air-install tailwind-install ## Install all dependencies
.PHONY: tailwind-install
tailwind-install: ## Install the Tailwind CSS CLI
curl -sLo tailwindcss https://github.com/tailwindlabs/tailwindcss/releases/latest/download/$(TAILWIND_PACKAGE)
chmod +x tailwindcss
curl -sLO https://github.com/saadeghi/daisyui/releases/latest/download/daisyui.js
curl -sLO https://github.com/saadeghi/daisyui/releases/latest/download/daisyui-theme.js
# Install Ent code-generation module
.PHONY: ent-install .PHONY: ent-install
ent-install: ent-install: ## Install Ent code-generation module
go get -d entgo.io/ent/cmd/ent go get entgo.io/ent/cmd/ent
.PHONY: air-install
air-install: ## Install air
go install github.com/air-verse/air@latest
# Generate Ent code
.PHONY: ent-gen .PHONY: ent-gen
ent-gen: ent-gen: ## Generate Ent code
go generate ./ent go generate ./ent
# Create a new Ent entity
.PHONY: ent-new .PHONY: ent-new
ent-new: ent-new: ## Create a new Ent entity (ie, make ent-new name=MyEntity)
go run entgo.io/ent/cmd/ent init $(name) go run entgo.io/ent/cmd/ent new $(name)
# Start the Docker containers .PHONY: admin
.PHONY: up admin: ## Create a new admin user (ie, make admin email=myemail@web.com)
up: go run cmd/admin/main.go --email=$(email)
docker-compose up -d
sleep 3
# Rebuild Docker containers to wipe all data
.PHONY: reset
reset:
docker-compose down
make up
# Run the application
.PHONY: run .PHONY: run
run: run: ## Run the application
clear clear
go run main.go go run cmd/web/main.go
.PHONY: watch
watch: ## Run the application and watch for changes with air to automatically rebuild
clear
air
# Run all tests
.PHONY: test .PHONY: test
test: test: ## Run all tests
go test -p 1 ./... go test ./...
.PHONY: check-updates
check-updates: ## Check for direct dependency updates
go list -u -m -f '{{if not .Indirect}}{{.}}{{end}}' all | grep "\["
.PHONY: css
css: ## Build and minify Tailwind CSS
./tailwindcss -i tailwind.css -o public/static/main.css -m
.PHONY: build
build: css ## Build CSS and compile the application binary
go build -o ./tmp/main ./cmd/web

1395
README.md

File diff suppressed because it is too large Load diff

63
cmd/admin/main.go Normal file
View file

@ -0,0 +1,63 @@
package main
import (
"context"
"flag"
"fmt"
"os"
"github.com/mikestefanello/pagoda/pkg/log"
"github.com/mikestefanello/pagoda/pkg/services"
)
// main creates a new admin user with the email passed in via the flag.
func main() {
// Start a new container.
c := services.NewContainer()
defer func() {
// Gracefully shutdown all services.
if err := c.Shutdown(); err != nil {
log.Default().Error("shutdown failed", "error", err)
}
}()
var email string
flag.StringVar(&email, "email", "", "email address for the admin user")
flag.Parse()
if len(email) == 0 {
invalid("email is required")
}
// Generate a password.
pw, err := c.Auth.RandomToken(10)
if err != nil {
invalid("failed to generate a random password")
}
// Create the admin user.
err = c.ORM.User.
Create().
SetEmail(email).
SetName("Admin").
SetAdmin(true).
SetVerified(true).
SetPassword(pw).
Exec(context.Background())
if err != nil {
invalid(err.Error())
}
fmt.Println("")
fmt.Println("-- ADMIN USER CREATED --")
fmt.Printf("Email: %s\n", email)
fmt.Printf("Password: %s\n", pw)
fmt.Println("----")
fmt.Println("")
}
func invalid(msg string) {
fmt.Printf("[ERROR] %s\n", msg)
os.Exit(1)
}

74
cmd/web/main.go Normal file
View file

@ -0,0 +1,74 @@
package main
import (
"context"
"crypto/tls"
"errors"
"fmt"
"net/http"
"os"
"os/signal"
"github.com/mikestefanello/pagoda/pkg/handlers"
"github.com/mikestefanello/pagoda/pkg/log"
"github.com/mikestefanello/pagoda/pkg/services"
"github.com/mikestefanello/pagoda/pkg/tasks"
)
func main() {
// Start a new container.
c := services.NewContainer()
defer func() {
// Gracefully shutdown all services.
fatal("shutdown failed", c.Shutdown())
}()
// Build the router.
if err := handlers.BuildRouter(c); err != nil {
fatal("failed to build the router", err)
}
// Register all task queues.
tasks.Register(c)
// Start the task runner to execute queued tasks.
c.Tasks.Start(context.Background())
// Start the server.
go func() {
srv := http.Server{
Addr: fmt.Sprintf("%s:%d", c.Config.HTTP.Hostname, c.Config.HTTP.Port),
Handler: c.Web,
ReadTimeout: c.Config.HTTP.ReadTimeout,
WriteTimeout: c.Config.HTTP.WriteTimeout,
IdleTimeout: c.Config.HTTP.IdleTimeout,
}
if c.Config.HTTP.TLS.Enabled {
certs, err := tls.LoadX509KeyPair(c.Config.HTTP.TLS.Certificate, c.Config.HTTP.TLS.Key)
fatal("cannot load TLS certificate", err)
srv.TLSConfig = &tls.Config{
Certificates: []tls.Certificate{certs},
}
}
if err := c.Web.StartServer(&srv); errors.Is(err, http.ErrServerClosed) {
fatal("shutting down the server", err)
}
}()
// Wait for interrupt signal to gracefully shut down the web server and task runner.
quit := make(chan os.Signal, 1)
signal.Notify(quit, os.Interrupt)
signal.Notify(quit, os.Kill)
<-quit
}
// fatal logs an error and terminates the application, if the error is not nil.
func fatal(msg string, err error) {
if err != nil {
log.Default().Error(msg, "error", err)
os.Exit(1)
}
}

View file

@ -2,115 +2,146 @@ package config
import ( import (
"os" "os"
"strings"
"time" "time"
"github.com/joeshaw/envdecode" "github.com/spf13/viper"
) )
const ( type environment string
// TemplateDir stores the name of the directory that contains templates
TemplateDir = "templates"
// TemplateExt stores the extension used for the template files
TemplateExt = ".gohtml"
// StaticDir stores the name of the directory that will serve static files
StaticDir = "static"
// StaticPrefix stores the URL prefix used when serving static files
StaticPrefix = "files"
)
type Environment string
const ( const (
EnvLocal Environment = "local" // EnvLocal represents the local environment.
EnvTest Environment = "test" EnvLocal environment = "local"
EnvDevelop Environment = "dev"
EnvStaging Environment = "staging" // EnvTest represents the test environment.
EnvQA Environment = "qa" EnvTest environment = "test"
EnvProduction Environment = "prod"
// EnvDevelopment represents the development environment.
EnvDevelopment environment = "dev"
// EnvStaging represents the staging environment.
EnvStaging environment = "staging"
// EnvQA represents the qa environment.
EnvQA environment = "qa"
// EnvProduction represents the production environment.
EnvProduction environment = "prod"
) )
// SwitchEnvironment sets the environment variable used to dictate which environment the application is // SwitchEnvironment sets the environment variable used to dictate which environment the application is
// currently running in. // currently running in.
// This must be called prior to loading the configuration in order for it to take effect. // This must be called prior to loading the configuration in order for it to take effect.
func SwitchEnvironment(env Environment) { func SwitchEnvironment(env environment) {
if err := os.Setenv("APP_ENVIRONMENT", string(env)); err != nil { if err := os.Setenv("PAGODA_APP_ENVIRONMENT", string(env)); err != nil {
panic(err) panic(err)
} }
} }
type ( type (
// Config stores complete configuration // Config stores complete configuration.
Config struct { Config struct {
HTTP HTTPConfig HTTP HTTPConfig
App AppConfig App AppConfig
Cache CacheConfig Cache CacheConfig
Database DatabaseConfig Database DatabaseConfig
Files FilesConfig
Tasks TasksConfig
Mail MailConfig Mail MailConfig
} }
// HTTPConfig stores HTTP configuration // HTTPConfig stores HTTP configuration.
HTTPConfig struct { HTTPConfig struct {
Hostname string `env:"HTTP_HOSTNAME"` Hostname string
Port uint16 `env:"HTTP_PORT,default=8000"` Port uint16
ReadTimeout time.Duration `env:"HTTP_READ_TIMEOUT,default=5s"` ReadTimeout time.Duration
WriteTimeout time.Duration `env:"HTTP_WRITE_TIMEOUT,default=10s"` WriteTimeout time.Duration
IdleTimeout time.Duration `env:"HTTP_IDLE_TIMEOUT,default=2m"` IdleTimeout time.Duration
TLS struct { ShutdownTimeout time.Duration
Enabled bool `env:"HTTP_TLS_ENABLED,default=false"` TLS struct {
Certificate string `env:"HTTP_TLS_CERTIFICATE"` Enabled bool
Key string `env:"HTTP_TLS_KEY"` Certificate string
Key string
} }
} }
// AppConfig stores application configuration // AppConfig stores application configuration.
AppConfig struct { AppConfig struct {
Name string `env:"APP_NAME,default=Goweb"` Name string
Environment Environment `env:"APP_ENVIRONMENT,default=local"` Host string
EncryptionKey string `env:"APP_ENCRYPTION_KEY,default=?E(G+KbPeShVmYq3t6w9z$C&F)J@McQf"` Environment environment
Timeout time.Duration `env:"APP_TIMEOUT,default=20s"` EncryptionKey string
Timeout time.Duration
PasswordToken struct { PasswordToken struct {
Expiration time.Duration `env:"APP_PASSWORD_TOKEN_EXPIRATION,default=60m"` Expiration time.Duration
Length int `env:"APP_PASSWORD_TOKEN_LENGTH,default=64"` Length int
} }
EmailVerificationTokenExpiration time.Duration
} }
// CacheConfig stores the cache configuration // CacheConfig stores the cache configuration.
CacheConfig struct { CacheConfig struct {
Hostname string `env:"CACHE_HOSTNAME,default=localhost"` Capacity int
Port uint16 `env:"CACHE_PORT,default=6379"`
Password string `env:"CACHE_PASSWORD"`
Expiration struct { Expiration struct {
StaticFile time.Duration `env:"CACHE_EXPIRATION_STATIC_FILE,default=4380h"` PublicFile time.Duration
Page time.Duration `env:"CACHE_EXPIRATION_PAGE,default=24h"`
} }
} }
// DatabaseConfig stores the database configuration // DatabaseConfig stores the database configuration.
DatabaseConfig struct { DatabaseConfig struct {
Hostname string `env:"DB_HOSTNAME,default=localhost"` Driver string
Port uint16 `env:"DB_PORT,default=5432"` Connection string
User string `env:"DB_USER,default=admin"` TestConnection string
Password string `env:"DB_PASSWORD,default=admin"`
Database string `env:"DB_NAME,default=app"`
TestDatabase string `env:"DB_NAME_TEST,default=app_test"`
} }
// MailConfig stores the mail configuration // FilesConfig stores the file system configuration.
FilesConfig struct {
Directory string
}
// TasksConfig stores the tasks configuration.
TasksConfig struct {
Goroutines int
ReleaseAfter time.Duration
CleanupInterval time.Duration
ShutdownTimeout time.Duration
}
// MailConfig stores the mail configuration.
MailConfig struct { MailConfig struct {
Hostname string `env:"MAIL_HOSTNAME,default=localhost"` Hostname string
Port uint16 `env:"MAIL_PORT,default=25"` Port uint16
User string `env:"MAIL_USER,default=admin"` User string
Password string `env:"MAIL_PASSWORD,default=admin"` Password string
FromAddress string `env:"MAIL_FROM_ADDRESS,default=admin@localhost"` FromAddress string
} }
) )
// GetConfig loads and returns configuration // GetConfig loads and returns configuration.
func GetConfig() (Config, error) { func GetConfig() (Config, error) {
var cfg Config var c Config
err := envdecode.StrictDecode(&cfg)
return cfg, err // Load the config file.
viper.SetConfigName("config")
viper.SetConfigType("yaml")
viper.AddConfigPath(".")
viper.AddConfigPath("config")
viper.AddConfigPath("../config")
viper.AddConfigPath("../../config")
// Load env variables.
viper.SetEnvPrefix("pagoda")
viper.AutomaticEnv()
viper.SetEnvKeyReplacer(strings.NewReplacer(".", "_"))
if err := viper.ReadInConfig(); err != nil {
return c, err
}
if err := viper.Unmarshal(&c); err != nil {
return c, err
}
return c, nil
} }

54
config/config.yaml Normal file
View file

@ -0,0 +1,54 @@
http:
hostname: ""
port: 8000
readTimeout: "5s"
writeTimeout: "10s"
idleTimeout: "2m"
shutdownTimeout: "10s"
tls:
enabled: false
certificate: ""
key: ""
app:
name: "Pagoda"
# We manually set this rather than using the HTTP settings in order to build absolute URLs for users
# since it's likely your app's HTTP settings are not identical to what is exposed by your server.
host: "http://localhost:8000"
environment: "local"
# Change this on any live environments.
encryptionKey: "?E(G+KbPeShVmYq3t6w9z$C&F)J@McQf"
timeout: "20s"
passwordToken:
expiration: "60m"
length: 64
emailVerificationTokenExpiration: "12h"
cache:
capacity: 100000
expiration:
publicFile: "4380h"
database:
driver: "sqlite3"
connection: "dbs/main.db?_journal=WAL&_timeout=5000&_fk=true"
# $RAND will be automatically replaced with a random value.
# memdb is more robust for an in-memory database rather than :memory: because the latter has the potential
# retain data even after you close and re-open the connection.
testConnection: "file:/$RAND?vfs=memdb&_timeout=1000&_fk=true"
files:
directory: "uploads"
tasks:
goroutines: 1
releaseAfter: "15m"
cleanupInterval: "1h"
shutdownTimeout: "10s"
mail:
hostname: "localhost"
port: 25
user: "admin"
password: "admin"
fromAddress: "admin@localhost"

View file

@ -11,7 +11,7 @@ func TestGetConfig(t *testing.T) {
_, err := GetConfig() _, err := GetConfig()
require.NoError(t, err) require.NoError(t, err)
var env Environment var env environment
env = "abc" env = "abc"
SwitchEnvironment(env) SwitchEnvironment(env)
cfg, err := GetConfig() cfg, err := GetConfig()

View file

@ -1,15 +0,0 @@
package context
const (
// AuthenticatedUserKey is the key value used to store the authenticated user in context
AuthenticatedUserKey = "auth_user"
// UserKey is the key value used to store a user in context
UserKey = "user"
// FormKey is the key value used to store a form in context
FormKey = "form"
// PasswordTokenKey is the key value used to store a password token in context
PasswordTokenKey = "password_token"
)

View file

@ -1,163 +0,0 @@
package controller
import (
"bytes"
"fmt"
"net/http"
"goweb/middleware"
"goweb/services"
"github.com/eko/gocache/v2/marshaler"
"github.com/eko/gocache/v2/store"
"github.com/labstack/echo/v4"
)
// Controller provides base functionality and dependencies to routes.
// The proposed pattern is to embed a Controller in each individual route struct and to use
// the router to inject the container so your routes have access to the services within the container
type Controller struct {
// Container stores a services container which contains dependencies
Container *services.Container
}
// NewController creates a new Controller
func NewController(c *services.Container) Controller {
return Controller{
Container: c,
}
}
// RenderPage renders a Page as an HTTP response
func (c *Controller) RenderPage(ctx echo.Context, page Page) error {
var buf *bytes.Buffer
var err error
// Page name is required
if page.Name == "" {
ctx.Logger().Error("page render failed due to missing name")
return echo.NewHTTPError(http.StatusInternalServerError)
}
// Use the app name in configuration if a value was not set
if page.AppName == "" {
page.AppName = c.Container.Config.App.Name
}
// Check if this is an HTMX non-boosted request which indicates that only partial
// content should be rendered
if page.HTMX.Request.Enabled && !page.HTMX.Request.Boosted {
// Parse and execute the templates only for the content portion of the page
// The templates used for this partial request will be:
// 1. The base htmx template which omits the layout and only includes the content template
// 2. The content template specified in Page.Name
// 3. All templates within the components directory
// Also included is the function map provided by the funcmap package
buf, err = c.Container.TemplateRenderer.ParseAndExecute(
"page:htmx",
page.Name,
"htmx",
[]string{
"htmx",
fmt.Sprintf("pages/%s", page.Name),
},
[]string{"components"},
page,
)
} else {
// Parse and execute the templates for the Page
// As mentioned in the documentation for the Page struct, the templates used for the page will be:
// 1. The layout/base template specified in Page.Layout
// 2. The content template specified in Page.Name
// 3. All templates within the components directory
// Also included is the function map provided by the funcmap package
buf, err = c.Container.TemplateRenderer.ParseAndExecute(
"page",
page.Name,
page.Layout,
[]string{
fmt.Sprintf("layouts/%s", page.Layout),
fmt.Sprintf("pages/%s", page.Name),
},
[]string{"components"},
page,
)
}
if err != nil {
ctx.Logger().Errorf("failed to parse and execute templates: %v", err)
return echo.NewHTTPError(http.StatusInternalServerError)
}
// Set the status code
ctx.Response().Status = page.StatusCode
// Set any headers
for k, v := range page.Headers {
ctx.Response().Header().Set(k, v)
}
// Apply the HTMX response, if one
if page.HTMX.Response != nil {
page.HTMX.Response.Apply(ctx)
}
// Cache this page, if caching was enabled
c.cachePage(ctx, page, buf)
return ctx.HTMLBlob(ctx.Response().Status, buf.Bytes())
}
// cachePage caches the HTML for a given Page if the Page has caching enabled
func (c *Controller) cachePage(ctx echo.Context, page Page, html *bytes.Buffer) {
if !page.Cache.Enabled {
return
}
// If no expiration time was provided, default to the configuration value
if page.Cache.Expiration == 0 {
page.Cache.Expiration = c.Container.Config.Cache.Expiration.Page
}
// Extract the headers
headers := make(map[string]string)
for k, v := range ctx.Response().Header() {
headers[k] = v[0]
}
// The request URL is used as the cache key so the middleware can serve the
// cached page on matching requests
key := ctx.Request().URL.String()
opts := &store.Options{
Expiration: page.Cache.Expiration,
Tags: page.Cache.Tags,
}
cp := middleware.CachedPage{
URL: key,
HTML: html.Bytes(),
Headers: headers,
StatusCode: ctx.Response().Status,
}
err := marshaler.New(c.Container.Cache).Set(ctx.Request().Context(), key, cp, opts)
if err != nil {
ctx.Logger().Errorf("failed to cache page: %v", err)
return
}
ctx.Logger().Infof("cached page")
}
// Redirect redirects to a given route name with optional route parameters
func (c *Controller) Redirect(ctx echo.Context, route string, routeParams ...interface{}) error {
url := ctx.Echo().Reverse(route, routeParams)
return ctx.Redirect(http.StatusFound, url)
}
// Fail is a helper to fail a request by returning a 500 error and logging the error
func (c *Controller) Fail(ctx echo.Context, err error, log string) error {
ctx.Logger().Errorf("%s: %v", log, err)
return echo.NewHTTPError(http.StatusInternalServerError)
}

View file

@ -1,177 +0,0 @@
package controller
import (
"context"
"io/ioutil"
"net/http"
"net/http/httptest"
"os"
"testing"
"goweb/config"
"goweb/htmx"
"goweb/middleware"
"goweb/services"
"goweb/tests"
"github.com/eko/gocache/v2/store"
"github.com/eko/gocache/v2/marshaler"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/labstack/echo/v4"
)
var (
c *services.Container
)
func TestMain(m *testing.M) {
// Set the environment to test
config.SwitchEnvironment(config.EnvTest)
// Create a new container
c = services.NewContainer()
defer func() {
if err := c.Shutdown(); err != nil {
c.Web.Logger.Fatal(err)
}
}()
// Run tests
exitVal := m.Run()
os.Exit(exitVal)
}
func TestController_Redirect(t *testing.T) {
ctx, _ := tests.NewContext(c.Web, "/abc")
ctr := NewController(c)
err := ctr.Redirect(ctx, "home")
require.NoError(t, err)
assert.Equal(t, "", ctx.Response().Header().Get(echo.HeaderLocation))
assert.Equal(t, http.StatusFound, ctx.Response().Status)
}
func TestController_RenderPage(t *testing.T) {
setup := func() (echo.Context, *httptest.ResponseRecorder, Controller, Page) {
ctx, rec := tests.NewContext(c.Web, "/test/TestController_RenderPage")
tests.InitSession(ctx)
ctr := NewController(c)
p := NewPage(ctx)
p.Name = "home"
p.Layout = "main"
p.Cache.Enabled = false
p.Headers["A"] = "b"
p.Headers["C"] = "d"
p.StatusCode = http.StatusCreated
return ctx, rec, ctr, p
}
t.Run("missing name", func(t *testing.T) {
// Rendering should fail if the Page has no name
ctx, _, ctr, p := setup()
p.Name = ""
err := ctr.RenderPage(ctx, p)
assert.Error(t, err)
})
t.Run("no page cache", func(t *testing.T) {
ctx, _, ctr, p := setup()
err := ctr.RenderPage(ctx, p)
require.NoError(t, err)
// Check status code and headers
assert.Equal(t, http.StatusCreated, ctx.Response().Status)
for k, v := range p.Headers {
assert.Equal(t, v, ctx.Response().Header().Get(k))
}
// Check the template cache
parsed, err := c.TemplateRenderer.Load("page", p.Name)
assert.NoError(t, err)
// Check that all expected templates were parsed.
// This includes the name, layout and all components
expectedTemplates := make(map[string]bool)
expectedTemplates[p.Name+config.TemplateExt] = true
expectedTemplates[p.Layout+config.TemplateExt] = true
components, err := ioutil.ReadDir(c.TemplateRenderer.GetTemplatesPath() + "/components")
require.NoError(t, err)
for _, f := range components {
expectedTemplates[f.Name()] = true
}
for _, v := range parsed.Templates() {
delete(expectedTemplates, v.Name())
}
assert.Empty(t, expectedTemplates)
})
t.Run("htmx rendering", func(t *testing.T) {
ctx, _, ctr, p := setup()
p.HTMX.Request.Enabled = true
p.HTMX.Response = &htmx.Response{
Trigger: "trigger",
}
err := ctr.RenderPage(ctx, p)
require.NoError(t, err)
// Check HTMX header
assert.Equal(t, "trigger", ctx.Response().Header().Get(htmx.HeaderTrigger))
// Check the template cache
parsed, err := c.TemplateRenderer.Load("page:htmx", p.Name)
assert.NoError(t, err)
// Check that all expected templates were parsed.
// This includes the name, htmx and all components
expectedTemplates := make(map[string]bool)
expectedTemplates[p.Name+config.TemplateExt] = true
expectedTemplates["htmx"+config.TemplateExt] = true
components, err := ioutil.ReadDir(c.TemplateRenderer.GetTemplatesPath() + "/components")
require.NoError(t, err)
for _, f := range components {
expectedTemplates[f.Name()] = true
}
for _, v := range parsed.Templates() {
delete(expectedTemplates, v.Name())
}
assert.Empty(t, expectedTemplates)
})
t.Run("page cache", func(t *testing.T) {
ctx, rec, ctr, p := setup()
p.Cache.Enabled = true
p.Cache.Tags = []string{"tag1"}
err := ctr.RenderPage(ctx, p)
require.NoError(t, err)
// Fetch from the cache
res, err := marshaler.New(c.Cache).
Get(context.Background(), p.URL, new(middleware.CachedPage))
require.NoError(t, err)
// Compare the cached page
cp, ok := res.(*middleware.CachedPage)
require.True(t, ok)
assert.Equal(t, p.URL, cp.URL)
assert.Equal(t, p.Headers, cp.Headers)
assert.Equal(t, p.StatusCode, cp.StatusCode)
assert.Equal(t, rec.Body.Bytes(), cp.HTML)
// Clear the tag
err = c.Cache.Invalidate(context.Background(), store.InvalidateOptions{
Tags: []string{p.Cache.Tags[0]},
})
require.NoError(t, err)
// Refetch from the cache and expect no results
_, err = marshaler.New(c.Cache).
Get(context.Background(), p.URL, new(middleware.CachedPage))
assert.Error(t, err)
})
}

View file

@ -1,104 +0,0 @@
package controller
import (
"github.com/go-playground/validator/v10"
"github.com/labstack/echo/v4"
)
// FormSubmission represents the state of the submission of a form, not including the form itself
type FormSubmission struct {
// IsSubmitted indicates if the form has been submitted
IsSubmitted bool
// Errors stores a slice of error message strings keyed by form struct field name
Errors map[string][]string
}
// Process processes a submission for a form
func (f *FormSubmission) Process(ctx echo.Context, form interface{}) error {
f.Errors = make(map[string][]string)
f.IsSubmitted = true
// Validate the form
if err := ctx.Validate(form); err != nil {
f.setErrorMessages(err)
}
return nil
}
// HasErrors indicates if the submission has any validation errors
func (f FormSubmission) HasErrors() bool {
if f.Errors == nil {
return false
}
return len(f.Errors) > 0
}
// FieldHasErrors indicates if a given field on the form has any validation errors
func (f FormSubmission) FieldHasErrors(fieldName string) bool {
return len(f.GetFieldErrors(fieldName)) > 0
}
// SetFieldError sets an error message for a given field name
func (f *FormSubmission) SetFieldError(fieldName string, message string) {
if f.Errors == nil {
f.Errors = make(map[string][]string)
}
f.Errors[fieldName] = append(f.Errors[fieldName], message)
}
// GetFieldErrors gets the errors for a given field name
func (f FormSubmission) GetFieldErrors(fieldName string) []string {
if f.Errors == nil {
return []string{}
}
return f.Errors[fieldName]
}
// GetFieldStatusClass returns an HTML class based on the status of the field
func (f FormSubmission) GetFieldStatusClass(fieldName string) string {
if f.IsSubmitted {
if f.FieldHasErrors(fieldName) {
return "is-danger"
}
return "is-success"
}
return ""
}
// IsDone indicates if the submission is considered done which is when it has been submitted
// and there are no errors.
func (f FormSubmission) IsDone() bool {
return f.IsSubmitted && !f.HasErrors()
}
// setErrorMessages sets errors messages on the submission for all fields that failed validation
func (f *FormSubmission) setErrorMessages(err error) {
// Only this is supported right now
ves, ok := err.(validator.ValidationErrors)
if !ok {
return
}
for _, ve := range ves {
var message string
// Provide better error messages depending on the failed validation tag
// This should be expanded as you use additional tags in your validation
switch ve.Tag() {
case "required":
message = "This field is required."
case "email":
message = "Enter a valid email address."
case "eqfield":
message = "Does not match."
default:
message = "Invalid value."
}
// Add the error
f.SetFieldError(ve.Field(), message)
}
}

View file

@ -1,36 +0,0 @@
package controller
import (
"testing"
"goweb/tests"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestFormSubmission(t *testing.T) {
type formTest struct {
Name string `validate:"required"`
Email string `validate:"required,email"`
Submission FormSubmission
}
ctx, _ := tests.NewContext(c.Web, "/")
form := formTest{
Name: "",
Email: "a@a.com",
}
err := form.Submission.Process(ctx, form)
assert.NoError(t, err)
assert.True(t, form.Submission.HasErrors())
assert.True(t, form.Submission.FieldHasErrors("Name"))
assert.False(t, form.Submission.FieldHasErrors("Email"))
require.Len(t, form.Submission.GetFieldErrors("Name"), 1)
assert.Len(t, form.Submission.GetFieldErrors("Email"), 0)
assert.Equal(t, "This field is required.", form.Submission.GetFieldErrors("Name")[0])
assert.Equal(t, "is-danger", form.Submission.GetFieldStatusClass("Name"))
assert.Equal(t, "is-success", form.Submission.GetFieldStatusClass("Email"))
assert.False(t, form.Submission.IsDone())
}

View file

@ -1,161 +0,0 @@
package controller
import (
"html/template"
"net/http"
"time"
"goweb/context"
"goweb/ent"
"goweb/htmx"
"goweb/msg"
echomw "github.com/labstack/echo/v4/middleware"
"github.com/labstack/echo/v4"
)
// Page consists of all data that will be used to render a page response for a given controller.
// While it's not required for a controller to render a Page on a route, this is the common data
// object that will be passed to the templates, making it easy for all controllers to share
// functionality both on the back and frontend. The Page can be expanded to include anything else
// your app wants to support.
// Methods on this page also then become available in the templates, which can be more useful than
// the funcmap if your methods require data stored in the page, such as the context.
type Page struct {
// AppName stores the name of the application.
// If omitted, the configuration value will be used.
AppName string
// Title stores the title of the page
Title string
// Context stores the request context
Context echo.Context
// ToURL is a function to convert a route name and optional route parameters to a URL
ToURL func(name string, params ...interface{}) string
// Path stores the path of the current request
Path string
// URL stores the URL of the current request
URL string
// Data stores whatever additional data that needs to be passed to the templates.
// This is what the controller uses to pass the content of the page.
Data interface{}
// Form stores a struct that represents a form on the page.
// This should be a struct with fields for each form field, using both "form" and "validate" tags
// It should also contain a Submission field of type FormSubmission if you wish to have validation
// messagesa and markup presented to the user
Form interface{}
// Layout stores the name of the layout base template file which will be used when the page is rendered.
// This should match a template file located within the layouts directory inside the templates directory.
// The template extension should not be included in this value.
Layout string
// Name stores the name of the page as well as the name of the template file which will be used to render
// the content portion of the layout template.
// This should match a template file located within the pages directory inside the templates directory.
// The template extension should not be included in this value.
Name string
// IsHome stores whether the requested page is the home page or not
IsHome bool
// IsAuth stores whether or not the user is authenticated
IsAuth bool
// AuthUser stores the authenticated user
AuthUser *ent.User
// StatusCode stores the HTTP status code that will be returned
StatusCode int
// Metatags stores metatag values
Metatags struct {
// Description stores the description metatag value
Description string
// Keywords stores the keywords metatag values
Keywords []string
}
// Pager stores a pager which can be used to page lists of results
Pager Pager
// CSRF stores the CSRF token for the given request.
// This will only be populated if the CSRF middleware is in effect for the given request.
// If this is populated, all forms must include this value otherwise the requests will be rejected.
CSRF string
// Headers stores a list of HTTP headers and values to be set on the response
Headers map[string]string
// RequestID stores the ID of the given request.
// This will only be populated if the request ID middleware is in effect for the given request.
RequestID string
HTMX struct {
Request htmx.Request
Response *htmx.Response
}
// Cache stores values for caching the response of this page
Cache struct {
// Enabled dictates if the response of this page should be cached.
// Cached responses are served via middleware.
Enabled bool
// Expiration stores the amount of time that the cache entry should live for before expiring.
// If omitted, the configuration value will be used.
Expiration time.Duration
// Tags stores a list of tags to apply to the cache entry.
// These are useful when invalidating cache for dynamic events such as entity operations.
Tags []string
}
}
// NewPage creates and initiatizes a new Page for a given request context
func NewPage(ctx echo.Context) Page {
p := Page{
Context: ctx,
ToURL: ctx.Echo().Reverse,
Path: ctx.Request().URL.Path,
URL: ctx.Request().URL.String(),
StatusCode: http.StatusOK,
Pager: NewPager(ctx, DefaultItemsPerPage),
Headers: make(map[string]string),
RequestID: ctx.Response().Header().Get(echo.HeaderXRequestID),
}
p.IsHome = p.Path == "/"
if csrf := ctx.Get(echomw.DefaultCSRFConfig.ContextKey); csrf != nil {
p.CSRF = csrf.(string)
}
if u := ctx.Get(context.AuthenticatedUserKey); u != nil {
p.IsAuth = true
p.AuthUser = u.(*ent.User)
}
p.HTMX.Request = htmx.GetRequest(ctx)
return p
}
// GetMessages gets all flash messages for a given type.
// This allows for easy access to flash messages from the templates.
func (p Page) GetMessages(typ msg.Type) []template.HTML {
strs := msg.Get(p.Context, typ)
ret := make([]template.HTML, len(strs))
for k, v := range strs {
ret[k] = template.HTML(v)
}
return ret
}

View file

@ -1,75 +0,0 @@
package controller
import (
"net/http"
"testing"
"goweb/context"
"goweb/msg"
"goweb/tests"
echomw "github.com/labstack/echo/v4/middleware"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestNewPage(t *testing.T) {
ctx, _ := tests.NewContext(c.Web, "/")
p := NewPage(ctx)
assert.Same(t, ctx, p.Context)
assert.NotNil(t, p.ToURL)
assert.Equal(t, "/", p.Path)
assert.Equal(t, "/", p.URL)
assert.Equal(t, http.StatusOK, p.StatusCode)
assert.Equal(t, NewPager(ctx, DefaultItemsPerPage), p.Pager)
assert.Empty(t, p.Headers)
assert.True(t, p.IsHome)
assert.False(t, p.IsAuth)
assert.Empty(t, p.CSRF)
assert.Empty(t, p.RequestID)
assert.False(t, p.Cache.Enabled)
ctx, _ = tests.NewContext(c.Web, "/abc?def=123")
usr, err := tests.CreateUser(c.ORM)
require.NoError(t, err)
ctx.Set(context.AuthenticatedUserKey, usr)
ctx.Set(echomw.DefaultCSRFConfig.ContextKey, "csrf")
p = NewPage(ctx)
assert.Equal(t, "/abc", p.Path)
assert.Equal(t, "/abc?def=123", p.URL)
assert.False(t, p.IsHome)
assert.True(t, p.IsAuth)
assert.Equal(t, usr, p.AuthUser)
assert.Equal(t, "csrf", p.CSRF)
}
func TestPage_GetMessages(t *testing.T) {
ctx, _ := tests.NewContext(c.Web, "/")
tests.InitSession(ctx)
p := NewPage(ctx)
// Set messages
msgTests := make(map[msg.Type][]string)
msgTests[msg.TypeWarning] = []string{
"abc",
"def",
}
msgTests[msg.TypeInfo] = []string{
"123",
"456",
}
for typ, values := range msgTests {
for _, value := range values {
msg.Set(ctx, typ, value)
}
}
// Get the messages
for typ, values := range msgTests {
msgs := p.GetMessages(typ)
for i, message := range msgs {
assert.Equal(t, values[i], string(message))
}
}
}

View file

@ -1,15 +0,0 @@
version: "3"
services:
cache:
image: "redis:alpine"
ports:
- "6379:6379"
db:
image: postgres:alpine
ports:
- "5432:5432"
environment:
- POSTGRES_USER=admin
- POSTGRES_PASSWORD=admin
- POSTGRES_DB=app

97
ent/admin/extension.go Normal file
View file

@ -0,0 +1,97 @@
package admin
import (
"embed"
"strings"
"text/template"
"unicode"
"entgo.io/ent/entc"
"entgo.io/ent/entc/gen"
"entgo.io/ent/schema/field"
)
var (
//go:embed templates
templateDir embed.FS
)
// Extension is the Ent extension that generates code to support the entity admin panel.
type Extension struct {
entc.DefaultExtension
}
func (*Extension) Templates() []*gen.Template {
return []*gen.Template{
gen.MustParse(
gen.NewTemplate("admin").
Funcs(template.FuncMap{
"fieldName": fieldName,
"fieldLabel": FieldLabel,
"fieldIsPointer": fieldIsPointer,
}).
ParseFS(templateDir, "templates/*tmpl"),
),
}
}
// fieldName provides a struct field name from an entity field name (ie, user_id -> UserID).
func fieldName(name string) string {
if len(name) == 0 {
return name
}
parts := strings.Split(name, "_")
for i := 0; i < len(parts); i++ {
if parts[i] == "id" {
parts[i] = "ID"
} else {
parts[i] = upperFirst(parts[i])
}
}
return strings.Join(parts, "")
}
// FieldLabel provides a label for an entity field name (ie, user_id -> User ID).
func FieldLabel(name string) string {
if len(name) == 0 {
return name
}
parts := strings.Split(name, "_")
for i := 0; i < len(parts); i++ {
if parts[i] == "id" {
parts[i] = "ID"
}
if i == 0 {
parts[i] = upperFirst(parts[i])
}
}
return strings.Join(parts, " ")
}
// fieldIsPointer determines if a given entity field should be a pointer on the struct.
func fieldIsPointer(f *gen.Field) bool {
switch {
case f.Type.Type == field.TypeBool:
return false
case f.Optional,
f.Default,
f.Sensitive(),
f.Nillable:
return true
}
return false
}
// upperFirst uppercases the first character of a given string.
func upperFirst(s string) string {
if len(s) == 0 {
return s
}
out := []rune(s)
out[0] = unicode.ToUpper(out[0])
return string(out)
}

319
ent/admin/handler.go Normal file
View file

@ -0,0 +1,319 @@
// Code generated by ent, DO NOT EDIT.
package admin
import (
"fmt"
"net/url"
"strconv"
"time"
"entgo.io/ent/dialect/sql"
"github.com/labstack/echo/v4"
"github.com/mikestefanello/pagoda/ent"
"github.com/mikestefanello/pagoda/ent/passwordtoken"
"github.com/mikestefanello/pagoda/ent/user"
)
const dateTimeFormat = "2006-01-02T15:04:05"
const dateTimeFormatNoSeconds = "2006-01-02T15:04"
type Handler struct {
client *ent.Client
Config HandlerConfig
}
func NewHandler(client *ent.Client, cfg HandlerConfig) *Handler {
return &Handler{
client: client,
Config: cfg,
}
}
func (h *Handler) Create(ctx echo.Context, entityType EntityType) error {
switch entityType.(type) {
case *PasswordToken:
return h.PasswordTokenCreate(ctx)
case *User:
return h.UserCreate(ctx)
default:
return fmt.Errorf("unsupported entity type: %s", entityType)
}
}
func (h *Handler) Get(ctx echo.Context, entityType EntityType, id int) (url.Values, error) {
switch entityType.(type) {
case *PasswordToken:
return h.PasswordTokenGet(ctx, id)
case *User:
return h.UserGet(ctx, id)
default:
return nil, fmt.Errorf("unsupported entity type: %s", entityType)
}
}
func (h *Handler) Delete(ctx echo.Context, entityType EntityType, id int) error {
switch entityType.(type) {
case *PasswordToken:
return h.PasswordTokenDelete(ctx, id)
case *User:
return h.UserDelete(ctx, id)
default:
return fmt.Errorf("unsupported entity type: %s", entityType)
}
}
func (h *Handler) Update(ctx echo.Context, entityType EntityType, id int) error {
switch entityType.(type) {
case *PasswordToken:
return h.PasswordTokenUpdate(ctx, id)
case *User:
return h.UserUpdate(ctx, id)
default:
return fmt.Errorf("unsupported entity type: %s", entityType)
}
}
func (h *Handler) List(ctx echo.Context, entityType EntityType) (*EntityList, error) {
switch entityType.(type) {
case *PasswordToken:
return h.PasswordTokenList(ctx)
case *User:
return h.UserList(ctx)
default:
return nil, fmt.Errorf("unsupported entity type: %s", entityType)
}
}
func (h *Handler) PasswordTokenCreate(ctx echo.Context) error {
var payload PasswordToken
if err := h.bind(ctx, &payload); err != nil {
return err
}
op := h.client.PasswordToken.Create()
if payload.Token != nil {
op.SetToken(*payload.Token)
}
op.SetUserID(payload.UserID)
if payload.CreatedAt != nil {
op.SetCreatedAt(*payload.CreatedAt)
}
_, err := op.Save(ctx.Request().Context())
return err
}
func (h *Handler) PasswordTokenUpdate(ctx echo.Context, id int) error {
entity, err := h.client.PasswordToken.Get(ctx.Request().Context(), id)
if err != nil {
return err
}
var payload PasswordToken
if err = h.bind(ctx, &payload); err != nil {
return err
}
op := entity.Update()
if payload.Token != nil {
op.SetToken(*payload.Token)
}
op.SetUserID(payload.UserID)
if payload.CreatedAt == nil {
var empty time.Time
op.SetCreatedAt(empty)
} else {
op.SetCreatedAt(*payload.CreatedAt)
}
_, err = op.Save(ctx.Request().Context())
return err
}
func (h *Handler) PasswordTokenDelete(ctx echo.Context, id int) error {
return h.client.PasswordToken.DeleteOneID(id).
Exec(ctx.Request().Context())
}
func (h *Handler) PasswordTokenList(ctx echo.Context) (*EntityList, error) {
page, offset := h.getPageAndOffset(ctx)
res, err := h.client.PasswordToken.
Query().
Limit(h.Config.ItemsPerPage + 1).
Offset(offset).
Order(passwordtoken.ByID(sql.OrderDesc())).
All(ctx.Request().Context())
if err != nil {
return nil, err
}
list := &EntityList{
Columns: []string{
"User ID",
"Created at",
},
Entities: make([]EntityValues, 0, len(res)),
Page: page,
HasNextPage: len(res) > h.Config.ItemsPerPage,
}
for i := 0; i <= len(res)-1; i++ {
list.Entities = append(list.Entities, EntityValues{
ID: res[i].ID,
Values: []string{
fmt.Sprint(res[i].UserID),
res[i].CreatedAt.Format(h.Config.TimeFormat),
},
})
}
return list, err
}
func (h *Handler) PasswordTokenGet(ctx echo.Context, id int) (url.Values, error) {
entity, err := h.client.PasswordToken.Get(ctx.Request().Context(), id)
if err != nil {
return nil, err
}
v := url.Values{}
v.Set("user_id", fmt.Sprint(entity.UserID))
v.Set("created_at", entity.CreatedAt.Format(dateTimeFormat))
return v, err
}
func (h *Handler) UserCreate(ctx echo.Context) error {
var payload User
if err := h.bind(ctx, &payload); err != nil {
return err
}
op := h.client.User.Create()
op.SetName(payload.Name)
op.SetEmail(payload.Email)
if payload.Password != nil {
op.SetPassword(*payload.Password)
}
op.SetVerified(payload.Verified)
op.SetAdmin(payload.Admin)
if payload.CreatedAt != nil {
op.SetCreatedAt(*payload.CreatedAt)
}
_, err := op.Save(ctx.Request().Context())
return err
}
func (h *Handler) UserUpdate(ctx echo.Context, id int) error {
entity, err := h.client.User.Get(ctx.Request().Context(), id)
if err != nil {
return err
}
var payload User
if err = h.bind(ctx, &payload); err != nil {
return err
}
op := entity.Update()
op.SetName(payload.Name)
op.SetEmail(payload.Email)
if payload.Password != nil {
op.SetPassword(*payload.Password)
}
op.SetVerified(payload.Verified)
op.SetAdmin(payload.Admin)
_, err = op.Save(ctx.Request().Context())
return err
}
func (h *Handler) UserDelete(ctx echo.Context, id int) error {
return h.client.User.DeleteOneID(id).
Exec(ctx.Request().Context())
}
func (h *Handler) UserList(ctx echo.Context) (*EntityList, error) {
page, offset := h.getPageAndOffset(ctx)
res, err := h.client.User.
Query().
Limit(h.Config.ItemsPerPage + 1).
Offset(offset).
Order(user.ByID(sql.OrderDesc())).
All(ctx.Request().Context())
if err != nil {
return nil, err
}
list := &EntityList{
Columns: []string{
"Name",
"Email",
"Verified",
"Admin",
"Created at",
},
Entities: make([]EntityValues, 0, len(res)),
Page: page,
HasNextPage: len(res) > h.Config.ItemsPerPage,
}
for i := 0; i <= len(res)-1; i++ {
list.Entities = append(list.Entities, EntityValues{
ID: res[i].ID,
Values: []string{
res[i].Name,
res[i].Email,
fmt.Sprint(res[i].Verified),
fmt.Sprint(res[i].Admin),
res[i].CreatedAt.Format(h.Config.TimeFormat),
},
})
}
return list, err
}
func (h *Handler) UserGet(ctx echo.Context, id int) (url.Values, error) {
entity, err := h.client.User.Get(ctx.Request().Context(), id)
if err != nil {
return nil, err
}
v := url.Values{}
v.Set("name", entity.Name)
v.Set("email", entity.Email)
v.Set("verified", fmt.Sprint(entity.Verified))
v.Set("admin", fmt.Sprint(entity.Admin))
return v, err
}
func (h *Handler) getPageAndOffset(ctx echo.Context) (int, int) {
if page, err := strconv.Atoi(ctx.QueryParam(h.Config.PageQueryKey)); err == nil {
if page > 1 {
return page, (page - 1) * h.Config.ItemsPerPage
}
}
return 1, 0
}
func (h *Handler) bind(ctx echo.Context, entity any) error {
// Echo requires some pre-processing of form values to avoid problems.
for k, v := range ctx.Request().Form {
// Remove empty field values so Echo's bind does not fail when trying to parse things like
// times, etc.
if len(v) == 1 && len(v[0]) == 0 {
delete(ctx.Request().Form, k)
continue
}
// Echo expects datetime values to be in a certain format but that does not align with the datetime-local
// HTML form element format, so we will attempt to convert it here.
for _, format := range []string{dateTimeFormatNoSeconds, dateTimeFormat} {
if t, err := time.Parse(format, v[0]); err == nil {
ctx.Request().Form[k][0] = t.Format(time.RFC3339)
break
}
}
}
return ctx.Bind(entity)
}

101
ent/admin/schema.go Normal file
View file

@ -0,0 +1,101 @@
// Code generated by ent, DO NOT EDIT.
package admin
import (
"entgo.io/ent/schema/field"
)
type Enum struct {
Label, Value string
}
type FieldSchema struct {
Name string
Type field.Type
Optional bool
Immutable bool
Sensitive bool
Enums []string
}
const NamePasswordToken = "PasswordToken"
var fieldsPasswordToken = []*FieldSchema{
{
Name: "token",
Type: field.TypeString,
Optional: false,
Immutable: false,
Sensitive: true,
Enums: nil,
},
{
Name: "user_id",
Type: field.TypeInt,
Optional: false,
Immutable: false,
Sensitive: false,
Enums: nil,
},
{
Name: "created_at",
Type: field.TypeTime,
Optional: false,
Immutable: false,
Sensitive: false,
Enums: nil,
},
}
const NameUser = "User"
var fieldsUser = []*FieldSchema{
{
Name: "name",
Type: field.TypeString,
Optional: false,
Immutable: false,
Sensitive: false,
Enums: nil,
},
{
Name: "email",
Type: field.TypeString,
Optional: false,
Immutable: false,
Sensitive: false,
Enums: nil,
},
{
Name: "password",
Type: field.TypeString,
Optional: false,
Immutable: false,
Sensitive: true,
Enums: nil,
},
{
Name: "verified",
Type: field.TypeBool,
Optional: false,
Immutable: false,
Sensitive: false,
Enums: nil,
},
{
Name: "admin",
Type: field.TypeBool,
Optional: false,
Immutable: false,
Sensitive: false,
Enums: nil,
},
{
Name: "created_at",
Type: field.TypeTime,
Optional: false,
Immutable: true,
Sensitive: false,
Enums: nil,
},
}

View file

@ -0,0 +1,262 @@
{{/* Tell Intellij/GoLand to enable the autocompletion based on the *gen.Graph type. */}}
{{/* gotype: entgo.io/ent/entc/gen.Graph */}}
{{ define "admin/handler" }}
// Code generated by ent, DO NOT EDIT.
{{- $pkg := base $.Config.Package }}
package admin
import (
"fmt"
"net/url"
"strconv"
"entgo.io/ent/dialect/sql"
"github.com/labstack/echo/v4"
"{{ $.Config.Package }}"
{{- range $n := $.Nodes }}
"{{ $.Config.Package }}/{{ $n.Package }}"
{{- end }}
)
const dateTimeFormat = "2006-01-02T15:04:05"
const dateTimeFormatNoSeconds = "2006-01-02T15:04"
type Handler struct {
client *{{ $pkg }}.Client
Config HandlerConfig
}
func NewHandler(client *{{ $pkg }}.Client, cfg HandlerConfig) *Handler {
return &Handler{
client: client,
Config: cfg,
}
}
func (h *Handler) Create(ctx echo.Context, entityType EntityType) error {
switch entityType.(type) {
{{- range $n := $.Nodes }}
case *{{ $n.Name }}:
return h.{{ $n.Name }}Create(ctx)
{{- end }}
default:
return fmt.Errorf("unsupported entity type: %s", entityType)
}
}
func (h *Handler) Get(ctx echo.Context, entityType EntityType, id int) (url.Values, error) {
switch entityType.(type) {
{{- range $n := $.Nodes }}
case *{{ $n.Name }}:
return h.{{ $n.Name }}Get(ctx, id)
{{- end }}
default:
return nil, fmt.Errorf("unsupported entity type: %s", entityType)
}
}
func (h *Handler) Delete(ctx echo.Context, entityType EntityType, id int) error {
switch entityType.(type) {
{{- range $n := $.Nodes }}
case *{{ $n.Name }}:
return h.{{ $n.Name }}Delete(ctx, id)
{{- end }}
default:
return fmt.Errorf("unsupported entity type: %s", entityType)
}
}
func (h *Handler) Update(ctx echo.Context, entityType EntityType, id int) error {
switch entityType.(type) {
{{- range $n := $.Nodes }}
case *{{ $n.Name }}:
return h.{{ $n.Name }}Update(ctx, id)
{{- end }}
default:
return fmt.Errorf("unsupported entity type: %s", entityType)
}
}
func (h *Handler) List(ctx echo.Context, entityType EntityType) (*EntityList, error) {
switch entityType.(type) {
{{- range $n := $.Nodes }}
case *{{ $n.Name }}:
return h.{{ $n.Name }}List(ctx)
{{- end }}
default:
return nil, fmt.Errorf("unsupported entity type: %s", entityType)
}
}
{{ range $n := $.Nodes }}
func (h *Handler) {{ $n.Name }}Create(ctx echo.Context) error {
var payload {{ $n.Name }}
if err := h.bind(ctx, &payload); err != nil {
return err
}
op := h.client.{{ $n.Name }}.Create()
{{- range $f := $n.Fields }}
{{- if (fieldIsPointer $f) }}
if payload.{{ fieldName $f.Name }} != nil {
op.Set{{ fieldName $f.Name }}(*payload.{{ fieldName $f.Name }})
}
{{- else }}
op.Set{{ fieldName $f.Name }}(payload.{{ fieldName $f.Name }})
{{- end }}
{{- end }}
_, err := op.Save(ctx.Request().Context())
return err
}
func (h *Handler) {{ $n.Name }}Update(ctx echo.Context, id int) error {
entity, err := h.client.{{ $n.Name }}.Get(ctx.Request().Context(), id)
if err != nil {
return err
}
var payload {{ $n.Name }}
if err = h.bind(ctx, &payload); err != nil {
return err
}
op := entity.Update()
{{- range $f := $n.Fields }}
{{- if not $f.Immutable }}
{{- if $f.Sensitive }}
if payload.{{ fieldName $f.Name }} != nil {
op.Set{{ fieldName $f.Name }}(*payload.{{ fieldName $f.Name }})
}
{{- else if $f.Nillable }}
op.SetNillable{{ fieldName $f.Name }}(payload.{{ fieldName $f.Name }})
{{- else if $f.Optional }}
if payload.{{ fieldName $f.Name }} == nil {
op.Clear{{ fieldName $f.Name }}()
} else {
op.Set{{ fieldName $f.Name }}(*payload.{{ fieldName $f.Name }})
}
{{- else if (fieldIsPointer $f) }}
if payload.{{ fieldName $f.Name }} == nil {
var empty {{ $f.Type }}
op.Set{{ fieldName $f.Name }}(empty)
} else {
op.Set{{ fieldName $f.Name }}(*payload.{{ fieldName $f.Name }})
}
{{- else }}
op.Set{{ fieldName $f.Name }}(payload.{{ fieldName $f.Name }})
{{- end }}
{{- end }}
{{- end }}
_, err = op.Save(ctx.Request().Context())
return err
}
func (h *Handler) {{ $n.Name }}Delete(ctx echo.Context, id int) error {
return h.client.{{ $n.Name }}.DeleteOneID(id).
Exec(ctx.Request().Context())
}
func (h *Handler) {{ $n.Name }}List(ctx echo.Context) (*EntityList, error) {
page, offset := h.getPageAndOffset(ctx)
res, err := h.client.{{ $n.Name }}.
Query().
Limit(h.Config.ItemsPerPage+1).
Offset(offset).
Order({{ $n.Package }}.ByID(sql.OrderDesc())).
All(ctx.Request().Context())
if err != nil {
return nil, err
}
list := &EntityList{
Columns: []string{
{{- range $f := $n.Fields }}
{{- if not $f.Sensitive }}
"{{ fieldLabel $f.Name }}",
{{- end }}
{{- end }}
},
Entities: make([]EntityValues, 0, len(res)),
Page: page,
HasNextPage: len(res) > h.Config.ItemsPerPage,
}
for i := 0; i <= len(res)-1; i++ {
list.Entities = append(list.Entities, EntityValues{
ID: res[i].ID,
Values: []string{
{{- range $f := $n.Fields }}
{{- if not $f.Sensitive }}
{{- if eq $f.Type.String "string" }}
res[i].{{ fieldName $f.Name }},
{{- else if eq $f.Type.String "time.Time" }}
res[i].{{ fieldName $f.Name }}.Format(h.Config.TimeFormat),
{{- else }}
fmt.Sprint(res[i].{{ fieldName $f.Name }}),
{{- end }}
{{- end }}
{{- end }}
},
})
}
return list, err
}
func (h *Handler) {{ $n.Name }}Get(ctx echo.Context, id int) (url.Values, error) {
entity, err := h.client.{{ $n.Name }}.Get(ctx.Request().Context(), id)
if err != nil {
return nil, err
}
v := url.Values{}
{{- range $f := $n.Fields }}
{{- if and (not $f.Sensitive) (not $f.Immutable) }}
{{- if eq $f.Type.String "string" }}
v.Set("{{ $f.Name }}", entity.{{ fieldName $f.Name }})
{{- else if eq $f.Type.String "time.Time" }}
v.Set("{{ $f.Name }}", entity.{{ fieldName $f.Name }}.Format(dateTimeFormat))
{{- else }}
v.Set("{{ $f.Name }}", fmt.Sprint(entity.{{ fieldName $f.Name }}))
{{- end }}
{{- end }}
{{- end }}
return v, err
}
{{ end }}
func (h *Handler) getPageAndOffset(ctx echo.Context) (int, int) {
if page, err := strconv.Atoi(ctx.QueryParam(h.Config.PageQueryKey)); err == nil {
if page > 1 {
return page, (page-1) * h.Config.ItemsPerPage
}
}
return 1, 0
}
func (h *Handler) bind(ctx echo.Context, entity any) error {
// Echo requires some pre-processing of form values to avoid problems.
for k, v := range ctx.Request().Form {
// Remove empty field values so Echo's bind does not fail when trying to parse things like
// times, etc.
if len(v) == 1 && len(v[0]) == 0 {
delete(ctx.Request().Form, k)
continue
}
// Echo expects datetime values to be in a certain format but that does not align with the datetime-local
// HTML form element format, so we will attempt to convert it here.
for _, format := range []string{dateTimeFormatNoSeconds, dateTimeFormat} {
if t, err := time.Parse(format, v[0]); err == nil {
ctx.Request().Form[k][0] = t.Format(time.RFC3339)
break
}
}
}
return ctx.Bind(entity)
}
{{ end }}

View file

@ -0,0 +1,51 @@
{{/* Tell Intellij/GoLand to enable the autocompletion based on the *gen.Graph type. */}}
{{/* gotype: entgo.io/ent/entc/gen.Graph */}}
{{ define "admin/schema" }}
// Code generated by ent, DO NOT EDIT.
package admin
import (
"entgo.io/ent/schema/field"
)
type Enum struct {
Label, Value string
}
type FieldSchema struct {
Name string
Type field.Type
Optional bool
Immutable bool
Sensitive bool
Enums []string
}
{{- range $n := $.Nodes }}
const Name{{ $n.Name }} = "{{ $n.Name }}"
var fields{{ $n.Name }} = []*FieldSchema{
{{- range $f := $n.Fields }}
{
Name: "{{ $f.Name }}",
Type: field.{{ $f.Type.Type.ConstName }},
Optional: {{ $f.Optional }},
Immutable: {{ $f.Immutable }},
Sensitive: {{ $f.Sensitive }},
{{- if len $f.Enums }}
Enums: []string{
{{- range $e := $f.Enums }}
"{{ $e.Value }}",
{{- end }}
},
{{- else }}
Enums: nil,
{{- end }}
},
{{- end }}
}
{{ end }}
{{ end }}

View file

@ -0,0 +1,56 @@
{{/* Tell Intellij/GoLand to enable the autocompletion based on the *gen.Graph type. */}}
{{/* gotype: entgo.io/ent/entc/gen.Graph */}}
{{ define "admin/types" }}
// Code generated by ent, DO NOT EDIT.
package admin
{{- range $n := $.Nodes }}
type {{ $n.Name }} struct {
{{- range $f := $n.Fields }}
{{ fieldName $f.Name }} {{ if (fieldIsPointer $f) }}*{{ end }}{{ $f.Type }} `form:"{{ $f.Name }}"`
{{- end }}
}
func (e *{{ $n.Name }}) GetName() string {
return Name{{ $n.Name }}
}
func (e *{{ $n.Name }}) GetSchema() []*FieldSchema {
return fields{{ $n.Name }}
}
{{ end }}
type EntityType interface {
GetName() string
GetSchema() []*FieldSchema
}
var entityTypes = []EntityType{
{{- range $n := $.Nodes }}
&{{ $n.Name }}{},
{{- end }}
}
type EntityList struct {
Columns []string
Entities []EntityValues
Page int
HasNextPage bool
}
type EntityValues struct {
ID int
Values []string
}
type HandlerConfig struct {
ItemsPerPage int
PageQueryKey string
TimeFormat string
}
func GetEntityTypes() []EntityType {
return entityTypes
}
{{ end }}

67
ent/admin/types.go Normal file
View file

@ -0,0 +1,67 @@
// Code generated by ent, DO NOT EDIT.
package admin
import "time"
type PasswordToken struct {
Token *string `form:"token"`
UserID int `form:"user_id"`
CreatedAt *time.Time `form:"created_at"`
}
func (e *PasswordToken) GetName() string {
return NamePasswordToken
}
func (e *PasswordToken) GetSchema() []*FieldSchema {
return fieldsPasswordToken
}
type User struct {
Name string `form:"name"`
Email string `form:"email"`
Password *string `form:"password"`
Verified bool `form:"verified"`
Admin bool `form:"admin"`
CreatedAt *time.Time `form:"created_at"`
}
func (e *User) GetName() string {
return NameUser
}
func (e *User) GetSchema() []*FieldSchema {
return fieldsUser
}
type EntityType interface {
GetName() string
GetSchema() []*FieldSchema
}
var entityTypes = []EntityType{
&PasswordToken{},
&User{},
}
type EntityList struct {
Columns []string
Entities []EntityValues
Page int
HasNextPage bool
}
type EntityValues struct {
ID int
Values []string
}
type HandlerConfig struct {
ItemsPerPage int
PageQueryKey string
TimeFormat string
}
func GetEntityTypes() []EntityType {
return entityTypes
}

View file

@ -1,20 +1,22 @@
// Code generated by entc, DO NOT EDIT. // Code generated by ent, DO NOT EDIT.
package ent package ent
import ( import (
"context" "context"
"errors"
"fmt" "fmt"
"log" "log"
"reflect"
"goweb/ent/migrate" "github.com/mikestefanello/pagoda/ent/migrate"
"goweb/ent/passwordtoken"
"goweb/ent/user"
"entgo.io/ent"
"entgo.io/ent/dialect" "entgo.io/ent/dialect"
"entgo.io/ent/dialect/sql" "entgo.io/ent/dialect/sql"
"entgo.io/ent/dialect/sql/sqlgraph" "entgo.io/ent/dialect/sql/sqlgraph"
"github.com/mikestefanello/pagoda/ent/passwordtoken"
"github.com/mikestefanello/pagoda/ent/user"
) )
// Client is the client that holds all ent builders. // Client is the client that holds all ent builders.
@ -30,9 +32,7 @@ type Client struct {
// NewClient creates a new client configured with the given options. // NewClient creates a new client configured with the given options.
func NewClient(opts ...Option) *Client { func NewClient(opts ...Option) *Client {
cfg := config{log: log.Println, hooks: &hooks{}} client := &Client{config: newConfig(opts...)}
cfg.options(opts...)
client := &Client{config: cfg}
client.init() client.init()
return client return client
} }
@ -43,6 +43,62 @@ func (c *Client) init() {
c.User = NewUserClient(c.config) c.User = NewUserClient(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 // Open opens a database/sql.DB specified by the driver name and
// the data source name, and returns a new client attached to it. // the data source name, and returns a new client attached to it.
// Optional parameters can be added for configuring the client. // Optional parameters can be added for configuring the client.
@ -59,11 +115,14 @@ func Open(driverName, dataSourceName string, options ...Option) (*Client, error)
} }
} }
// 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 // Tx returns a new transactional client. The provided context
// is used until the transaction is committed or rolled back. // is used until the transaction is committed or rolled back.
func (c *Client) Tx(ctx context.Context) (*Tx, error) { func (c *Client) Tx(ctx context.Context) (*Tx, error) {
if _, ok := c.driver.(*txDriver); ok { if _, ok := c.driver.(*txDriver); ok {
return nil, fmt.Errorf("ent: cannot start a transaction within a transaction") return nil, ErrTxStarted
} }
tx, err := newTx(ctx, c.driver) tx, err := newTx(ctx, c.driver)
if err != nil { if err != nil {
@ -82,7 +141,7 @@ func (c *Client) Tx(ctx context.Context) (*Tx, error) {
// BeginTx returns a transactional client with specified options. // BeginTx returns a transactional client with specified options.
func (c *Client) BeginTx(ctx context.Context, opts *sql.TxOptions) (*Tx, error) { func (c *Client) BeginTx(ctx context.Context, opts *sql.TxOptions) (*Tx, error) {
if _, ok := c.driver.(*txDriver); ok { if _, ok := c.driver.(*txDriver); ok {
return nil, fmt.Errorf("ent: cannot start a transaction within a transaction") return nil, errors.New("ent: cannot start a transaction within a transaction")
} }
tx, err := c.driver.(interface { tx, err := c.driver.(interface {
BeginTx(context.Context, *sql.TxOptions) (dialect.Tx, error) BeginTx(context.Context, *sql.TxOptions) (dialect.Tx, error)
@ -93,6 +152,7 @@ func (c *Client) BeginTx(ctx context.Context, opts *sql.TxOptions) (*Tx, error)
cfg := c.config cfg := c.config
cfg.driver = &txDriver{tx: tx, drv: c.driver} cfg.driver = &txDriver{tx: tx, drv: c.driver}
return &Tx{ return &Tx{
ctx: ctx,
config: cfg, config: cfg,
PasswordToken: NewPasswordTokenClient(cfg), PasswordToken: NewPasswordTokenClient(cfg),
User: NewUserClient(cfg), User: NewUserClient(cfg),
@ -105,7 +165,6 @@ func (c *Client) BeginTx(ctx context.Context, opts *sql.TxOptions) (*Tx, error)
// PasswordToken. // PasswordToken.
// Query(). // Query().
// Count(ctx) // Count(ctx)
//
func (c *Client) Debug() *Client { func (c *Client) Debug() *Client {
if c.debug { if c.debug {
return c return c
@ -129,6 +188,25 @@ func (c *Client) Use(hooks ...Hook) {
c.User.Use(hooks...) c.User.Use(hooks...)
} }
// Intercept adds the query interceptors to all the entity clients.
// In order to add interceptors to a specific client, call: `client.Node.Intercept(...)`.
func (c *Client) Intercept(interceptors ...Interceptor) {
c.PasswordToken.Intercept(interceptors...)
c.User.Intercept(interceptors...)
}
// Mutate implements the ent.Mutator interface.
func (c *Client) Mutate(ctx context.Context, m Mutation) (Value, error) {
switch m := m.(type) {
case *PasswordTokenMutation:
return c.PasswordToken.mutate(ctx, m)
case *UserMutation:
return c.User.mutate(ctx, m)
default:
return nil, fmt.Errorf("ent: unknown mutation type %T", m)
}
}
// PasswordTokenClient is a client for the PasswordToken schema. // PasswordTokenClient is a client for the PasswordToken schema.
type PasswordTokenClient struct { type PasswordTokenClient struct {
config config
@ -145,7 +223,13 @@ func (c *PasswordTokenClient) Use(hooks ...Hook) {
c.hooks.PasswordToken = append(c.hooks.PasswordToken, hooks...) c.hooks.PasswordToken = append(c.hooks.PasswordToken, hooks...)
} }
// Create returns a create builder for PasswordToken. // Intercept adds a list of query interceptors to the interceptors stack.
// A call to `Intercept(f, g, h)` equals to `passwordtoken.Intercept(f(g(h())))`.
func (c *PasswordTokenClient) Intercept(interceptors ...Interceptor) {
c.inters.PasswordToken = append(c.inters.PasswordToken, interceptors...)
}
// Create returns a builder for creating a PasswordToken entity.
func (c *PasswordTokenClient) Create() *PasswordTokenCreate { func (c *PasswordTokenClient) Create() *PasswordTokenCreate {
mutation := newPasswordTokenMutation(c.config, OpCreate) mutation := newPasswordTokenMutation(c.config, OpCreate)
return &PasswordTokenCreate{config: c.config, hooks: c.Hooks(), mutation: mutation} return &PasswordTokenCreate{config: c.config, hooks: c.Hooks(), mutation: mutation}
@ -156,6 +240,21 @@ func (c *PasswordTokenClient) CreateBulk(builders ...*PasswordTokenCreate) *Pass
return &PasswordTokenCreateBulk{config: c.config, builders: builders} return &PasswordTokenCreateBulk{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 *PasswordTokenClient) MapCreateBulk(slice any, setFunc func(*PasswordTokenCreate, int)) *PasswordTokenCreateBulk {
rv := reflect.ValueOf(slice)
if rv.Kind() != reflect.Slice {
return &PasswordTokenCreateBulk{err: fmt.Errorf("calling to PasswordTokenClient.MapCreateBulk with wrong type %T, need slice", slice)}
}
builders := make([]*PasswordTokenCreate, rv.Len())
for i := 0; i < rv.Len(); i++ {
builders[i] = c.Create()
setFunc(builders[i], i)
}
return &PasswordTokenCreateBulk{config: c.config, builders: builders}
}
// Update returns an update builder for PasswordToken. // Update returns an update builder for PasswordToken.
func (c *PasswordTokenClient) Update() *PasswordTokenUpdate { func (c *PasswordTokenClient) Update() *PasswordTokenUpdate {
mutation := newPasswordTokenMutation(c.config, OpUpdate) mutation := newPasswordTokenMutation(c.config, OpUpdate)
@ -163,8 +262,8 @@ func (c *PasswordTokenClient) Update() *PasswordTokenUpdate {
} }
// UpdateOne returns an update builder for the given entity. // UpdateOne returns an update builder for the given entity.
func (c *PasswordTokenClient) UpdateOne(pt *PasswordToken) *PasswordTokenUpdateOne { func (c *PasswordTokenClient) UpdateOne(_m *PasswordToken) *PasswordTokenUpdateOne {
mutation := newPasswordTokenMutation(c.config, OpUpdateOne, withPasswordToken(pt)) mutation := newPasswordTokenMutation(c.config, OpUpdateOne, withPasswordToken(_m))
return &PasswordTokenUpdateOne{config: c.config, hooks: c.Hooks(), mutation: mutation} return &PasswordTokenUpdateOne{config: c.config, hooks: c.Hooks(), mutation: mutation}
} }
@ -180,12 +279,12 @@ func (c *PasswordTokenClient) Delete() *PasswordTokenDelete {
return &PasswordTokenDelete{config: c.config, hooks: c.Hooks(), mutation: mutation} return &PasswordTokenDelete{config: c.config, hooks: c.Hooks(), mutation: mutation}
} }
// DeleteOne returns a delete builder for the given entity. // DeleteOne returns a builder for deleting the given entity.
func (c *PasswordTokenClient) DeleteOne(pt *PasswordToken) *PasswordTokenDeleteOne { func (c *PasswordTokenClient) DeleteOne(_m *PasswordToken) *PasswordTokenDeleteOne {
return c.DeleteOneID(pt.ID) return c.DeleteOneID(_m.ID)
} }
// DeleteOneID returns a delete builder for the given id. // DeleteOneID returns a builder for deleting the given entity by its id.
func (c *PasswordTokenClient) DeleteOneID(id int) *PasswordTokenDeleteOne { func (c *PasswordTokenClient) DeleteOneID(id int) *PasswordTokenDeleteOne {
builder := c.Delete().Where(passwordtoken.ID(id)) builder := c.Delete().Where(passwordtoken.ID(id))
builder.mutation.id = &id builder.mutation.id = &id
@ -197,6 +296,8 @@ func (c *PasswordTokenClient) DeleteOneID(id int) *PasswordTokenDeleteOne {
func (c *PasswordTokenClient) Query() *PasswordTokenQuery { func (c *PasswordTokenClient) Query() *PasswordTokenQuery {
return &PasswordTokenQuery{ return &PasswordTokenQuery{
config: c.config, config: c.config,
ctx: &QueryContext{Type: TypePasswordToken},
inters: c.Interceptors(),
} }
} }
@ -215,16 +316,16 @@ func (c *PasswordTokenClient) GetX(ctx context.Context, id int) *PasswordToken {
} }
// QueryUser queries the user edge of a PasswordToken. // QueryUser queries the user edge of a PasswordToken.
func (c *PasswordTokenClient) QueryUser(pt *PasswordToken) *UserQuery { func (c *PasswordTokenClient) QueryUser(_m *PasswordToken) *UserQuery {
query := &UserQuery{config: c.config} query := (&UserClient{config: c.config}).Query()
query.path = func(ctx context.Context) (fromV *sql.Selector, _ error) { query.path = func(context.Context) (fromV *sql.Selector, _ error) {
id := pt.ID id := _m.ID
step := sqlgraph.NewStep( step := sqlgraph.NewStep(
sqlgraph.From(passwordtoken.Table, passwordtoken.FieldID, id), sqlgraph.From(passwordtoken.Table, passwordtoken.FieldID, id),
sqlgraph.To(user.Table, user.FieldID), sqlgraph.To(user.Table, user.FieldID),
sqlgraph.Edge(sqlgraph.M2O, false, passwordtoken.UserTable, passwordtoken.UserColumn), sqlgraph.Edge(sqlgraph.M2O, false, passwordtoken.UserTable, passwordtoken.UserColumn),
) )
fromV = sqlgraph.Neighbors(pt.driver.Dialect(), step) fromV = sqlgraph.Neighbors(_m.driver.Dialect(), step)
return fromV, nil return fromV, nil
} }
return query return query
@ -232,7 +333,28 @@ func (c *PasswordTokenClient) QueryUser(pt *PasswordToken) *UserQuery {
// Hooks returns the client hooks. // Hooks returns the client hooks.
func (c *PasswordTokenClient) Hooks() []Hook { func (c *PasswordTokenClient) Hooks() []Hook {
return c.hooks.PasswordToken hooks := c.hooks.PasswordToken
return append(hooks[:len(hooks):len(hooks)], passwordtoken.Hooks[:]...)
}
// Interceptors returns the client interceptors.
func (c *PasswordTokenClient) Interceptors() []Interceptor {
return c.inters.PasswordToken
}
func (c *PasswordTokenClient) mutate(ctx context.Context, m *PasswordTokenMutation) (Value, error) {
switch m.Op() {
case OpCreate:
return (&PasswordTokenCreate{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx)
case OpUpdate:
return (&PasswordTokenUpdate{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx)
case OpUpdateOne:
return (&PasswordTokenUpdateOne{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx)
case OpDelete, OpDeleteOne:
return (&PasswordTokenDelete{config: c.config, hooks: c.Hooks(), mutation: m}).Exec(ctx)
default:
return nil, fmt.Errorf("ent: unknown PasswordToken mutation op: %q", m.Op())
}
} }
// UserClient is a client for the User schema. // UserClient is a client for the User schema.
@ -251,7 +373,13 @@ func (c *UserClient) Use(hooks ...Hook) {
c.hooks.User = append(c.hooks.User, hooks...) c.hooks.User = append(c.hooks.User, hooks...)
} }
// Create returns a create builder for User. // Intercept adds a list of query interceptors to the interceptors stack.
// A call to `Intercept(f, g, h)` equals to `user.Intercept(f(g(h())))`.
func (c *UserClient) Intercept(interceptors ...Interceptor) {
c.inters.User = append(c.inters.User, interceptors...)
}
// Create returns a builder for creating a User entity.
func (c *UserClient) Create() *UserCreate { func (c *UserClient) Create() *UserCreate {
mutation := newUserMutation(c.config, OpCreate) mutation := newUserMutation(c.config, OpCreate)
return &UserCreate{config: c.config, hooks: c.Hooks(), mutation: mutation} return &UserCreate{config: c.config, hooks: c.Hooks(), mutation: mutation}
@ -262,6 +390,21 @@ func (c *UserClient) CreateBulk(builders ...*UserCreate) *UserCreateBulk {
return &UserCreateBulk{config: c.config, builders: builders} return &UserCreateBulk{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 *UserClient) MapCreateBulk(slice any, setFunc func(*UserCreate, int)) *UserCreateBulk {
rv := reflect.ValueOf(slice)
if rv.Kind() != reflect.Slice {
return &UserCreateBulk{err: fmt.Errorf("calling to UserClient.MapCreateBulk with wrong type %T, need slice", slice)}
}
builders := make([]*UserCreate, rv.Len())
for i := 0; i < rv.Len(); i++ {
builders[i] = c.Create()
setFunc(builders[i], i)
}
return &UserCreateBulk{config: c.config, builders: builders}
}
// Update returns an update builder for User. // Update returns an update builder for User.
func (c *UserClient) Update() *UserUpdate { func (c *UserClient) Update() *UserUpdate {
mutation := newUserMutation(c.config, OpUpdate) mutation := newUserMutation(c.config, OpUpdate)
@ -269,8 +412,8 @@ func (c *UserClient) Update() *UserUpdate {
} }
// UpdateOne returns an update builder for the given entity. // UpdateOne returns an update builder for the given entity.
func (c *UserClient) UpdateOne(u *User) *UserUpdateOne { func (c *UserClient) UpdateOne(_m *User) *UserUpdateOne {
mutation := newUserMutation(c.config, OpUpdateOne, withUser(u)) mutation := newUserMutation(c.config, OpUpdateOne, withUser(_m))
return &UserUpdateOne{config: c.config, hooks: c.Hooks(), mutation: mutation} return &UserUpdateOne{config: c.config, hooks: c.Hooks(), mutation: mutation}
} }
@ -286,12 +429,12 @@ func (c *UserClient) Delete() *UserDelete {
return &UserDelete{config: c.config, hooks: c.Hooks(), mutation: mutation} return &UserDelete{config: c.config, hooks: c.Hooks(), mutation: mutation}
} }
// DeleteOne returns a delete builder for the given entity. // DeleteOne returns a builder for deleting the given entity.
func (c *UserClient) DeleteOne(u *User) *UserDeleteOne { func (c *UserClient) DeleteOne(_m *User) *UserDeleteOne {
return c.DeleteOneID(u.ID) return c.DeleteOneID(_m.ID)
} }
// DeleteOneID returns a delete builder for the given id. // DeleteOneID returns a builder for deleting the given entity by its id.
func (c *UserClient) DeleteOneID(id int) *UserDeleteOne { func (c *UserClient) DeleteOneID(id int) *UserDeleteOne {
builder := c.Delete().Where(user.ID(id)) builder := c.Delete().Where(user.ID(id))
builder.mutation.id = &id builder.mutation.id = &id
@ -303,6 +446,8 @@ func (c *UserClient) DeleteOneID(id int) *UserDeleteOne {
func (c *UserClient) Query() *UserQuery { func (c *UserClient) Query() *UserQuery {
return &UserQuery{ return &UserQuery{
config: c.config, config: c.config,
ctx: &QueryContext{Type: TypeUser},
inters: c.Interceptors(),
} }
} }
@ -321,16 +466,16 @@ func (c *UserClient) GetX(ctx context.Context, id int) *User {
} }
// QueryOwner queries the owner edge of a User. // QueryOwner queries the owner edge of a User.
func (c *UserClient) QueryOwner(u *User) *PasswordTokenQuery { func (c *UserClient) QueryOwner(_m *User) *PasswordTokenQuery {
query := &PasswordTokenQuery{config: c.config} query := (&PasswordTokenClient{config: c.config}).Query()
query.path = func(ctx context.Context) (fromV *sql.Selector, _ error) { query.path = func(context.Context) (fromV *sql.Selector, _ error) {
id := u.ID id := _m.ID
step := sqlgraph.NewStep( step := sqlgraph.NewStep(
sqlgraph.From(user.Table, user.FieldID, id), sqlgraph.From(user.Table, user.FieldID, id),
sqlgraph.To(passwordtoken.Table, passwordtoken.FieldID), sqlgraph.To(passwordtoken.Table, passwordtoken.FieldID),
sqlgraph.Edge(sqlgraph.O2M, true, user.OwnerTable, user.OwnerColumn), sqlgraph.Edge(sqlgraph.O2M, true, user.OwnerTable, user.OwnerColumn),
) )
fromV = sqlgraph.Neighbors(u.driver.Dialect(), step) fromV = sqlgraph.Neighbors(_m.driver.Dialect(), step)
return fromV, nil return fromV, nil
} }
return query return query
@ -341,3 +486,33 @@ func (c *UserClient) Hooks() []Hook {
hooks := c.hooks.User hooks := c.hooks.User
return append(hooks[:len(hooks):len(hooks)], user.Hooks[:]...) return append(hooks[:len(hooks):len(hooks)], user.Hooks[:]...)
} }
// Interceptors returns the client interceptors.
func (c *UserClient) Interceptors() []Interceptor {
return c.inters.User
}
func (c *UserClient) mutate(ctx context.Context, m *UserMutation) (Value, error) {
switch m.Op() {
case OpCreate:
return (&UserCreate{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx)
case OpUpdate:
return (&UserUpdate{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx)
case OpUpdateOne:
return (&UserUpdateOne{config: c.config, hooks: c.Hooks(), mutation: m}).Save(ctx)
case OpDelete, OpDeleteOne:
return (&UserDelete{config: c.config, hooks: c.Hooks(), mutation: m}).Exec(ctx)
default:
return nil, fmt.Errorf("ent: unknown User mutation op: %q", m.Op())
}
}
// hooks and interceptors per client, for fast access.
type (
hooks struct {
PasswordToken, User []ent.Hook
}
inters struct {
PasswordToken, User []ent.Interceptor
}
)

View file

@ -1,60 +0,0 @@
// Code generated by entc, DO NOT EDIT.
package ent
import (
"entgo.io/ent"
"entgo.io/ent/dialect"
)
// Option function to configure the client.
type Option func(*config)
// Config is the configuration for the client and its builder.
type 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(...interface{})
// hooks to execute on mutations.
hooks *hooks
}
// hooks per client, for fast access.
type hooks struct {
PasswordToken []ent.Hook
User []ent.Hook
}
// 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(...interface{})) 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
}
}

View file

@ -1,33 +0,0 @@
// Code generated by entc, DO NOT EDIT.
package ent
import (
"context"
)
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)
}

View file

@ -1,58 +1,91 @@
// Code generated by entc, DO NOT EDIT. // Code generated by ent, DO NOT EDIT.
package ent package ent
import ( import (
"context"
"errors" "errors"
"fmt" "fmt"
"goweb/ent/passwordtoken" "reflect"
"goweb/ent/user" "sync"
"entgo.io/ent" "entgo.io/ent"
"entgo.io/ent/dialect/sql" "entgo.io/ent/dialect/sql"
"entgo.io/ent/dialect/sql/sqlgraph"
"github.com/mikestefanello/pagoda/ent/passwordtoken"
"github.com/mikestefanello/pagoda/ent/user"
) )
// ent aliases to avoid import conflicts in user's code. // ent aliases to avoid import conflicts in user's code.
type ( type (
Op = ent.Op Op = ent.Op
Hook = ent.Hook Hook = ent.Hook
Value = ent.Value Value = ent.Value
Query = ent.Query Query = ent.Query
Policy = ent.Policy QueryContext = ent.QueryContext
Mutator = ent.Mutator Querier = ent.Querier
Mutation = ent.Mutation QuerierFunc = ent.QuerierFunc
MutateFunc = ent.MutateFunc 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. // OrderFunc applies an ordering on the sql selector.
// Deprecated: Use Asc/Desc functions or the package builders instead.
type OrderFunc func(*sql.Selector) type OrderFunc func(*sql.Selector)
// columnChecker returns a function indicates if the column exists in the given column. var (
func columnChecker(table string) func(string) error { initCheck sync.Once
checks := map[string]func(string) bool{ columnCheck sql.ColumnCheck
passwordtoken.Table: passwordtoken.ValidColumn, )
user.Table: user.ValidColumn,
} // checkColumn checks if the column exists in the given table.
check, ok := checks[table] func checkColumn(t, c string) error {
if !ok { initCheck.Do(func() {
return func(string) error { columnCheck = sql.NewColumnCheck(map[string]func(string) bool{
return fmt.Errorf("unknown table %q", table) passwordtoken.Table: passwordtoken.ValidColumn,
} user.Table: user.ValidColumn,
} })
return func(column string) error { })
if !check(column) { return columnCheck(t, c)
return fmt.Errorf("unknown column %q for table %q", column, table)
}
return nil
}
} }
// Asc applies the given fields in ASC order. // Asc applies the given fields in ASC order.
func Asc(fields ...string) OrderFunc { func Asc(fields ...string) func(*sql.Selector) {
return func(s *sql.Selector) { return func(s *sql.Selector) {
check := columnChecker(s.TableName())
for _, f := range fields { for _, f := range fields {
if err := check(f); err != nil { if err := checkColumn(s.TableName(), f); err != nil {
s.AddError(&ValidationError{Name: f, err: fmt.Errorf("ent: %w", err)}) s.AddError(&ValidationError{Name: f, err: fmt.Errorf("ent: %w", err)})
} }
s.OrderBy(sql.Asc(s.C(f))) s.OrderBy(sql.Asc(s.C(f)))
@ -61,11 +94,10 @@ func Asc(fields ...string) OrderFunc {
} }
// Desc applies the given fields in DESC order. // Desc applies the given fields in DESC order.
func Desc(fields ...string) OrderFunc { func Desc(fields ...string) func(*sql.Selector) {
return func(s *sql.Selector) { return func(s *sql.Selector) {
check := columnChecker(s.TableName())
for _, f := range fields { for _, f := range fields {
if err := check(f); err != nil { if err := checkColumn(s.TableName(), f); err != nil {
s.AddError(&ValidationError{Name: f, err: fmt.Errorf("ent: %w", err)}) s.AddError(&ValidationError{Name: f, err: fmt.Errorf("ent: %w", err)})
} }
s.OrderBy(sql.Desc(s.C(f))) s.OrderBy(sql.Desc(s.C(f)))
@ -81,7 +113,6 @@ type AggregateFunc func(*sql.Selector) string
// GroupBy(field1, field2). // GroupBy(field1, field2).
// Aggregate(ent.As(ent.Sum(field1), "sum_field1"), (ent.As(ent.Sum(field2), "sum_field2")). // Aggregate(ent.As(ent.Sum(field1), "sum_field1"), (ent.As(ent.Sum(field2), "sum_field2")).
// Scan(ctx, &v) // Scan(ctx, &v)
//
func As(fn AggregateFunc, end string) AggregateFunc { func As(fn AggregateFunc, end string) AggregateFunc {
return func(s *sql.Selector) string { return func(s *sql.Selector) string {
return sql.As(fn(s), end) return sql.As(fn(s), end)
@ -98,8 +129,7 @@ func Count() AggregateFunc {
// Max applies the "max" aggregation function on the given field of each group. // Max applies the "max" aggregation function on the given field of each group.
func Max(field string) AggregateFunc { func Max(field string) AggregateFunc {
return func(s *sql.Selector) string { return func(s *sql.Selector) string {
check := columnChecker(s.TableName()) if err := checkColumn(s.TableName(), field); err != nil {
if err := check(field); err != nil {
s.AddError(&ValidationError{Name: field, err: fmt.Errorf("ent: %w", err)}) s.AddError(&ValidationError{Name: field, err: fmt.Errorf("ent: %w", err)})
return "" return ""
} }
@ -110,8 +140,7 @@ func Max(field string) AggregateFunc {
// Mean applies the "mean" aggregation function on the given field of each group. // Mean applies the "mean" aggregation function on the given field of each group.
func Mean(field string) AggregateFunc { func Mean(field string) AggregateFunc {
return func(s *sql.Selector) string { return func(s *sql.Selector) string {
check := columnChecker(s.TableName()) if err := checkColumn(s.TableName(), field); err != nil {
if err := check(field); err != nil {
s.AddError(&ValidationError{Name: field, err: fmt.Errorf("ent: %w", err)}) s.AddError(&ValidationError{Name: field, err: fmt.Errorf("ent: %w", err)})
return "" return ""
} }
@ -122,8 +151,7 @@ func Mean(field string) AggregateFunc {
// Min applies the "min" aggregation function on the given field of each group. // Min applies the "min" aggregation function on the given field of each group.
func Min(field string) AggregateFunc { func Min(field string) AggregateFunc {
return func(s *sql.Selector) string { return func(s *sql.Selector) string {
check := columnChecker(s.TableName()) if err := checkColumn(s.TableName(), field); err != nil {
if err := check(field); err != nil {
s.AddError(&ValidationError{Name: field, err: fmt.Errorf("ent: %w", err)}) s.AddError(&ValidationError{Name: field, err: fmt.Errorf("ent: %w", err)})
return "" return ""
} }
@ -134,8 +162,7 @@ func Min(field string) AggregateFunc {
// Sum applies the "sum" aggregation function on the given field of each group. // Sum applies the "sum" aggregation function on the given field of each group.
func Sum(field string) AggregateFunc { func Sum(field string) AggregateFunc {
return func(s *sql.Selector) string { return func(s *sql.Selector) string {
check := columnChecker(s.TableName()) if err := checkColumn(s.TableName(), field); err != nil {
if err := check(field); err != nil {
s.AddError(&ValidationError{Name: field, err: fmt.Errorf("ent: %w", err)}) s.AddError(&ValidationError{Name: field, err: fmt.Errorf("ent: %w", err)})
return "" return ""
} }
@ -143,7 +170,7 @@ func Sum(field string) AggregateFunc {
} }
} }
// ValidationError returns when validating a field fails. // ValidationError returns when validating a field or edge fails.
type ValidationError struct { type ValidationError struct {
Name string // Field or edge name. Name string // Field or edge name.
err error err error
@ -259,3 +286,325 @@ func IsConstraintError(err error) bool {
var e *ConstraintError var e *ConstraintError
return errors.As(err, &e) 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)

22
ent/entc.go Normal file
View file

@ -0,0 +1,22 @@
//go:build ignore
// +build ignore
package main
import (
"log"
"entgo.io/ent/entc"
"entgo.io/ent/entc/gen"
"github.com/mikestefanello/pagoda/ent/admin"
)
func main() {
err := entc.Generate("./schema",
&gen.Config{},
entc.Extensions(&admin.Extension{}),
)
if err != nil {
log.Fatal("running ent codegen:", err)
}
}

View file

@ -1,14 +1,16 @@
// Code generated by entc, DO NOT EDIT. // Code generated by ent, DO NOT EDIT.
package enttest package enttest
import ( import (
"context" "context"
"goweb/ent"
"github.com/mikestefanello/pagoda/ent"
// required by schema hooks. // required by schema hooks.
_ "goweb/ent/runtime" _ "github.com/mikestefanello/pagoda/ent/runtime"
"entgo.io/ent/dialect/sql/schema" "entgo.io/ent/dialect/sql/schema"
"github.com/mikestefanello/pagoda/ent/migrate"
) )
type ( type (
@ -16,7 +18,7 @@ type (
// testing.T and testing.B and used by enttest. // testing.T and testing.B and used by enttest.
TestingT interface { TestingT interface {
FailNow() FailNow()
Error(...interface{}) Error(...any)
} }
// Option configures client creation. // Option configures client creation.
@ -58,10 +60,7 @@ func Open(t TestingT, driverName, dataSourceName string, opts ...Option) *ent.Cl
t.Error(err) t.Error(err)
t.FailNow() t.FailNow()
} }
if err := c.Schema.Create(context.Background(), o.migrateOpts...); err != nil { migrateSchema(t, c, o)
t.Error(err)
t.FailNow()
}
return c return c
} }
@ -69,9 +68,17 @@ func Open(t TestingT, driverName, dataSourceName string, opts ...Option) *ent.Cl
func NewClient(t TestingT, opts ...Option) *ent.Client { func NewClient(t TestingT, opts ...Option) *ent.Client {
o := newOptions(opts) o := newOptions(opts)
c := ent.NewClient(o.opts...) c := ent.NewClient(o.opts...)
if err := c.Schema.Create(context.Background(), o.migrateOpts...); err != nil { 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.Error(err)
t.FailNow() t.FailNow()
} }
return c
} }

View file

@ -1,3 +1,3 @@
package ent package ent
//go:generate go run -mod=mod entgo.io/ent/cmd/ent generate ./schema //go:generate go run -mod=mod entc.go

View file

@ -1,11 +1,12 @@
// Code generated by entc, DO NOT EDIT. // Code generated by ent, DO NOT EDIT.
package hook package hook
import ( import (
"context" "context"
"fmt" "fmt"
"goweb/ent"
"github.com/mikestefanello/pagoda/ent"
) )
// The PasswordTokenFunc type is an adapter to allow the use of ordinary // The PasswordTokenFunc type is an adapter to allow the use of ordinary
@ -14,11 +15,10 @@ type PasswordTokenFunc func(context.Context, *ent.PasswordTokenMutation) (ent.Va
// Mutate calls f(ctx, m). // Mutate calls f(ctx, m).
func (f PasswordTokenFunc) Mutate(ctx context.Context, m ent.Mutation) (ent.Value, error) { func (f PasswordTokenFunc) Mutate(ctx context.Context, m ent.Mutation) (ent.Value, error) {
mv, ok := m.(*ent.PasswordTokenMutation) if mv, ok := m.(*ent.PasswordTokenMutation); ok {
if !ok { return f(ctx, mv)
return nil, fmt.Errorf("unexpected mutation type %T. expect *ent.PasswordTokenMutation", m)
} }
return f(ctx, mv) return nil, fmt.Errorf("unexpected mutation type %T. expect *ent.PasswordTokenMutation", m)
} }
// The UserFunc type is an adapter to allow the use of ordinary // The UserFunc type is an adapter to allow the use of ordinary
@ -27,11 +27,10 @@ type UserFunc func(context.Context, *ent.UserMutation) (ent.Value, error)
// Mutate calls f(ctx, m). // Mutate calls f(ctx, m).
func (f UserFunc) Mutate(ctx context.Context, m ent.Mutation) (ent.Value, error) { func (f UserFunc) Mutate(ctx context.Context, m ent.Mutation) (ent.Value, error) {
mv, ok := m.(*ent.UserMutation) if mv, ok := m.(*ent.UserMutation); ok {
if !ok { return f(ctx, mv)
return nil, fmt.Errorf("unexpected mutation type %T. expect *ent.UserMutation", m)
} }
return f(ctx, mv) return nil, fmt.Errorf("unexpected mutation type %T. expect *ent.UserMutation", m)
} }
// Condition is a hook condition function. // Condition is a hook condition function.
@ -129,7 +128,6 @@ func HasFields(field string, fields ...string) Condition {
// If executes the given hook under condition. // If executes the given hook under condition.
// //
// hook.If(ComputeAverage, And(HasFields(...), HasAddedFields(...))) // hook.If(ComputeAverage, And(HasFields(...), HasAddedFields(...)))
//
func If(hk ent.Hook, cond Condition) ent.Hook { func If(hk ent.Hook, cond Condition) ent.Hook {
return func(next ent.Mutator) ent.Mutator { return func(next ent.Mutator) ent.Mutator {
return ent.MutateFunc(func(ctx context.Context, m ent.Mutation) (ent.Value, error) { return ent.MutateFunc(func(ctx context.Context, m ent.Mutation) (ent.Value, error) {
@ -144,7 +142,6 @@ func If(hk ent.Hook, cond Condition) ent.Hook {
// On executes the given hook only for the given operation. // On executes the given hook only for the given operation.
// //
// hook.On(Log, ent.Delete|ent.Create) // hook.On(Log, ent.Delete|ent.Create)
//
func On(hk ent.Hook, op ent.Op) ent.Hook { func On(hk ent.Hook, op ent.Op) ent.Hook {
return If(hk, HasOp(op)) return If(hk, HasOp(op))
} }
@ -152,7 +149,6 @@ func On(hk ent.Hook, op ent.Op) ent.Hook {
// Unless skips the given hook only for the given operation. // Unless skips the given hook only for the given operation.
// //
// hook.Unless(Log, ent.Update|ent.UpdateOne) // hook.Unless(Log, ent.Update|ent.UpdateOne)
//
func Unless(hk ent.Hook, op ent.Op) ent.Hook { func Unless(hk ent.Hook, op ent.Op) ent.Hook {
return If(hk, Not(HasOp(op))) return If(hk, Not(HasOp(op)))
} }
@ -173,7 +169,6 @@ func FixedError(err error) ent.Hook {
// Reject(ent.Delete|ent.Update), // Reject(ent.Delete|ent.Update),
// } // }
// } // }
//
func Reject(op ent.Op) ent.Hook { func Reject(op ent.Op) ent.Hook {
hk := FixedError(fmt.Errorf("%s operation is not allowed", op)) hk := FixedError(fmt.Errorf("%s operation is not allowed", op))
return On(hk, op) return On(hk, op)

View file

@ -1,4 +1,4 @@
// Code generated by entc, DO NOT EDIT. // Code generated by ent, DO NOT EDIT.
package migrate package migrate
@ -28,17 +28,13 @@ var (
// and therefore, it's recommended to enable this option to get more // and therefore, it's recommended to enable this option to get more
// flexibility in the schema changes. // flexibility in the schema changes.
WithDropIndex = schema.WithDropIndex WithDropIndex = schema.WithDropIndex
// WithFixture sets the foreign-key renaming option to the migration when upgrading
// ent from v0.1.0 (issue-#285). Defaults to false.
WithFixture = schema.WithFixture
// WithForeignKeys enables creating foreign-key in schema DDL. This defaults to true. // WithForeignKeys enables creating foreign-key in schema DDL. This defaults to true.
WithForeignKeys = schema.WithForeignKeys WithForeignKeys = schema.WithForeignKeys
) )
// Schema is the API for creating, migrating and dropping a schema. // Schema is the API for creating, migrating and dropping a schema.
type Schema struct { type Schema struct {
drv dialect.Driver drv dialect.Driver
universalID bool
} }
// NewSchema creates a new schema client. // NewSchema creates a new schema client.
@ -46,27 +42,23 @@ func NewSchema(drv dialect.Driver) *Schema { return &Schema{drv: drv} }
// Create creates all schema resources. // Create creates all schema resources.
func (s *Schema) Create(ctx context.Context, opts ...schema.MigrateOption) error { 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...) migrate, err := schema.NewMigrate(s.drv, opts...)
if err != nil { if err != nil {
return fmt.Errorf("ent/migrate: %w", err) return fmt.Errorf("ent/migrate: %w", err)
} }
return migrate.Create(ctx, Tables...) return migrate.Create(ctx, tables...)
} }
// WriteTo writes the schema changes to w instead of running them against the database. // 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 { // if err := client.Schema.WriteTo(context.Background(), os.Stdout); err != nil {
// log.Fatal(err) // log.Fatal(err)
// } // }
//
func (s *Schema) WriteTo(ctx context.Context, w io.Writer, opts ...schema.MigrateOption) error { func (s *Schema) WriteTo(ctx context.Context, w io.Writer, opts ...schema.MigrateOption) error {
drv := &schema.WriteDriver{ return Create(ctx, &Schema{drv: &schema.WriteDriver{Writer: w, Driver: s.drv}}, Tables, opts...)
Writer: w,
Driver: s.drv,
}
migrate, err := schema.NewMigrate(drv, opts...)
if err != nil {
return fmt.Errorf("ent/migrate: %w", err)
}
return migrate.Create(ctx, Tables...)
} }

View file

@ -1,4 +1,4 @@
// Code generated by entc, DO NOT EDIT. // Code generated by ent, DO NOT EDIT.
package migrate package migrate
@ -11,9 +11,9 @@ var (
// PasswordTokensColumns holds the columns for the "password_tokens" table. // PasswordTokensColumns holds the columns for the "password_tokens" table.
PasswordTokensColumns = []*schema.Column{ PasswordTokensColumns = []*schema.Column{
{Name: "id", Type: field.TypeInt, Increment: true}, {Name: "id", Type: field.TypeInt, Increment: true},
{Name: "hash", Type: field.TypeString}, {Name: "token", Type: field.TypeString},
{Name: "created_at", Type: field.TypeTime}, {Name: "created_at", Type: field.TypeTime},
{Name: "password_token_user", Type: field.TypeInt, Nullable: true}, {Name: "user_id", Type: field.TypeInt},
} }
// PasswordTokensTable holds the schema information for the "password_tokens" table. // PasswordTokensTable holds the schema information for the "password_tokens" table.
PasswordTokensTable = &schema.Table{ PasswordTokensTable = &schema.Table{
@ -25,7 +25,7 @@ var (
Symbol: "password_tokens_users_user", Symbol: "password_tokens_users_user",
Columns: []*schema.Column{PasswordTokensColumns[3]}, Columns: []*schema.Column{PasswordTokensColumns[3]},
RefColumns: []*schema.Column{UsersColumns[0]}, RefColumns: []*schema.Column{UsersColumns[0]},
OnDelete: schema.SetNull, OnDelete: schema.NoAction,
}, },
}, },
} }
@ -35,6 +35,8 @@ var (
{Name: "name", Type: field.TypeString}, {Name: "name", Type: field.TypeString},
{Name: "email", Type: field.TypeString, Unique: true}, {Name: "email", Type: field.TypeString, Unique: true},
{Name: "password", Type: field.TypeString}, {Name: "password", Type: field.TypeString},
{Name: "verified", Type: field.TypeBool, Default: false},
{Name: "admin", Type: field.TypeBool, Default: false},
{Name: "created_at", Type: field.TypeTime}, {Name: "created_at", Type: field.TypeTime},
} }
// UsersTable holds the schema information for the "users" table. // UsersTable holds the schema information for the "users" table.

View file

@ -1,17 +1,19 @@
// Code generated by entc, DO NOT EDIT. // Code generated by ent, DO NOT EDIT.
package ent package ent
import ( import (
"context" "context"
"errors"
"fmt" "fmt"
"goweb/ent/passwordtoken"
"goweb/ent/predicate"
"goweb/ent/user"
"sync" "sync"
"time" "time"
"entgo.io/ent" "entgo.io/ent"
"entgo.io/ent/dialect/sql"
"github.com/mikestefanello/pagoda/ent/passwordtoken"
"github.com/mikestefanello/pagoda/ent/predicate"
"github.com/mikestefanello/pagoda/ent/user"
) )
const ( const (
@ -33,7 +35,7 @@ type PasswordTokenMutation struct {
op Op op Op
typ string typ string
id *int id *int
hash *string token *string
created_at *time.Time created_at *time.Time
clearedFields map[string]struct{} clearedFields map[string]struct{}
user *int user *int
@ -73,7 +75,7 @@ func withPasswordTokenID(id int) passwordtokenOption {
m.oldValue = func(ctx context.Context) (*PasswordToken, error) { m.oldValue = func(ctx context.Context) (*PasswordToken, error) {
once.Do(func() { once.Do(func() {
if m.done { if m.done {
err = fmt.Errorf("querying old values post mutation is not allowed") err = errors.New("querying old values post mutation is not allowed")
} else { } else {
value, err = m.Client().PasswordToken.Get(ctx, id) value, err = m.Client().PasswordToken.Get(ctx, id)
} }
@ -106,7 +108,7 @@ func (m PasswordTokenMutation) Client() *Client {
// it returns an error otherwise. // it returns an error otherwise.
func (m PasswordTokenMutation) Tx() (*Tx, error) { func (m PasswordTokenMutation) Tx() (*Tx, error) {
if _, ok := m.driver.(*txDriver); !ok { if _, ok := m.driver.(*txDriver); !ok {
return nil, fmt.Errorf("ent: mutation is not running in a transaction") return nil, errors.New("ent: mutation is not running in a transaction")
} }
tx := &Tx{config: m.config} tx := &Tx{config: m.config}
tx.init() tx.init()
@ -122,40 +124,95 @@ func (m *PasswordTokenMutation) ID() (id int, exists bool) {
return *m.id, true return *m.id, true
} }
// SetHash sets the "hash" field. // IDs queries the database and returns the entity ids that match the mutation's predicate.
func (m *PasswordTokenMutation) SetHash(s string) { // That means, if the mutation is applied within a transaction with an isolation level such
m.hash = &s // as sql.LevelSerializable, the returned ids match the ids of the rows that will be updated
// or updated by the mutation.
func (m *PasswordTokenMutation) IDs(ctx context.Context) ([]int, error) {
switch {
case m.op.Is(OpUpdateOne | OpDeleteOne):
id, exists := m.ID()
if exists {
return []int{id}, nil
}
fallthrough
case m.op.Is(OpUpdate | OpDelete):
return m.Client().PasswordToken.Query().Where(m.predicates...).IDs(ctx)
default:
return nil, fmt.Errorf("IDs is not allowed on %s operations", m.op)
}
} }
// Hash returns the value of the "hash" field in the mutation. // SetToken sets the "token" field.
func (m *PasswordTokenMutation) Hash() (r string, exists bool) { func (m *PasswordTokenMutation) SetToken(s string) {
v := m.hash m.token = &s
}
// Token returns the value of the "token" field in the mutation.
func (m *PasswordTokenMutation) Token() (r string, exists bool) {
v := m.token
if v == nil { if v == nil {
return return
} }
return *v, true return *v, true
} }
// OldHash returns the old "hash" field's value of the PasswordToken entity. // OldToken returns the old "token" field's value of the PasswordToken entity.
// If the PasswordToken object wasn't provided to the builder, the object is fetched from the database. // If the PasswordToken object wasn't provided to the builder, the object is fetched from the database.
// An error is returned if the mutation operation is not UpdateOne, or the database query fails. // An error is returned if the mutation operation is not UpdateOne, or the database query fails.
func (m *PasswordTokenMutation) OldHash(ctx context.Context) (v string, err error) { func (m *PasswordTokenMutation) OldToken(ctx context.Context) (v string, err error) {
if !m.op.Is(OpUpdateOne) { if !m.op.Is(OpUpdateOne) {
return v, fmt.Errorf("OldHash is only allowed on UpdateOne operations") return v, errors.New("OldToken is only allowed on UpdateOne operations")
} }
if m.id == nil || m.oldValue == nil { if m.id == nil || m.oldValue == nil {
return v, fmt.Errorf("OldHash requires an ID field in the mutation") return v, errors.New("OldToken requires an ID field in the mutation")
} }
oldValue, err := m.oldValue(ctx) oldValue, err := m.oldValue(ctx)
if err != nil { if err != nil {
return v, fmt.Errorf("querying old value for OldHash: %w", err) return v, fmt.Errorf("querying old value for OldToken: %w", err)
} }
return oldValue.Hash, nil return oldValue.Token, nil
} }
// ResetHash resets all changes to the "hash" field. // ResetToken resets all changes to the "token" field.
func (m *PasswordTokenMutation) ResetHash() { func (m *PasswordTokenMutation) ResetToken() {
m.hash = nil m.token = nil
}
// SetUserID sets the "user_id" field.
func (m *PasswordTokenMutation) SetUserID(i int) {
m.user = &i
}
// UserID returns the value of the "user_id" field in the mutation.
func (m *PasswordTokenMutation) UserID() (r int, exists bool) {
v := m.user
if v == nil {
return
}
return *v, true
}
// OldUserID returns the old "user_id" field's value of the PasswordToken entity.
// If the PasswordToken object wasn't provided to the builder, the object is fetched from the database.
// An error is returned if the mutation operation is not UpdateOne, or the database query fails.
func (m *PasswordTokenMutation) OldUserID(ctx context.Context) (v int, err error) {
if !m.op.Is(OpUpdateOne) {
return v, errors.New("OldUserID is only allowed on UpdateOne operations")
}
if m.id == nil || m.oldValue == nil {
return v, errors.New("OldUserID requires an ID field in the mutation")
}
oldValue, err := m.oldValue(ctx)
if err != nil {
return v, fmt.Errorf("querying old value for OldUserID: %w", err)
}
return oldValue.UserID, nil
}
// ResetUserID resets all changes to the "user_id" field.
func (m *PasswordTokenMutation) ResetUserID() {
m.user = nil
} }
// SetCreatedAt sets the "created_at" field. // SetCreatedAt sets the "created_at" field.
@ -177,10 +234,10 @@ func (m *PasswordTokenMutation) CreatedAt() (r time.Time, exists bool) {
// An error is returned if the mutation operation is not UpdateOne, or the database query fails. // An error is returned if the mutation operation is not UpdateOne, or the database query fails.
func (m *PasswordTokenMutation) OldCreatedAt(ctx context.Context) (v time.Time, err error) { func (m *PasswordTokenMutation) OldCreatedAt(ctx context.Context) (v time.Time, err error) {
if !m.op.Is(OpUpdateOne) { if !m.op.Is(OpUpdateOne) {
return v, fmt.Errorf("OldCreatedAt is only allowed on UpdateOne operations") return v, errors.New("OldCreatedAt is only allowed on UpdateOne operations")
} }
if m.id == nil || m.oldValue == nil { if m.id == nil || m.oldValue == nil {
return v, fmt.Errorf("OldCreatedAt requires an ID field in the mutation") return v, errors.New("OldCreatedAt requires an ID field in the mutation")
} }
oldValue, err := m.oldValue(ctx) oldValue, err := m.oldValue(ctx)
if err != nil { if err != nil {
@ -194,14 +251,10 @@ func (m *PasswordTokenMutation) ResetCreatedAt() {
m.created_at = nil m.created_at = nil
} }
// SetUserID sets the "user" edge to the User entity by id.
func (m *PasswordTokenMutation) SetUserID(id int) {
m.user = &id
}
// ClearUser clears the "user" edge to the User entity. // ClearUser clears the "user" edge to the User entity.
func (m *PasswordTokenMutation) ClearUser() { func (m *PasswordTokenMutation) ClearUser() {
m.cleareduser = true m.cleareduser = true
m.clearedFields[passwordtoken.FieldUserID] = struct{}{}
} }
// UserCleared reports if the "user" edge to the User entity was cleared. // UserCleared reports if the "user" edge to the User entity was cleared.
@ -209,14 +262,6 @@ func (m *PasswordTokenMutation) UserCleared() bool {
return m.cleareduser return m.cleareduser
} }
// UserID returns the "user" edge ID in the mutation.
func (m *PasswordTokenMutation) UserID() (id int, exists bool) {
if m.user != nil {
return *m.user, true
}
return
}
// UserIDs returns the "user" edge IDs in the mutation. // UserIDs returns the "user" edge IDs in the mutation.
// Note that IDs always returns len(IDs) <= 1 for unique edges, and you should use // Note that IDs always returns len(IDs) <= 1 for unique edges, and you should use
// UserID instead. It exists only for internal usage by the builders. // UserID instead. It exists only for internal usage by the builders.
@ -238,11 +283,26 @@ func (m *PasswordTokenMutation) Where(ps ...predicate.PasswordToken) {
m.predicates = append(m.predicates, ps...) m.predicates = append(m.predicates, ps...)
} }
// WhereP appends storage-level predicates to the PasswordTokenMutation builder. Using this method,
// users can use type-assertion to append predicates that do not depend on any generated package.
func (m *PasswordTokenMutation) WhereP(ps ...func(*sql.Selector)) {
p := make([]predicate.PasswordToken, len(ps))
for i := range ps {
p[i] = ps[i]
}
m.Where(p...)
}
// Op returns the operation name. // Op returns the operation name.
func (m *PasswordTokenMutation) Op() Op { func (m *PasswordTokenMutation) Op() Op {
return m.op return m.op
} }
// SetOp allows setting the mutation operation.
func (m *PasswordTokenMutation) SetOp(op Op) {
m.op = op
}
// Type returns the node type of this mutation (PasswordToken). // Type returns the node type of this mutation (PasswordToken).
func (m *PasswordTokenMutation) Type() string { func (m *PasswordTokenMutation) Type() string {
return m.typ return m.typ
@ -252,9 +312,12 @@ func (m *PasswordTokenMutation) Type() string {
// order to get all numeric fields that were incremented/decremented, call // order to get all numeric fields that were incremented/decremented, call
// AddedFields(). // AddedFields().
func (m *PasswordTokenMutation) Fields() []string { func (m *PasswordTokenMutation) Fields() []string {
fields := make([]string, 0, 2) fields := make([]string, 0, 3)
if m.hash != nil { if m.token != nil {
fields = append(fields, passwordtoken.FieldHash) fields = append(fields, passwordtoken.FieldToken)
}
if m.user != nil {
fields = append(fields, passwordtoken.FieldUserID)
} }
if m.created_at != nil { if m.created_at != nil {
fields = append(fields, passwordtoken.FieldCreatedAt) fields = append(fields, passwordtoken.FieldCreatedAt)
@ -267,8 +330,10 @@ func (m *PasswordTokenMutation) Fields() []string {
// schema. // schema.
func (m *PasswordTokenMutation) Field(name string) (ent.Value, bool) { func (m *PasswordTokenMutation) Field(name string) (ent.Value, bool) {
switch name { switch name {
case passwordtoken.FieldHash: case passwordtoken.FieldToken:
return m.Hash() return m.Token()
case passwordtoken.FieldUserID:
return m.UserID()
case passwordtoken.FieldCreatedAt: case passwordtoken.FieldCreatedAt:
return m.CreatedAt() return m.CreatedAt()
} }
@ -280,8 +345,10 @@ func (m *PasswordTokenMutation) Field(name string) (ent.Value, bool) {
// database failed. // database failed.
func (m *PasswordTokenMutation) OldField(ctx context.Context, name string) (ent.Value, error) { func (m *PasswordTokenMutation) OldField(ctx context.Context, name string) (ent.Value, error) {
switch name { switch name {
case passwordtoken.FieldHash: case passwordtoken.FieldToken:
return m.OldHash(ctx) return m.OldToken(ctx)
case passwordtoken.FieldUserID:
return m.OldUserID(ctx)
case passwordtoken.FieldCreatedAt: case passwordtoken.FieldCreatedAt:
return m.OldCreatedAt(ctx) return m.OldCreatedAt(ctx)
} }
@ -293,12 +360,19 @@ func (m *PasswordTokenMutation) OldField(ctx context.Context, name string) (ent.
// type. // type.
func (m *PasswordTokenMutation) SetField(name string, value ent.Value) error { func (m *PasswordTokenMutation) SetField(name string, value ent.Value) error {
switch name { switch name {
case passwordtoken.FieldHash: case passwordtoken.FieldToken:
v, ok := value.(string) v, ok := value.(string)
if !ok { if !ok {
return fmt.Errorf("unexpected type %T for field %s", value, name) return fmt.Errorf("unexpected type %T for field %s", value, name)
} }
m.SetHash(v) m.SetToken(v)
return nil
case passwordtoken.FieldUserID:
v, ok := value.(int)
if !ok {
return fmt.Errorf("unexpected type %T for field %s", value, name)
}
m.SetUserID(v)
return nil return nil
case passwordtoken.FieldCreatedAt: case passwordtoken.FieldCreatedAt:
v, ok := value.(time.Time) v, ok := value.(time.Time)
@ -314,13 +388,16 @@ func (m *PasswordTokenMutation) SetField(name string, value ent.Value) error {
// AddedFields returns all numeric fields that were incremented/decremented during // AddedFields returns all numeric fields that were incremented/decremented during
// this mutation. // this mutation.
func (m *PasswordTokenMutation) AddedFields() []string { func (m *PasswordTokenMutation) AddedFields() []string {
return nil var fields []string
return fields
} }
// AddedField returns the numeric value that was incremented/decremented on a field // 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 // with the given name. The second boolean return value indicates that this field
// was not set, or was not defined in the schema. // was not set, or was not defined in the schema.
func (m *PasswordTokenMutation) AddedField(name string) (ent.Value, bool) { func (m *PasswordTokenMutation) AddedField(name string) (ent.Value, bool) {
switch name {
}
return nil, false return nil, false
} }
@ -356,8 +433,11 @@ func (m *PasswordTokenMutation) ClearField(name string) error {
// It returns an error if the field is not defined in the schema. // It returns an error if the field is not defined in the schema.
func (m *PasswordTokenMutation) ResetField(name string) error { func (m *PasswordTokenMutation) ResetField(name string) error {
switch name { switch name {
case passwordtoken.FieldHash: case passwordtoken.FieldToken:
m.ResetHash() m.ResetToken()
return nil
case passwordtoken.FieldUserID:
m.ResetUserID()
return nil return nil
case passwordtoken.FieldCreatedAt: case passwordtoken.FieldCreatedAt:
m.ResetCreatedAt() m.ResetCreatedAt()
@ -396,8 +476,6 @@ func (m *PasswordTokenMutation) RemovedEdges() []string {
// RemovedIDs returns all IDs (to other nodes) that were removed for the edge with // RemovedIDs returns all IDs (to other nodes) that were removed for the edge with
// the given name in this mutation. // the given name in this mutation.
func (m *PasswordTokenMutation) RemovedIDs(name string) []ent.Value { func (m *PasswordTokenMutation) RemovedIDs(name string) []ent.Value {
switch name {
}
return nil return nil
} }
@ -451,6 +529,8 @@ type UserMutation struct {
name *string name *string
email *string email *string
password *string password *string
verified *bool
admin *bool
created_at *time.Time created_at *time.Time
clearedFields map[string]struct{} clearedFields map[string]struct{}
owner map[int]struct{} owner map[int]struct{}
@ -491,7 +571,7 @@ func withUserID(id int) userOption {
m.oldValue = func(ctx context.Context) (*User, error) { m.oldValue = func(ctx context.Context) (*User, error) {
once.Do(func() { once.Do(func() {
if m.done { if m.done {
err = fmt.Errorf("querying old values post mutation is not allowed") err = errors.New("querying old values post mutation is not allowed")
} else { } else {
value, err = m.Client().User.Get(ctx, id) value, err = m.Client().User.Get(ctx, id)
} }
@ -524,7 +604,7 @@ func (m UserMutation) Client() *Client {
// it returns an error otherwise. // it returns an error otherwise.
func (m UserMutation) Tx() (*Tx, error) { func (m UserMutation) Tx() (*Tx, error) {
if _, ok := m.driver.(*txDriver); !ok { if _, ok := m.driver.(*txDriver); !ok {
return nil, fmt.Errorf("ent: mutation is not running in a transaction") return nil, errors.New("ent: mutation is not running in a transaction")
} }
tx := &Tx{config: m.config} tx := &Tx{config: m.config}
tx.init() tx.init()
@ -540,6 +620,25 @@ func (m *UserMutation) ID() (id int, exists bool) {
return *m.id, true return *m.id, true
} }
// IDs queries the database and returns the entity ids that match the mutation's predicate.
// That means, if the mutation is applied within a transaction with an isolation level such
// as sql.LevelSerializable, the returned ids match the ids of the rows that will be updated
// or updated by the mutation.
func (m *UserMutation) IDs(ctx context.Context) ([]int, error) {
switch {
case m.op.Is(OpUpdateOne | OpDeleteOne):
id, exists := m.ID()
if exists {
return []int{id}, nil
}
fallthrough
case m.op.Is(OpUpdate | OpDelete):
return m.Client().User.Query().Where(m.predicates...).IDs(ctx)
default:
return nil, fmt.Errorf("IDs is not allowed on %s operations", m.op)
}
}
// SetName sets the "name" field. // SetName sets the "name" field.
func (m *UserMutation) SetName(s string) { func (m *UserMutation) SetName(s string) {
m.name = &s m.name = &s
@ -559,10 +658,10 @@ func (m *UserMutation) Name() (r string, exists bool) {
// An error is returned if the mutation operation is not UpdateOne, or the database query fails. // An error is returned if the mutation operation is not UpdateOne, or the database query fails.
func (m *UserMutation) OldName(ctx context.Context) (v string, err error) { func (m *UserMutation) OldName(ctx context.Context) (v string, err error) {
if !m.op.Is(OpUpdateOne) { if !m.op.Is(OpUpdateOne) {
return v, fmt.Errorf("OldName is only allowed on UpdateOne operations") return v, errors.New("OldName is only allowed on UpdateOne operations")
} }
if m.id == nil || m.oldValue == nil { if m.id == nil || m.oldValue == nil {
return v, fmt.Errorf("OldName requires an ID field in the mutation") return v, errors.New("OldName requires an ID field in the mutation")
} }
oldValue, err := m.oldValue(ctx) oldValue, err := m.oldValue(ctx)
if err != nil { if err != nil {
@ -595,10 +694,10 @@ func (m *UserMutation) Email() (r string, exists bool) {
// An error is returned if the mutation operation is not UpdateOne, or the database query fails. // An error is returned if the mutation operation is not UpdateOne, or the database query fails.
func (m *UserMutation) OldEmail(ctx context.Context) (v string, err error) { func (m *UserMutation) OldEmail(ctx context.Context) (v string, err error) {
if !m.op.Is(OpUpdateOne) { if !m.op.Is(OpUpdateOne) {
return v, fmt.Errorf("OldEmail is only allowed on UpdateOne operations") return v, errors.New("OldEmail is only allowed on UpdateOne operations")
} }
if m.id == nil || m.oldValue == nil { if m.id == nil || m.oldValue == nil {
return v, fmt.Errorf("OldEmail requires an ID field in the mutation") return v, errors.New("OldEmail requires an ID field in the mutation")
} }
oldValue, err := m.oldValue(ctx) oldValue, err := m.oldValue(ctx)
if err != nil { if err != nil {
@ -631,10 +730,10 @@ func (m *UserMutation) Password() (r string, exists bool) {
// An error is returned if the mutation operation is not UpdateOne, or the database query fails. // An error is returned if the mutation operation is not UpdateOne, or the database query fails.
func (m *UserMutation) OldPassword(ctx context.Context) (v string, err error) { func (m *UserMutation) OldPassword(ctx context.Context) (v string, err error) {
if !m.op.Is(OpUpdateOne) { if !m.op.Is(OpUpdateOne) {
return v, fmt.Errorf("OldPassword is only allowed on UpdateOne operations") return v, errors.New("OldPassword is only allowed on UpdateOne operations")
} }
if m.id == nil || m.oldValue == nil { if m.id == nil || m.oldValue == nil {
return v, fmt.Errorf("OldPassword requires an ID field in the mutation") return v, errors.New("OldPassword requires an ID field in the mutation")
} }
oldValue, err := m.oldValue(ctx) oldValue, err := m.oldValue(ctx)
if err != nil { if err != nil {
@ -648,6 +747,78 @@ func (m *UserMutation) ResetPassword() {
m.password = nil m.password = nil
} }
// SetVerified sets the "verified" field.
func (m *UserMutation) SetVerified(b bool) {
m.verified = &b
}
// Verified returns the value of the "verified" field in the mutation.
func (m *UserMutation) Verified() (r bool, exists bool) {
v := m.verified
if v == nil {
return
}
return *v, true
}
// OldVerified returns the old "verified" field's value of the User entity.
// If the User object wasn't provided to the builder, the object is fetched from the database.
// An error is returned if the mutation operation is not UpdateOne, or the database query fails.
func (m *UserMutation) OldVerified(ctx context.Context) (v bool, err error) {
if !m.op.Is(OpUpdateOne) {
return v, errors.New("OldVerified is only allowed on UpdateOne operations")
}
if m.id == nil || m.oldValue == nil {
return v, errors.New("OldVerified requires an ID field in the mutation")
}
oldValue, err := m.oldValue(ctx)
if err != nil {
return v, fmt.Errorf("querying old value for OldVerified: %w", err)
}
return oldValue.Verified, nil
}
// ResetVerified resets all changes to the "verified" field.
func (m *UserMutation) ResetVerified() {
m.verified = nil
}
// SetAdmin sets the "admin" field.
func (m *UserMutation) SetAdmin(b bool) {
m.admin = &b
}
// Admin returns the value of the "admin" field in the mutation.
func (m *UserMutation) Admin() (r bool, exists bool) {
v := m.admin
if v == nil {
return
}
return *v, true
}
// OldAdmin returns the old "admin" field's value of the User entity.
// If the User object wasn't provided to the builder, the object is fetched from the database.
// An error is returned if the mutation operation is not UpdateOne, or the database query fails.
func (m *UserMutation) OldAdmin(ctx context.Context) (v bool, err error) {
if !m.op.Is(OpUpdateOne) {
return v, errors.New("OldAdmin is only allowed on UpdateOne operations")
}
if m.id == nil || m.oldValue == nil {
return v, errors.New("OldAdmin requires an ID field in the mutation")
}
oldValue, err := m.oldValue(ctx)
if err != nil {
return v, fmt.Errorf("querying old value for OldAdmin: %w", err)
}
return oldValue.Admin, nil
}
// ResetAdmin resets all changes to the "admin" field.
func (m *UserMutation) ResetAdmin() {
m.admin = nil
}
// SetCreatedAt sets the "created_at" field. // SetCreatedAt sets the "created_at" field.
func (m *UserMutation) SetCreatedAt(t time.Time) { func (m *UserMutation) SetCreatedAt(t time.Time) {
m.created_at = &t m.created_at = &t
@ -667,10 +838,10 @@ func (m *UserMutation) CreatedAt() (r time.Time, exists bool) {
// An error is returned if the mutation operation is not UpdateOne, or the database query fails. // An error is returned if the mutation operation is not UpdateOne, or the database query fails.
func (m *UserMutation) OldCreatedAt(ctx context.Context) (v time.Time, err error) { func (m *UserMutation) OldCreatedAt(ctx context.Context) (v time.Time, err error) {
if !m.op.Is(OpUpdateOne) { if !m.op.Is(OpUpdateOne) {
return v, fmt.Errorf("OldCreatedAt is only allowed on UpdateOne operations") return v, errors.New("OldCreatedAt is only allowed on UpdateOne operations")
} }
if m.id == nil || m.oldValue == nil { if m.id == nil || m.oldValue == nil {
return v, fmt.Errorf("OldCreatedAt requires an ID field in the mutation") return v, errors.New("OldCreatedAt requires an ID field in the mutation")
} }
oldValue, err := m.oldValue(ctx) oldValue, err := m.oldValue(ctx)
if err != nil { if err != nil {
@ -743,11 +914,26 @@ func (m *UserMutation) Where(ps ...predicate.User) {
m.predicates = append(m.predicates, ps...) m.predicates = append(m.predicates, ps...)
} }
// WhereP appends storage-level predicates to the UserMutation builder. Using this method,
// users can use type-assertion to append predicates that do not depend on any generated package.
func (m *UserMutation) WhereP(ps ...func(*sql.Selector)) {
p := make([]predicate.User, len(ps))
for i := range ps {
p[i] = ps[i]
}
m.Where(p...)
}
// Op returns the operation name. // Op returns the operation name.
func (m *UserMutation) Op() Op { func (m *UserMutation) Op() Op {
return m.op return m.op
} }
// SetOp allows setting the mutation operation.
func (m *UserMutation) SetOp(op Op) {
m.op = op
}
// Type returns the node type of this mutation (User). // Type returns the node type of this mutation (User).
func (m *UserMutation) Type() string { func (m *UserMutation) Type() string {
return m.typ return m.typ
@ -757,7 +943,7 @@ func (m *UserMutation) Type() string {
// order to get all numeric fields that were incremented/decremented, call // order to get all numeric fields that were incremented/decremented, call
// AddedFields(). // AddedFields().
func (m *UserMutation) Fields() []string { func (m *UserMutation) Fields() []string {
fields := make([]string, 0, 4) fields := make([]string, 0, 6)
if m.name != nil { if m.name != nil {
fields = append(fields, user.FieldName) fields = append(fields, user.FieldName)
} }
@ -767,6 +953,12 @@ func (m *UserMutation) Fields() []string {
if m.password != nil { if m.password != nil {
fields = append(fields, user.FieldPassword) fields = append(fields, user.FieldPassword)
} }
if m.verified != nil {
fields = append(fields, user.FieldVerified)
}
if m.admin != nil {
fields = append(fields, user.FieldAdmin)
}
if m.created_at != nil { if m.created_at != nil {
fields = append(fields, user.FieldCreatedAt) fields = append(fields, user.FieldCreatedAt)
} }
@ -784,6 +976,10 @@ func (m *UserMutation) Field(name string) (ent.Value, bool) {
return m.Email() return m.Email()
case user.FieldPassword: case user.FieldPassword:
return m.Password() return m.Password()
case user.FieldVerified:
return m.Verified()
case user.FieldAdmin:
return m.Admin()
case user.FieldCreatedAt: case user.FieldCreatedAt:
return m.CreatedAt() return m.CreatedAt()
} }
@ -801,6 +997,10 @@ func (m *UserMutation) OldField(ctx context.Context, name string) (ent.Value, er
return m.OldEmail(ctx) return m.OldEmail(ctx)
case user.FieldPassword: case user.FieldPassword:
return m.OldPassword(ctx) return m.OldPassword(ctx)
case user.FieldVerified:
return m.OldVerified(ctx)
case user.FieldAdmin:
return m.OldAdmin(ctx)
case user.FieldCreatedAt: case user.FieldCreatedAt:
return m.OldCreatedAt(ctx) return m.OldCreatedAt(ctx)
} }
@ -833,6 +1033,20 @@ func (m *UserMutation) SetField(name string, value ent.Value) error {
} }
m.SetPassword(v) m.SetPassword(v)
return nil return nil
case user.FieldVerified:
v, ok := value.(bool)
if !ok {
return fmt.Errorf("unexpected type %T for field %s", value, name)
}
m.SetVerified(v)
return nil
case user.FieldAdmin:
v, ok := value.(bool)
if !ok {
return fmt.Errorf("unexpected type %T for field %s", value, name)
}
m.SetAdmin(v)
return nil
case user.FieldCreatedAt: case user.FieldCreatedAt:
v, ok := value.(time.Time) v, ok := value.(time.Time)
if !ok { if !ok {
@ -898,6 +1112,12 @@ func (m *UserMutation) ResetField(name string) error {
case user.FieldPassword: case user.FieldPassword:
m.ResetPassword() m.ResetPassword()
return nil return nil
case user.FieldVerified:
m.ResetVerified()
return nil
case user.FieldAdmin:
m.ResetAdmin()
return nil
case user.FieldCreatedAt: case user.FieldCreatedAt:
m.ResetCreatedAt() m.ResetCreatedAt()
return nil return nil

View file

@ -1,15 +1,16 @@
// Code generated by entc, DO NOT EDIT. // Code generated by ent, DO NOT EDIT.
package ent package ent
import ( import (
"fmt" "fmt"
"goweb/ent/passwordtoken"
"goweb/ent/user"
"strings" "strings"
"time" "time"
"entgo.io/ent"
"entgo.io/ent/dialect/sql" "entgo.io/ent/dialect/sql"
"github.com/mikestefanello/pagoda/ent/passwordtoken"
"github.com/mikestefanello/pagoda/ent/user"
) )
// PasswordToken is the model entity for the PasswordToken schema. // PasswordToken is the model entity for the PasswordToken schema.
@ -17,14 +18,16 @@ type PasswordToken struct {
config `json:"-"` config `json:"-"`
// ID of the ent. // ID of the ent.
ID int `json:"id,omitempty"` ID int `json:"id,omitempty"`
// Hash holds the value of the "hash" field. // Token holds the value of the "token" field.
Hash string `json:"-"` Token string `json:"-"`
// UserID holds the value of the "user_id" field.
UserID int `json:"user_id,omitempty"`
// CreatedAt holds the value of the "created_at" field. // CreatedAt holds the value of the "created_at" field.
CreatedAt time.Time `json:"created_at,omitempty"` CreatedAt time.Time `json:"created_at,omitempty"`
// Edges holds the relations/edges for other nodes in the graph. // Edges holds the relations/edges for other nodes in the graph.
// The values are being populated by the PasswordTokenQuery when eager-loading is set. // The values are being populated by the PasswordTokenQuery when eager-loading is set.
Edges PasswordTokenEdges `json:"edges"` Edges PasswordTokenEdges `json:"edges"`
password_token_user *int selectValues sql.SelectValues
} }
// PasswordTokenEdges holds the relations/edges for other nodes in the graph. // PasswordTokenEdges holds the relations/edges for other nodes in the graph.
@ -39,32 +42,27 @@ type PasswordTokenEdges struct {
// UserOrErr returns the User value or an error if the edge // UserOrErr returns the User value or an error if the edge
// was not loaded in eager-loading, or loaded but was not found. // was not loaded in eager-loading, or loaded but was not found.
func (e PasswordTokenEdges) UserOrErr() (*User, error) { func (e PasswordTokenEdges) UserOrErr() (*User, error) {
if e.loadedTypes[0] { if e.User != nil {
if e.User == nil {
// The edge user was loaded in eager-loading,
// but was not found.
return nil, &NotFoundError{label: user.Label}
}
return e.User, nil return e.User, nil
} else if e.loadedTypes[0] {
return nil, &NotFoundError{label: user.Label}
} }
return nil, &NotLoadedError{edge: "user"} return nil, &NotLoadedError{edge: "user"}
} }
// scanValues returns the types for scanning values from sql.Rows. // scanValues returns the types for scanning values from sql.Rows.
func (*PasswordToken) scanValues(columns []string) ([]interface{}, error) { func (*PasswordToken) scanValues(columns []string) ([]any, error) {
values := make([]interface{}, len(columns)) values := make([]any, len(columns))
for i := range columns { for i := range columns {
switch columns[i] { switch columns[i] {
case passwordtoken.FieldID: case passwordtoken.FieldID, passwordtoken.FieldUserID:
values[i] = new(sql.NullInt64) values[i] = new(sql.NullInt64)
case passwordtoken.FieldHash: case passwordtoken.FieldToken:
values[i] = new(sql.NullString) values[i] = new(sql.NullString)
case passwordtoken.FieldCreatedAt: case passwordtoken.FieldCreatedAt:
values[i] = new(sql.NullTime) values[i] = new(sql.NullTime)
case passwordtoken.ForeignKeys[0]: // password_token_user
values[i] = new(sql.NullInt64)
default: default:
return nil, fmt.Errorf("unexpected column %q for type PasswordToken", columns[i]) values[i] = new(sql.UnknownType)
} }
} }
return values, nil return values, nil
@ -72,7 +70,7 @@ func (*PasswordToken) scanValues(columns []string) ([]interface{}, error) {
// assignValues assigns the values that were returned from sql.Rows (after scanning) // assignValues assigns the values that were returned from sql.Rows (after scanning)
// to the PasswordToken fields. // to the PasswordToken fields.
func (pt *PasswordToken) assignValues(columns []string, values []interface{}) error { func (_m *PasswordToken) assignValues(columns []string, values []any) error {
if m, n := len(values), len(columns); m < n { if m, n := len(values), len(columns); m < n {
return fmt.Errorf("mismatch number of scan values: %d != %d", m, n) return fmt.Errorf("mismatch number of scan values: %d != %d", m, n)
} }
@ -83,71 +81,76 @@ func (pt *PasswordToken) assignValues(columns []string, values []interface{}) er
if !ok { if !ok {
return fmt.Errorf("unexpected type %T for field id", value) return fmt.Errorf("unexpected type %T for field id", value)
} }
pt.ID = int(value.Int64) _m.ID = int(value.Int64)
case passwordtoken.FieldHash: case passwordtoken.FieldToken:
if value, ok := values[i].(*sql.NullString); !ok { if value, ok := values[i].(*sql.NullString); !ok {
return fmt.Errorf("unexpected type %T for field hash", values[i]) return fmt.Errorf("unexpected type %T for field token", values[i])
} else if value.Valid { } else if value.Valid {
pt.Hash = value.String _m.Token = value.String
}
case passwordtoken.FieldUserID:
if value, ok := values[i].(*sql.NullInt64); !ok {
return fmt.Errorf("unexpected type %T for field user_id", values[i])
} else if value.Valid {
_m.UserID = int(value.Int64)
} }
case passwordtoken.FieldCreatedAt: case passwordtoken.FieldCreatedAt:
if value, ok := values[i].(*sql.NullTime); !ok { if value, ok := values[i].(*sql.NullTime); !ok {
return fmt.Errorf("unexpected type %T for field created_at", values[i]) return fmt.Errorf("unexpected type %T for field created_at", values[i])
} else if value.Valid { } else if value.Valid {
pt.CreatedAt = value.Time _m.CreatedAt = value.Time
}
case passwordtoken.ForeignKeys[0]:
if value, ok := values[i].(*sql.NullInt64); !ok {
return fmt.Errorf("unexpected type %T for edge-field password_token_user", value)
} else if value.Valid {
pt.password_token_user = new(int)
*pt.password_token_user = int(value.Int64)
} }
default:
_m.selectValues.Set(columns[i], values[i])
} }
} }
return nil return nil
} }
// Value returns the ent.Value that was dynamically selected and assigned to the PasswordToken.
// This includes values selected through modifiers, order, etc.
func (_m *PasswordToken) Value(name string) (ent.Value, error) {
return _m.selectValues.Get(name)
}
// QueryUser queries the "user" edge of the PasswordToken entity. // QueryUser queries the "user" edge of the PasswordToken entity.
func (pt *PasswordToken) QueryUser() *UserQuery { func (_m *PasswordToken) QueryUser() *UserQuery {
return (&PasswordTokenClient{config: pt.config}).QueryUser(pt) return NewPasswordTokenClient(_m.config).QueryUser(_m)
} }
// Update returns a builder for updating this PasswordToken. // Update returns a builder for updating this PasswordToken.
// Note that you need to call PasswordToken.Unwrap() before calling this method if this PasswordToken // Note that you need to call PasswordToken.Unwrap() before calling this method if this PasswordToken
// was returned from a transaction, and the transaction was committed or rolled back. // was returned from a transaction, and the transaction was committed or rolled back.
func (pt *PasswordToken) Update() *PasswordTokenUpdateOne { func (_m *PasswordToken) Update() *PasswordTokenUpdateOne {
return (&PasswordTokenClient{config: pt.config}).UpdateOne(pt) return NewPasswordTokenClient(_m.config).UpdateOne(_m)
} }
// Unwrap unwraps the PasswordToken entity that was returned from a transaction after it was closed, // Unwrap unwraps the PasswordToken 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. // so that all future queries will be executed through the driver which created the transaction.
func (pt *PasswordToken) Unwrap() *PasswordToken { func (_m *PasswordToken) Unwrap() *PasswordToken {
tx, ok := pt.config.driver.(*txDriver) _tx, ok := _m.config.driver.(*txDriver)
if !ok { if !ok {
panic("ent: PasswordToken is not a transactional entity") panic("ent: PasswordToken is not a transactional entity")
} }
pt.config.driver = tx.drv _m.config.driver = _tx.drv
return pt return _m
} }
// String implements the fmt.Stringer. // String implements the fmt.Stringer.
func (pt *PasswordToken) String() string { func (_m *PasswordToken) String() string {
var builder strings.Builder var builder strings.Builder
builder.WriteString("PasswordToken(") builder.WriteString("PasswordToken(")
builder.WriteString(fmt.Sprintf("id=%v", pt.ID)) builder.WriteString(fmt.Sprintf("id=%v, ", _m.ID))
builder.WriteString(", hash=<sensitive>") builder.WriteString("token=<sensitive>")
builder.WriteString(", created_at=") builder.WriteString(", ")
builder.WriteString(pt.CreatedAt.Format(time.ANSIC)) builder.WriteString("user_id=")
builder.WriteString(fmt.Sprintf("%v", _m.UserID))
builder.WriteString(", ")
builder.WriteString("created_at=")
builder.WriteString(_m.CreatedAt.Format(time.ANSIC))
builder.WriteByte(')') builder.WriteByte(')')
return builder.String() return builder.String()
} }
// PasswordTokens is a parsable slice of PasswordToken. // PasswordTokens is a parsable slice of PasswordToken.
type PasswordTokens []*PasswordToken type PasswordTokens []*PasswordToken
func (pt PasswordTokens) config(cfg config) {
for _i := range pt {
pt[_i].config = cfg
}
}

View file

@ -1,9 +1,13 @@
// Code generated by entc, DO NOT EDIT. // Code generated by ent, DO NOT EDIT.
package passwordtoken package passwordtoken
import ( import (
"time" "time"
"entgo.io/ent"
"entgo.io/ent/dialect/sql"
"entgo.io/ent/dialect/sql/sqlgraph"
) )
const ( const (
@ -11,8 +15,10 @@ const (
Label = "password_token" Label = "password_token"
// FieldID holds the string denoting the id field in the database. // FieldID holds the string denoting the id field in the database.
FieldID = "id" FieldID = "id"
// FieldHash holds the string denoting the hash field in the database. // FieldToken holds the string denoting the token field in the database.
FieldHash = "hash" FieldToken = "token"
// FieldUserID holds the string denoting the user_id field in the database.
FieldUserID = "user_id"
// FieldCreatedAt holds the string denoting the created_at field in the database. // FieldCreatedAt holds the string denoting the created_at field in the database.
FieldCreatedAt = "created_at" FieldCreatedAt = "created_at"
// EdgeUser holds the string denoting the user edge name in mutations. // EdgeUser holds the string denoting the user edge name in mutations.
@ -25,22 +31,17 @@ const (
// It exists in this package in order to avoid circular dependency with the "user" package. // It exists in this package in order to avoid circular dependency with the "user" package.
UserInverseTable = "users" UserInverseTable = "users"
// UserColumn is the table column denoting the user relation/edge. // UserColumn is the table column denoting the user relation/edge.
UserColumn = "password_token_user" UserColumn = "user_id"
) )
// Columns holds all SQL columns for passwordtoken fields. // Columns holds all SQL columns for passwordtoken fields.
var Columns = []string{ var Columns = []string{
FieldID, FieldID,
FieldHash, FieldToken,
FieldUserID,
FieldCreatedAt, FieldCreatedAt,
} }
// ForeignKeys holds the SQL foreign-keys that are owned by the "password_tokens"
// table and are not defined as standalone fields in the schema.
var ForeignKeys = []string{
"password_token_user",
}
// ValidColumn reports if the column name is valid (part of the table columns). // ValidColumn reports if the column name is valid (part of the table columns).
func ValidColumn(column string) bool { func ValidColumn(column string) bool {
for i := range Columns { for i := range Columns {
@ -48,17 +49,55 @@ func ValidColumn(column string) bool {
return true return true
} }
} }
for i := range ForeignKeys {
if column == ForeignKeys[i] {
return true
}
}
return false return false
} }
// Note that the variables below are initialized by the runtime
// package on the initialization of the application. Therefore,
// it should be imported in the main as follows:
//
// import _ "github.com/mikestefanello/pagoda/ent/runtime"
var ( var (
// HashValidator is a validator for the "hash" field. It is called by the builders before save. Hooks [1]ent.Hook
HashValidator func(string) error // TokenValidator is a validator for the "token" field. It is called by the builders before save.
TokenValidator func(string) error
// DefaultCreatedAt holds the default value on creation for the "created_at" field. // DefaultCreatedAt holds the default value on creation for the "created_at" field.
DefaultCreatedAt func() time.Time DefaultCreatedAt func() time.Time
) )
// OrderOption defines the ordering options for the PasswordToken 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()
}
// ByToken orders the results by the token field.
func ByToken(opts ...sql.OrderTermOption) OrderOption {
return sql.OrderByField(FieldToken, opts...).ToFunc()
}
// ByUserID orders the results by the user_id field.
func ByUserID(opts ...sql.OrderTermOption) OrderOption {
return sql.OrderByField(FieldUserID, opts...).ToFunc()
}
// ByCreatedAt orders the results by the created_at field.
func ByCreatedAt(opts ...sql.OrderTermOption) OrderOption {
return sql.OrderByField(FieldCreatedAt, opts...).ToFunc()
}
// ByUserField orders the results by user field.
func ByUserField(field string, opts ...sql.OrderTermOption) OrderOption {
return func(s *sql.Selector) {
sqlgraph.OrderByNeighborTerms(s, newUserStep(), sql.OrderByField(field, opts...))
}
}
func newUserStep() *sqlgraph.Step {
return sqlgraph.NewStep(
sqlgraph.From(Table, FieldID),
sqlgraph.To(UserInverseTable, FieldID),
sqlgraph.Edge(sqlgraph.M2O, false, UserTable, UserColumn),
)
}

View file

@ -1,297 +1,198 @@
// Code generated by entc, DO NOT EDIT. // Code generated by ent, DO NOT EDIT.
package passwordtoken package passwordtoken
import ( import (
"goweb/ent/predicate"
"time" "time"
"entgo.io/ent/dialect/sql" "entgo.io/ent/dialect/sql"
"entgo.io/ent/dialect/sql/sqlgraph" "entgo.io/ent/dialect/sql/sqlgraph"
"github.com/mikestefanello/pagoda/ent/predicate"
) )
// ID filters vertices based on their ID field. // ID filters vertices based on their ID field.
func ID(id int) predicate.PasswordToken { func ID(id int) predicate.PasswordToken {
return predicate.PasswordToken(func(s *sql.Selector) { return predicate.PasswordToken(sql.FieldEQ(FieldID, id))
s.Where(sql.EQ(s.C(FieldID), id))
})
} }
// IDEQ applies the EQ predicate on the ID field. // IDEQ applies the EQ predicate on the ID field.
func IDEQ(id int) predicate.PasswordToken { func IDEQ(id int) predicate.PasswordToken {
return predicate.PasswordToken(func(s *sql.Selector) { return predicate.PasswordToken(sql.FieldEQ(FieldID, id))
s.Where(sql.EQ(s.C(FieldID), id))
})
} }
// IDNEQ applies the NEQ predicate on the ID field. // IDNEQ applies the NEQ predicate on the ID field.
func IDNEQ(id int) predicate.PasswordToken { func IDNEQ(id int) predicate.PasswordToken {
return predicate.PasswordToken(func(s *sql.Selector) { return predicate.PasswordToken(sql.FieldNEQ(FieldID, id))
s.Where(sql.NEQ(s.C(FieldID), id))
})
} }
// IDIn applies the In predicate on the ID field. // IDIn applies the In predicate on the ID field.
func IDIn(ids ...int) predicate.PasswordToken { func IDIn(ids ...int) predicate.PasswordToken {
return predicate.PasswordToken(func(s *sql.Selector) { return predicate.PasswordToken(sql.FieldIn(FieldID, ids...))
// if not arguments were provided, append the FALSE constants,
// since we can't apply "IN ()". This will make this predicate falsy.
if len(ids) == 0 {
s.Where(sql.False())
return
}
v := make([]interface{}, len(ids))
for i := range v {
v[i] = ids[i]
}
s.Where(sql.In(s.C(FieldID), v...))
})
} }
// IDNotIn applies the NotIn predicate on the ID field. // IDNotIn applies the NotIn predicate on the ID field.
func IDNotIn(ids ...int) predicate.PasswordToken { func IDNotIn(ids ...int) predicate.PasswordToken {
return predicate.PasswordToken(func(s *sql.Selector) { return predicate.PasswordToken(sql.FieldNotIn(FieldID, ids...))
// if not arguments were provided, append the FALSE constants,
// since we can't apply "IN ()". This will make this predicate falsy.
if len(ids) == 0 {
s.Where(sql.False())
return
}
v := make([]interface{}, len(ids))
for i := range v {
v[i] = ids[i]
}
s.Where(sql.NotIn(s.C(FieldID), v...))
})
} }
// IDGT applies the GT predicate on the ID field. // IDGT applies the GT predicate on the ID field.
func IDGT(id int) predicate.PasswordToken { func IDGT(id int) predicate.PasswordToken {
return predicate.PasswordToken(func(s *sql.Selector) { return predicate.PasswordToken(sql.FieldGT(FieldID, id))
s.Where(sql.GT(s.C(FieldID), id))
})
} }
// IDGTE applies the GTE predicate on the ID field. // IDGTE applies the GTE predicate on the ID field.
func IDGTE(id int) predicate.PasswordToken { func IDGTE(id int) predicate.PasswordToken {
return predicate.PasswordToken(func(s *sql.Selector) { return predicate.PasswordToken(sql.FieldGTE(FieldID, id))
s.Where(sql.GTE(s.C(FieldID), id))
})
} }
// IDLT applies the LT predicate on the ID field. // IDLT applies the LT predicate on the ID field.
func IDLT(id int) predicate.PasswordToken { func IDLT(id int) predicate.PasswordToken {
return predicate.PasswordToken(func(s *sql.Selector) { return predicate.PasswordToken(sql.FieldLT(FieldID, id))
s.Where(sql.LT(s.C(FieldID), id))
})
} }
// IDLTE applies the LTE predicate on the ID field. // IDLTE applies the LTE predicate on the ID field.
func IDLTE(id int) predicate.PasswordToken { func IDLTE(id int) predicate.PasswordToken {
return predicate.PasswordToken(func(s *sql.Selector) { return predicate.PasswordToken(sql.FieldLTE(FieldID, id))
s.Where(sql.LTE(s.C(FieldID), id))
})
} }
// Hash applies equality check predicate on the "hash" field. It's identical to HashEQ. // Token applies equality check predicate on the "token" field. It's identical to TokenEQ.
func Hash(v string) predicate.PasswordToken { func Token(v string) predicate.PasswordToken {
return predicate.PasswordToken(func(s *sql.Selector) { return predicate.PasswordToken(sql.FieldEQ(FieldToken, v))
s.Where(sql.EQ(s.C(FieldHash), v)) }
})
// UserID applies equality check predicate on the "user_id" field. It's identical to UserIDEQ.
func UserID(v int) predicate.PasswordToken {
return predicate.PasswordToken(sql.FieldEQ(FieldUserID, v))
} }
// CreatedAt applies equality check predicate on the "created_at" field. It's identical to CreatedAtEQ. // CreatedAt applies equality check predicate on the "created_at" field. It's identical to CreatedAtEQ.
func CreatedAt(v time.Time) predicate.PasswordToken { func CreatedAt(v time.Time) predicate.PasswordToken {
return predicate.PasswordToken(func(s *sql.Selector) { return predicate.PasswordToken(sql.FieldEQ(FieldCreatedAt, v))
s.Where(sql.EQ(s.C(FieldCreatedAt), v))
})
} }
// HashEQ applies the EQ predicate on the "hash" field. // TokenEQ applies the EQ predicate on the "token" field.
func HashEQ(v string) predicate.PasswordToken { func TokenEQ(v string) predicate.PasswordToken {
return predicate.PasswordToken(func(s *sql.Selector) { return predicate.PasswordToken(sql.FieldEQ(FieldToken, v))
s.Where(sql.EQ(s.C(FieldHash), v))
})
} }
// HashNEQ applies the NEQ predicate on the "hash" field. // TokenNEQ applies the NEQ predicate on the "token" field.
func HashNEQ(v string) predicate.PasswordToken { func TokenNEQ(v string) predicate.PasswordToken {
return predicate.PasswordToken(func(s *sql.Selector) { return predicate.PasswordToken(sql.FieldNEQ(FieldToken, v))
s.Where(sql.NEQ(s.C(FieldHash), v))
})
} }
// HashIn applies the In predicate on the "hash" field. // TokenIn applies the In predicate on the "token" field.
func HashIn(vs ...string) predicate.PasswordToken { func TokenIn(vs ...string) predicate.PasswordToken {
v := make([]interface{}, len(vs)) return predicate.PasswordToken(sql.FieldIn(FieldToken, vs...))
for i := range v {
v[i] = vs[i]
}
return predicate.PasswordToken(func(s *sql.Selector) {
// if not arguments were provided, append the FALSE constants,
// since we can't apply "IN ()". This will make this predicate falsy.
if len(v) == 0 {
s.Where(sql.False())
return
}
s.Where(sql.In(s.C(FieldHash), v...))
})
} }
// HashNotIn applies the NotIn predicate on the "hash" field. // TokenNotIn applies the NotIn predicate on the "token" field.
func HashNotIn(vs ...string) predicate.PasswordToken { func TokenNotIn(vs ...string) predicate.PasswordToken {
v := make([]interface{}, len(vs)) return predicate.PasswordToken(sql.FieldNotIn(FieldToken, vs...))
for i := range v {
v[i] = vs[i]
}
return predicate.PasswordToken(func(s *sql.Selector) {
// if not arguments were provided, append the FALSE constants,
// since we can't apply "IN ()". This will make this predicate falsy.
if len(v) == 0 {
s.Where(sql.False())
return
}
s.Where(sql.NotIn(s.C(FieldHash), v...))
})
} }
// HashGT applies the GT predicate on the "hash" field. // TokenGT applies the GT predicate on the "token" field.
func HashGT(v string) predicate.PasswordToken { func TokenGT(v string) predicate.PasswordToken {
return predicate.PasswordToken(func(s *sql.Selector) { return predicate.PasswordToken(sql.FieldGT(FieldToken, v))
s.Where(sql.GT(s.C(FieldHash), v))
})
} }
// HashGTE applies the GTE predicate on the "hash" field. // TokenGTE applies the GTE predicate on the "token" field.
func HashGTE(v string) predicate.PasswordToken { func TokenGTE(v string) predicate.PasswordToken {
return predicate.PasswordToken(func(s *sql.Selector) { return predicate.PasswordToken(sql.FieldGTE(FieldToken, v))
s.Where(sql.GTE(s.C(FieldHash), v))
})
} }
// HashLT applies the LT predicate on the "hash" field. // TokenLT applies the LT predicate on the "token" field.
func HashLT(v string) predicate.PasswordToken { func TokenLT(v string) predicate.PasswordToken {
return predicate.PasswordToken(func(s *sql.Selector) { return predicate.PasswordToken(sql.FieldLT(FieldToken, v))
s.Where(sql.LT(s.C(FieldHash), v))
})
} }
// HashLTE applies the LTE predicate on the "hash" field. // TokenLTE applies the LTE predicate on the "token" field.
func HashLTE(v string) predicate.PasswordToken { func TokenLTE(v string) predicate.PasswordToken {
return predicate.PasswordToken(func(s *sql.Selector) { return predicate.PasswordToken(sql.FieldLTE(FieldToken, v))
s.Where(sql.LTE(s.C(FieldHash), v))
})
} }
// HashContains applies the Contains predicate on the "hash" field. // TokenContains applies the Contains predicate on the "token" field.
func HashContains(v string) predicate.PasswordToken { func TokenContains(v string) predicate.PasswordToken {
return predicate.PasswordToken(func(s *sql.Selector) { return predicate.PasswordToken(sql.FieldContains(FieldToken, v))
s.Where(sql.Contains(s.C(FieldHash), v))
})
} }
// HashHasPrefix applies the HasPrefix predicate on the "hash" field. // TokenHasPrefix applies the HasPrefix predicate on the "token" field.
func HashHasPrefix(v string) predicate.PasswordToken { func TokenHasPrefix(v string) predicate.PasswordToken {
return predicate.PasswordToken(func(s *sql.Selector) { return predicate.PasswordToken(sql.FieldHasPrefix(FieldToken, v))
s.Where(sql.HasPrefix(s.C(FieldHash), v))
})
} }
// HashHasSuffix applies the HasSuffix predicate on the "hash" field. // TokenHasSuffix applies the HasSuffix predicate on the "token" field.
func HashHasSuffix(v string) predicate.PasswordToken { func TokenHasSuffix(v string) predicate.PasswordToken {
return predicate.PasswordToken(func(s *sql.Selector) { return predicate.PasswordToken(sql.FieldHasSuffix(FieldToken, v))
s.Where(sql.HasSuffix(s.C(FieldHash), v))
})
} }
// HashEqualFold applies the EqualFold predicate on the "hash" field. // TokenEqualFold applies the EqualFold predicate on the "token" field.
func HashEqualFold(v string) predicate.PasswordToken { func TokenEqualFold(v string) predicate.PasswordToken {
return predicate.PasswordToken(func(s *sql.Selector) { return predicate.PasswordToken(sql.FieldEqualFold(FieldToken, v))
s.Where(sql.EqualFold(s.C(FieldHash), v))
})
} }
// HashContainsFold applies the ContainsFold predicate on the "hash" field. // TokenContainsFold applies the ContainsFold predicate on the "token" field.
func HashContainsFold(v string) predicate.PasswordToken { func TokenContainsFold(v string) predicate.PasswordToken {
return predicate.PasswordToken(func(s *sql.Selector) { return predicate.PasswordToken(sql.FieldContainsFold(FieldToken, v))
s.Where(sql.ContainsFold(s.C(FieldHash), v)) }
})
// UserIDEQ applies the EQ predicate on the "user_id" field.
func UserIDEQ(v int) predicate.PasswordToken {
return predicate.PasswordToken(sql.FieldEQ(FieldUserID, v))
}
// UserIDNEQ applies the NEQ predicate on the "user_id" field.
func UserIDNEQ(v int) predicate.PasswordToken {
return predicate.PasswordToken(sql.FieldNEQ(FieldUserID, v))
}
// UserIDIn applies the In predicate on the "user_id" field.
func UserIDIn(vs ...int) predicate.PasswordToken {
return predicate.PasswordToken(sql.FieldIn(FieldUserID, vs...))
}
// UserIDNotIn applies the NotIn predicate on the "user_id" field.
func UserIDNotIn(vs ...int) predicate.PasswordToken {
return predicate.PasswordToken(sql.FieldNotIn(FieldUserID, vs...))
} }
// CreatedAtEQ applies the EQ predicate on the "created_at" field. // CreatedAtEQ applies the EQ predicate on the "created_at" field.
func CreatedAtEQ(v time.Time) predicate.PasswordToken { func CreatedAtEQ(v time.Time) predicate.PasswordToken {
return predicate.PasswordToken(func(s *sql.Selector) { return predicate.PasswordToken(sql.FieldEQ(FieldCreatedAt, v))
s.Where(sql.EQ(s.C(FieldCreatedAt), v))
})
} }
// CreatedAtNEQ applies the NEQ predicate on the "created_at" field. // CreatedAtNEQ applies the NEQ predicate on the "created_at" field.
func CreatedAtNEQ(v time.Time) predicate.PasswordToken { func CreatedAtNEQ(v time.Time) predicate.PasswordToken {
return predicate.PasswordToken(func(s *sql.Selector) { return predicate.PasswordToken(sql.FieldNEQ(FieldCreatedAt, v))
s.Where(sql.NEQ(s.C(FieldCreatedAt), v))
})
} }
// CreatedAtIn applies the In predicate on the "created_at" field. // CreatedAtIn applies the In predicate on the "created_at" field.
func CreatedAtIn(vs ...time.Time) predicate.PasswordToken { func CreatedAtIn(vs ...time.Time) predicate.PasswordToken {
v := make([]interface{}, len(vs)) return predicate.PasswordToken(sql.FieldIn(FieldCreatedAt, vs...))
for i := range v {
v[i] = vs[i]
}
return predicate.PasswordToken(func(s *sql.Selector) {
// if not arguments were provided, append the FALSE constants,
// since we can't apply "IN ()". This will make this predicate falsy.
if len(v) == 0 {
s.Where(sql.False())
return
}
s.Where(sql.In(s.C(FieldCreatedAt), v...))
})
} }
// CreatedAtNotIn applies the NotIn predicate on the "created_at" field. // CreatedAtNotIn applies the NotIn predicate on the "created_at" field.
func CreatedAtNotIn(vs ...time.Time) predicate.PasswordToken { func CreatedAtNotIn(vs ...time.Time) predicate.PasswordToken {
v := make([]interface{}, len(vs)) return predicate.PasswordToken(sql.FieldNotIn(FieldCreatedAt, vs...))
for i := range v {
v[i] = vs[i]
}
return predicate.PasswordToken(func(s *sql.Selector) {
// if not arguments were provided, append the FALSE constants,
// since we can't apply "IN ()". This will make this predicate falsy.
if len(v) == 0 {
s.Where(sql.False())
return
}
s.Where(sql.NotIn(s.C(FieldCreatedAt), v...))
})
} }
// CreatedAtGT applies the GT predicate on the "created_at" field. // CreatedAtGT applies the GT predicate on the "created_at" field.
func CreatedAtGT(v time.Time) predicate.PasswordToken { func CreatedAtGT(v time.Time) predicate.PasswordToken {
return predicate.PasswordToken(func(s *sql.Selector) { return predicate.PasswordToken(sql.FieldGT(FieldCreatedAt, v))
s.Where(sql.GT(s.C(FieldCreatedAt), v))
})
} }
// CreatedAtGTE applies the GTE predicate on the "created_at" field. // CreatedAtGTE applies the GTE predicate on the "created_at" field.
func CreatedAtGTE(v time.Time) predicate.PasswordToken { func CreatedAtGTE(v time.Time) predicate.PasswordToken {
return predicate.PasswordToken(func(s *sql.Selector) { return predicate.PasswordToken(sql.FieldGTE(FieldCreatedAt, v))
s.Where(sql.GTE(s.C(FieldCreatedAt), v))
})
} }
// CreatedAtLT applies the LT predicate on the "created_at" field. // CreatedAtLT applies the LT predicate on the "created_at" field.
func CreatedAtLT(v time.Time) predicate.PasswordToken { func CreatedAtLT(v time.Time) predicate.PasswordToken {
return predicate.PasswordToken(func(s *sql.Selector) { return predicate.PasswordToken(sql.FieldLT(FieldCreatedAt, v))
s.Where(sql.LT(s.C(FieldCreatedAt), v))
})
} }
// CreatedAtLTE applies the LTE predicate on the "created_at" field. // CreatedAtLTE applies the LTE predicate on the "created_at" field.
func CreatedAtLTE(v time.Time) predicate.PasswordToken { func CreatedAtLTE(v time.Time) predicate.PasswordToken {
return predicate.PasswordToken(func(s *sql.Selector) { return predicate.PasswordToken(sql.FieldLTE(FieldCreatedAt, v))
s.Where(sql.LTE(s.C(FieldCreatedAt), v))
})
} }
// HasUser applies the HasEdge predicate on the "user" edge. // HasUser applies the HasEdge predicate on the "user" edge.
@ -299,7 +200,6 @@ func HasUser() predicate.PasswordToken {
return predicate.PasswordToken(func(s *sql.Selector) { return predicate.PasswordToken(func(s *sql.Selector) {
step := sqlgraph.NewStep( step := sqlgraph.NewStep(
sqlgraph.From(Table, FieldID), sqlgraph.From(Table, FieldID),
sqlgraph.To(UserTable, FieldID),
sqlgraph.Edge(sqlgraph.M2O, false, UserTable, UserColumn), sqlgraph.Edge(sqlgraph.M2O, false, UserTable, UserColumn),
) )
sqlgraph.HasNeighbors(s, step) sqlgraph.HasNeighbors(s, step)
@ -309,11 +209,7 @@ func HasUser() predicate.PasswordToken {
// HasUserWith applies the HasEdge predicate on the "user" edge with a given conditions (other predicates). // HasUserWith applies the HasEdge predicate on the "user" edge with a given conditions (other predicates).
func HasUserWith(preds ...predicate.User) predicate.PasswordToken { func HasUserWith(preds ...predicate.User) predicate.PasswordToken {
return predicate.PasswordToken(func(s *sql.Selector) { return predicate.PasswordToken(func(s *sql.Selector) {
step := sqlgraph.NewStep( step := newUserStep()
sqlgraph.From(Table, FieldID),
sqlgraph.To(UserInverseTable, FieldID),
sqlgraph.Edge(sqlgraph.M2O, false, UserTable, UserColumn),
)
sqlgraph.HasNeighborsWith(s, step, func(s *sql.Selector) { sqlgraph.HasNeighborsWith(s, step, func(s *sql.Selector) {
for _, p := range preds { for _, p := range preds {
p(s) p(s)
@ -324,32 +220,15 @@ func HasUserWith(preds ...predicate.User) predicate.PasswordToken {
// And groups predicates with the AND operator between them. // And groups predicates with the AND operator between them.
func And(predicates ...predicate.PasswordToken) predicate.PasswordToken { func And(predicates ...predicate.PasswordToken) predicate.PasswordToken {
return predicate.PasswordToken(func(s *sql.Selector) { return predicate.PasswordToken(sql.AndPredicates(predicates...))
s1 := s.Clone().SetP(nil)
for _, p := range predicates {
p(s1)
}
s.Where(s1.P())
})
} }
// Or groups predicates with the OR operator between them. // Or groups predicates with the OR operator between them.
func Or(predicates ...predicate.PasswordToken) predicate.PasswordToken { func Or(predicates ...predicate.PasswordToken) predicate.PasswordToken {
return predicate.PasswordToken(func(s *sql.Selector) { return predicate.PasswordToken(sql.OrPredicates(predicates...))
s1 := s.Clone().SetP(nil)
for i, p := range predicates {
if i > 0 {
s1.Or()
}
p(s1)
}
s.Where(s1.P())
})
} }
// Not applies the not operator on the given predicate. // Not applies the not operator on the given predicate.
func Not(p predicate.PasswordToken) predicate.PasswordToken { func Not(p predicate.PasswordToken) predicate.PasswordToken {
return predicate.PasswordToken(func(s *sql.Selector) { return predicate.PasswordToken(sql.NotPredicates(p))
p(s.Not())
})
} }

View file

@ -1,4 +1,4 @@
// Code generated by entc, DO NOT EDIT. // Code generated by ent, DO NOT EDIT.
package ent package ent
@ -6,12 +6,12 @@ import (
"context" "context"
"errors" "errors"
"fmt" "fmt"
"goweb/ent/passwordtoken"
"goweb/ent/user"
"time" "time"
"entgo.io/ent/dialect/sql/sqlgraph" "entgo.io/ent/dialect/sql/sqlgraph"
"entgo.io/ent/schema/field" "entgo.io/ent/schema/field"
"github.com/mikestefanello/pagoda/ent/passwordtoken"
"github.com/mikestefanello/pagoda/ent/user"
) )
// PasswordTokenCreate is the builder for creating a PasswordToken entity. // PasswordTokenCreate is the builder for creating a PasswordToken entity.
@ -21,87 +21,53 @@ type PasswordTokenCreate struct {
hooks []Hook hooks []Hook
} }
// SetHash sets the "hash" field. // SetToken sets the "token" field.
func (ptc *PasswordTokenCreate) SetHash(s string) *PasswordTokenCreate { func (_c *PasswordTokenCreate) SetToken(v string) *PasswordTokenCreate {
ptc.mutation.SetHash(s) _c.mutation.SetToken(v)
return ptc return _c
}
// SetUserID sets the "user_id" field.
func (_c *PasswordTokenCreate) SetUserID(v int) *PasswordTokenCreate {
_c.mutation.SetUserID(v)
return _c
} }
// SetCreatedAt sets the "created_at" field. // SetCreatedAt sets the "created_at" field.
func (ptc *PasswordTokenCreate) SetCreatedAt(t time.Time) *PasswordTokenCreate { func (_c *PasswordTokenCreate) SetCreatedAt(v time.Time) *PasswordTokenCreate {
ptc.mutation.SetCreatedAt(t) _c.mutation.SetCreatedAt(v)
return ptc return _c
} }
// SetNillableCreatedAt sets the "created_at" field if the given value is not nil. // SetNillableCreatedAt sets the "created_at" field if the given value is not nil.
func (ptc *PasswordTokenCreate) SetNillableCreatedAt(t *time.Time) *PasswordTokenCreate { func (_c *PasswordTokenCreate) SetNillableCreatedAt(v *time.Time) *PasswordTokenCreate {
if t != nil { if v != nil {
ptc.SetCreatedAt(*t) _c.SetCreatedAt(*v)
} }
return ptc return _c
}
// SetUserID sets the "user" edge to the User entity by ID.
func (ptc *PasswordTokenCreate) SetUserID(id int) *PasswordTokenCreate {
ptc.mutation.SetUserID(id)
return ptc
} }
// SetUser sets the "user" edge to the User entity. // SetUser sets the "user" edge to the User entity.
func (ptc *PasswordTokenCreate) SetUser(u *User) *PasswordTokenCreate { func (_c *PasswordTokenCreate) SetUser(v *User) *PasswordTokenCreate {
return ptc.SetUserID(u.ID) return _c.SetUserID(v.ID)
} }
// Mutation returns the PasswordTokenMutation object of the builder. // Mutation returns the PasswordTokenMutation object of the builder.
func (ptc *PasswordTokenCreate) Mutation() *PasswordTokenMutation { func (_c *PasswordTokenCreate) Mutation() *PasswordTokenMutation {
return ptc.mutation return _c.mutation
} }
// Save creates the PasswordToken in the database. // Save creates the PasswordToken in the database.
func (ptc *PasswordTokenCreate) Save(ctx context.Context) (*PasswordToken, error) { func (_c *PasswordTokenCreate) Save(ctx context.Context) (*PasswordToken, error) {
var ( if err := _c.defaults(); err != nil {
err error return nil, err
node *PasswordToken
)
ptc.defaults()
if len(ptc.hooks) == 0 {
if err = ptc.check(); err != nil {
return nil, err
}
node, err = ptc.sqlSave(ctx)
} else {
var mut Mutator = MutateFunc(func(ctx context.Context, m Mutation) (Value, error) {
mutation, ok := m.(*PasswordTokenMutation)
if !ok {
return nil, fmt.Errorf("unexpected mutation type %T", m)
}
if err = ptc.check(); err != nil {
return nil, err
}
ptc.mutation = mutation
if node, err = ptc.sqlSave(ctx); err != nil {
return nil, err
}
mutation.id = &node.ID
mutation.done = true
return node, err
})
for i := len(ptc.hooks) - 1; i >= 0; i-- {
if ptc.hooks[i] == nil {
return nil, fmt.Errorf("ent: uninitialized hook (forgotten import ent/runtime?)")
}
mut = ptc.hooks[i](mut)
}
if _, err := mut.Mutate(ctx, ptc.mutation); err != nil {
return nil, err
}
} }
return node, err return withHooks(ctx, _c.sqlSave, _c.mutation, _c.hooks)
} }
// SaveX calls Save and panics if Save returns an error. // SaveX calls Save and panics if Save returns an error.
func (ptc *PasswordTokenCreate) SaveX(ctx context.Context) *PasswordToken { func (_c *PasswordTokenCreate) SaveX(ctx context.Context) *PasswordToken {
v, err := ptc.Save(ctx) v, err := _c.Save(ctx)
if err != nil { if err != nil {
panic(err) panic(err)
} }
@ -109,86 +75,84 @@ func (ptc *PasswordTokenCreate) SaveX(ctx context.Context) *PasswordToken {
} }
// Exec executes the query. // Exec executes the query.
func (ptc *PasswordTokenCreate) Exec(ctx context.Context) error { func (_c *PasswordTokenCreate) Exec(ctx context.Context) error {
_, err := ptc.Save(ctx) _, err := _c.Save(ctx)
return err return err
} }
// ExecX is like Exec, but panics if an error occurs. // ExecX is like Exec, but panics if an error occurs.
func (ptc *PasswordTokenCreate) ExecX(ctx context.Context) { func (_c *PasswordTokenCreate) ExecX(ctx context.Context) {
if err := ptc.Exec(ctx); err != nil { if err := _c.Exec(ctx); err != nil {
panic(err) panic(err)
} }
} }
// defaults sets the default values of the builder before save. // defaults sets the default values of the builder before save.
func (ptc *PasswordTokenCreate) defaults() { func (_c *PasswordTokenCreate) defaults() error {
if _, ok := ptc.mutation.CreatedAt(); !ok { if _, ok := _c.mutation.CreatedAt(); !ok {
v := passwordtoken.DefaultCreatedAt() if passwordtoken.DefaultCreatedAt == nil {
ptc.mutation.SetCreatedAt(v) return fmt.Errorf("ent: uninitialized passwordtoken.DefaultCreatedAt (forgotten import ent/runtime?)")
}
}
// check runs all checks and user-defined validators on the builder.
func (ptc *PasswordTokenCreate) check() error {
if _, ok := ptc.mutation.Hash(); !ok {
return &ValidationError{Name: "hash", err: errors.New(`ent: missing required field "hash"`)}
}
if v, ok := ptc.mutation.Hash(); ok {
if err := passwordtoken.HashValidator(v); err != nil {
return &ValidationError{Name: "hash", err: fmt.Errorf(`ent: validator failed for field "hash": %w`, err)}
} }
} v := passwordtoken.DefaultCreatedAt()
if _, ok := ptc.mutation.CreatedAt(); !ok { _c.mutation.SetCreatedAt(v)
return &ValidationError{Name: "created_at", err: errors.New(`ent: missing required field "created_at"`)}
}
if _, ok := ptc.mutation.UserID(); !ok {
return &ValidationError{Name: "user", err: errors.New("ent: missing required edge \"user\"")}
} }
return nil return nil
} }
func (ptc *PasswordTokenCreate) sqlSave(ctx context.Context) (*PasswordToken, error) { // check runs all checks and user-defined validators on the builder.
_node, _spec := ptc.createSpec() func (_c *PasswordTokenCreate) check() error {
if err := sqlgraph.CreateNode(ctx, ptc.driver, _spec); err != nil { if _, ok := _c.mutation.Token(); !ok {
return &ValidationError{Name: "token", err: errors.New(`ent: missing required field "PasswordToken.token"`)}
}
if v, ok := _c.mutation.Token(); ok {
if err := passwordtoken.TokenValidator(v); err != nil {
return &ValidationError{Name: "token", err: fmt.Errorf(`ent: validator failed for field "PasswordToken.token": %w`, err)}
}
}
if _, ok := _c.mutation.UserID(); !ok {
return &ValidationError{Name: "user_id", err: errors.New(`ent: missing required field "PasswordToken.user_id"`)}
}
if _, ok := _c.mutation.CreatedAt(); !ok {
return &ValidationError{Name: "created_at", err: errors.New(`ent: missing required field "PasswordToken.created_at"`)}
}
if len(_c.mutation.UserIDs()) == 0 {
return &ValidationError{Name: "user", err: errors.New(`ent: missing required edge "PasswordToken.user"`)}
}
return nil
}
func (_c *PasswordTokenCreate) sqlSave(ctx context.Context) (*PasswordToken, 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) { if sqlgraph.IsConstraintError(err) {
err = &ConstraintError{err.Error(), err} err = &ConstraintError{msg: err.Error(), wrap: err}
} }
return nil, err return nil, err
} }
id := _spec.ID.Value.(int64) id := _spec.ID.Value.(int64)
_node.ID = int(id) _node.ID = int(id)
_c.mutation.id = &_node.ID
_c.mutation.done = true
return _node, nil return _node, nil
} }
func (ptc *PasswordTokenCreate) createSpec() (*PasswordToken, *sqlgraph.CreateSpec) { func (_c *PasswordTokenCreate) createSpec() (*PasswordToken, *sqlgraph.CreateSpec) {
var ( var (
_node = &PasswordToken{config: ptc.config} _node = &PasswordToken{config: _c.config}
_spec = &sqlgraph.CreateSpec{ _spec = sqlgraph.NewCreateSpec(passwordtoken.Table, sqlgraph.NewFieldSpec(passwordtoken.FieldID, field.TypeInt))
Table: passwordtoken.Table,
ID: &sqlgraph.FieldSpec{
Type: field.TypeInt,
Column: passwordtoken.FieldID,
},
}
) )
if value, ok := ptc.mutation.Hash(); ok { if value, ok := _c.mutation.Token(); ok {
_spec.Fields = append(_spec.Fields, &sqlgraph.FieldSpec{ _spec.SetField(passwordtoken.FieldToken, field.TypeString, value)
Type: field.TypeString, _node.Token = value
Value: value,
Column: passwordtoken.FieldHash,
})
_node.Hash = value
} }
if value, ok := ptc.mutation.CreatedAt(); ok { if value, ok := _c.mutation.CreatedAt(); ok {
_spec.Fields = append(_spec.Fields, &sqlgraph.FieldSpec{ _spec.SetField(passwordtoken.FieldCreatedAt, field.TypeTime, value)
Type: field.TypeTime,
Value: value,
Column: passwordtoken.FieldCreatedAt,
})
_node.CreatedAt = value _node.CreatedAt = value
} }
if nodes := ptc.mutation.UserIDs(); len(nodes) > 0 { if nodes := _c.mutation.UserIDs(); len(nodes) > 0 {
edge := &sqlgraph.EdgeSpec{ edge := &sqlgraph.EdgeSpec{
Rel: sqlgraph.M2O, Rel: sqlgraph.M2O,
Inverse: false, Inverse: false,
@ -196,16 +160,13 @@ func (ptc *PasswordTokenCreate) createSpec() (*PasswordToken, *sqlgraph.CreateSp
Columns: []string{passwordtoken.UserColumn}, Columns: []string{passwordtoken.UserColumn},
Bidi: false, Bidi: false,
Target: &sqlgraph.EdgeTarget{ Target: &sqlgraph.EdgeTarget{
IDSpec: &sqlgraph.FieldSpec{ IDSpec: sqlgraph.NewFieldSpec(user.FieldID, field.TypeInt),
Type: field.TypeInt,
Column: user.FieldID,
},
}, },
} }
for _, k := range nodes { for _, k := range nodes {
edge.Target.Nodes = append(edge.Target.Nodes, k) edge.Target.Nodes = append(edge.Target.Nodes, k)
} }
_node.password_token_user = &nodes[0] _node.UserID = nodes[0]
_spec.Edges = append(_spec.Edges, edge) _spec.Edges = append(_spec.Edges, edge)
} }
return _node, _spec return _node, _spec
@ -214,17 +175,21 @@ func (ptc *PasswordTokenCreate) createSpec() (*PasswordToken, *sqlgraph.CreateSp
// PasswordTokenCreateBulk is the builder for creating many PasswordToken entities in bulk. // PasswordTokenCreateBulk is the builder for creating many PasswordToken entities in bulk.
type PasswordTokenCreateBulk struct { type PasswordTokenCreateBulk struct {
config config
err error
builders []*PasswordTokenCreate builders []*PasswordTokenCreate
} }
// Save creates the PasswordToken entities in the database. // Save creates the PasswordToken entities in the database.
func (ptcb *PasswordTokenCreateBulk) Save(ctx context.Context) ([]*PasswordToken, error) { func (_c *PasswordTokenCreateBulk) Save(ctx context.Context) ([]*PasswordToken, error) {
specs := make([]*sqlgraph.CreateSpec, len(ptcb.builders)) if _c.err != nil {
nodes := make([]*PasswordToken, len(ptcb.builders)) return nil, _c.err
mutators := make([]Mutator, len(ptcb.builders)) }
for i := range ptcb.builders { specs := make([]*sqlgraph.CreateSpec, len(_c.builders))
nodes := make([]*PasswordToken, len(_c.builders))
mutators := make([]Mutator, len(_c.builders))
for i := range _c.builders {
func(i int, root context.Context) { func(i int, root context.Context) {
builder := ptcb.builders[i] builder := _c.builders[i]
builder.defaults() builder.defaults()
var mut Mutator = MutateFunc(func(ctx context.Context, m Mutation) (Value, error) { var mut Mutator = MutateFunc(func(ctx context.Context, m Mutation) (Value, error) {
mutation, ok := m.(*PasswordTokenMutation) mutation, ok := m.(*PasswordTokenMutation)
@ -235,16 +200,16 @@ func (ptcb *PasswordTokenCreateBulk) Save(ctx context.Context) ([]*PasswordToken
return nil, err return nil, err
} }
builder.mutation = mutation builder.mutation = mutation
nodes[i], specs[i] = builder.createSpec()
var err error var err error
nodes[i], specs[i] = builder.createSpec()
if i < len(mutators)-1 { if i < len(mutators)-1 {
_, err = mutators[i+1].Mutate(root, ptcb.builders[i+1].mutation) _, err = mutators[i+1].Mutate(root, _c.builders[i+1].mutation)
} else { } else {
spec := &sqlgraph.BatchCreateSpec{Nodes: specs} spec := &sqlgraph.BatchCreateSpec{Nodes: specs}
// Invoke the actual operation on the latest mutation in the chain. // Invoke the actual operation on the latest mutation in the chain.
if err = sqlgraph.BatchCreate(ctx, ptcb.driver, spec); err != nil { if err = sqlgraph.BatchCreate(ctx, _c.driver, spec); err != nil {
if sqlgraph.IsConstraintError(err) { if sqlgraph.IsConstraintError(err) {
err = &ConstraintError{err.Error(), err} err = &ConstraintError{msg: err.Error(), wrap: err}
} }
} }
} }
@ -252,11 +217,11 @@ func (ptcb *PasswordTokenCreateBulk) Save(ctx context.Context) ([]*PasswordToken
return nil, err return nil, err
} }
mutation.id = &nodes[i].ID mutation.id = &nodes[i].ID
mutation.done = true
if specs[i].ID.Value != nil { if specs[i].ID.Value != nil {
id := specs[i].ID.Value.(int64) id := specs[i].ID.Value.(int64)
nodes[i].ID = int(id) nodes[i].ID = int(id)
} }
mutation.done = true
return nodes[i], nil return nodes[i], nil
}) })
for i := len(builder.hooks) - 1; i >= 0; i-- { for i := len(builder.hooks) - 1; i >= 0; i-- {
@ -266,7 +231,7 @@ func (ptcb *PasswordTokenCreateBulk) Save(ctx context.Context) ([]*PasswordToken
}(i, ctx) }(i, ctx)
} }
if len(mutators) > 0 { if len(mutators) > 0 {
if _, err := mutators[0].Mutate(ctx, ptcb.builders[0].mutation); err != nil { if _, err := mutators[0].Mutate(ctx, _c.builders[0].mutation); err != nil {
return nil, err return nil, err
} }
} }
@ -274,8 +239,8 @@ func (ptcb *PasswordTokenCreateBulk) Save(ctx context.Context) ([]*PasswordToken
} }
// SaveX is like Save, but panics if an error occurs. // SaveX is like Save, but panics if an error occurs.
func (ptcb *PasswordTokenCreateBulk) SaveX(ctx context.Context) []*PasswordToken { func (_c *PasswordTokenCreateBulk) SaveX(ctx context.Context) []*PasswordToken {
v, err := ptcb.Save(ctx) v, err := _c.Save(ctx)
if err != nil { if err != nil {
panic(err) panic(err)
} }
@ -283,14 +248,14 @@ func (ptcb *PasswordTokenCreateBulk) SaveX(ctx context.Context) []*PasswordToken
} }
// Exec executes the query. // Exec executes the query.
func (ptcb *PasswordTokenCreateBulk) Exec(ctx context.Context) error { func (_c *PasswordTokenCreateBulk) Exec(ctx context.Context) error {
_, err := ptcb.Save(ctx) _, err := _c.Save(ctx)
return err return err
} }
// ExecX is like Exec, but panics if an error occurs. // ExecX is like Exec, but panics if an error occurs.
func (ptcb *PasswordTokenCreateBulk) ExecX(ctx context.Context) { func (_c *PasswordTokenCreateBulk) ExecX(ctx context.Context) {
if err := ptcb.Exec(ctx); err != nil { if err := _c.Exec(ctx); err != nil {
panic(err) panic(err)
} }
} }

View file

@ -1,16 +1,15 @@
// Code generated by entc, DO NOT EDIT. // Code generated by ent, DO NOT EDIT.
package ent package ent
import ( import (
"context" "context"
"fmt"
"goweb/ent/passwordtoken"
"goweb/ent/predicate"
"entgo.io/ent/dialect/sql" "entgo.io/ent/dialect/sql"
"entgo.io/ent/dialect/sql/sqlgraph" "entgo.io/ent/dialect/sql/sqlgraph"
"entgo.io/ent/schema/field" "entgo.io/ent/schema/field"
"github.com/mikestefanello/pagoda/ent/passwordtoken"
"github.com/mikestefanello/pagoda/ent/predicate"
) )
// PasswordTokenDelete is the builder for deleting a PasswordToken entity. // PasswordTokenDelete is the builder for deleting a PasswordToken entity.
@ -21,80 +20,56 @@ type PasswordTokenDelete struct {
} }
// Where appends a list predicates to the PasswordTokenDelete builder. // Where appends a list predicates to the PasswordTokenDelete builder.
func (ptd *PasswordTokenDelete) Where(ps ...predicate.PasswordToken) *PasswordTokenDelete { func (_d *PasswordTokenDelete) Where(ps ...predicate.PasswordToken) *PasswordTokenDelete {
ptd.mutation.Where(ps...) _d.mutation.Where(ps...)
return ptd return _d
} }
// Exec executes the deletion query and returns how many vertices were deleted. // Exec executes the deletion query and returns how many vertices were deleted.
func (ptd *PasswordTokenDelete) Exec(ctx context.Context) (int, error) { func (_d *PasswordTokenDelete) Exec(ctx context.Context) (int, error) {
var ( return withHooks(ctx, _d.sqlExec, _d.mutation, _d.hooks)
err error
affected int
)
if len(ptd.hooks) == 0 {
affected, err = ptd.sqlExec(ctx)
} else {
var mut Mutator = MutateFunc(func(ctx context.Context, m Mutation) (Value, error) {
mutation, ok := m.(*PasswordTokenMutation)
if !ok {
return nil, fmt.Errorf("unexpected mutation type %T", m)
}
ptd.mutation = mutation
affected, err = ptd.sqlExec(ctx)
mutation.done = true
return affected, err
})
for i := len(ptd.hooks) - 1; i >= 0; i-- {
if ptd.hooks[i] == nil {
return 0, fmt.Errorf("ent: uninitialized hook (forgotten import ent/runtime?)")
}
mut = ptd.hooks[i](mut)
}
if _, err := mut.Mutate(ctx, ptd.mutation); err != nil {
return 0, err
}
}
return affected, err
} }
// ExecX is like Exec, but panics if an error occurs. // ExecX is like Exec, but panics if an error occurs.
func (ptd *PasswordTokenDelete) ExecX(ctx context.Context) int { func (_d *PasswordTokenDelete) ExecX(ctx context.Context) int {
n, err := ptd.Exec(ctx) n, err := _d.Exec(ctx)
if err != nil { if err != nil {
panic(err) panic(err)
} }
return n return n
} }
func (ptd *PasswordTokenDelete) sqlExec(ctx context.Context) (int, error) { func (_d *PasswordTokenDelete) sqlExec(ctx context.Context) (int, error) {
_spec := &sqlgraph.DeleteSpec{ _spec := sqlgraph.NewDeleteSpec(passwordtoken.Table, sqlgraph.NewFieldSpec(passwordtoken.FieldID, field.TypeInt))
Node: &sqlgraph.NodeSpec{ if ps := _d.mutation.predicates; len(ps) > 0 {
Table: passwordtoken.Table,
ID: &sqlgraph.FieldSpec{
Type: field.TypeInt,
Column: passwordtoken.FieldID,
},
},
}
if ps := ptd.mutation.predicates; len(ps) > 0 {
_spec.Predicate = func(selector *sql.Selector) { _spec.Predicate = func(selector *sql.Selector) {
for i := range ps { for i := range ps {
ps[i](selector) ps[i](selector)
} }
} }
} }
return sqlgraph.DeleteNodes(ctx, ptd.driver, _spec) 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
} }
// PasswordTokenDeleteOne is the builder for deleting a single PasswordToken entity. // PasswordTokenDeleteOne is the builder for deleting a single PasswordToken entity.
type PasswordTokenDeleteOne struct { type PasswordTokenDeleteOne struct {
ptd *PasswordTokenDelete _d *PasswordTokenDelete
}
// Where appends a list predicates to the PasswordTokenDelete builder.
func (_d *PasswordTokenDeleteOne) Where(ps ...predicate.PasswordToken) *PasswordTokenDeleteOne {
_d._d.mutation.Where(ps...)
return _d
} }
// Exec executes the deletion query. // Exec executes the deletion query.
func (ptdo *PasswordTokenDeleteOne) Exec(ctx context.Context) error { func (_d *PasswordTokenDeleteOne) Exec(ctx context.Context) error {
n, err := ptdo.ptd.Exec(ctx) n, err := _d._d.Exec(ctx)
switch { switch {
case err != nil: case err != nil:
return err return err
@ -106,6 +81,8 @@ func (ptdo *PasswordTokenDeleteOne) Exec(ctx context.Context) error {
} }
// ExecX is like Exec, but panics if an error occurs. // ExecX is like Exec, but panics if an error occurs.
func (ptdo *PasswordTokenDeleteOne) ExecX(ctx context.Context) { func (_d *PasswordTokenDeleteOne) ExecX(ctx context.Context) {
ptdo.ptd.ExecX(ctx) if err := _d.Exec(ctx); err != nil {
panic(err)
}
} }

File diff suppressed because it is too large Load diff

View file

@ -1,4 +1,4 @@
// Code generated by entc, DO NOT EDIT. // Code generated by ent, DO NOT EDIT.
package ent package ent
@ -6,14 +6,14 @@ import (
"context" "context"
"errors" "errors"
"fmt" "fmt"
"goweb/ent/passwordtoken"
"goweb/ent/predicate"
"goweb/ent/user"
"time" "time"
"entgo.io/ent/dialect/sql" "entgo.io/ent/dialect/sql"
"entgo.io/ent/dialect/sql/sqlgraph" "entgo.io/ent/dialect/sql/sqlgraph"
"entgo.io/ent/schema/field" "entgo.io/ent/schema/field"
"github.com/mikestefanello/pagoda/ent/passwordtoken"
"github.com/mikestefanello/pagoda/ent/predicate"
"github.com/mikestefanello/pagoda/ent/user"
) )
// PasswordTokenUpdate is the builder for updating PasswordToken entities. // PasswordTokenUpdate is the builder for updating PasswordToken entities.
@ -24,94 +24,77 @@ type PasswordTokenUpdate struct {
} }
// Where appends a list predicates to the PasswordTokenUpdate builder. // Where appends a list predicates to the PasswordTokenUpdate builder.
func (ptu *PasswordTokenUpdate) Where(ps ...predicate.PasswordToken) *PasswordTokenUpdate { func (_u *PasswordTokenUpdate) Where(ps ...predicate.PasswordToken) *PasswordTokenUpdate {
ptu.mutation.Where(ps...) _u.mutation.Where(ps...)
return ptu return _u
} }
// SetHash sets the "hash" field. // SetToken sets the "token" field.
func (ptu *PasswordTokenUpdate) SetHash(s string) *PasswordTokenUpdate { func (_u *PasswordTokenUpdate) SetToken(v string) *PasswordTokenUpdate {
ptu.mutation.SetHash(s) _u.mutation.SetToken(v)
return ptu return _u
}
// SetNillableToken sets the "token" field if the given value is not nil.
func (_u *PasswordTokenUpdate) SetNillableToken(v *string) *PasswordTokenUpdate {
if v != nil {
_u.SetToken(*v)
}
return _u
}
// SetUserID sets the "user_id" field.
func (_u *PasswordTokenUpdate) SetUserID(v int) *PasswordTokenUpdate {
_u.mutation.SetUserID(v)
return _u
}
// SetNillableUserID sets the "user_id" field if the given value is not nil.
func (_u *PasswordTokenUpdate) SetNillableUserID(v *int) *PasswordTokenUpdate {
if v != nil {
_u.SetUserID(*v)
}
return _u
} }
// SetCreatedAt sets the "created_at" field. // SetCreatedAt sets the "created_at" field.
func (ptu *PasswordTokenUpdate) SetCreatedAt(t time.Time) *PasswordTokenUpdate { func (_u *PasswordTokenUpdate) SetCreatedAt(v time.Time) *PasswordTokenUpdate {
ptu.mutation.SetCreatedAt(t) _u.mutation.SetCreatedAt(v)
return ptu return _u
} }
// SetNillableCreatedAt sets the "created_at" field if the given value is not nil. // SetNillableCreatedAt sets the "created_at" field if the given value is not nil.
func (ptu *PasswordTokenUpdate) SetNillableCreatedAt(t *time.Time) *PasswordTokenUpdate { func (_u *PasswordTokenUpdate) SetNillableCreatedAt(v *time.Time) *PasswordTokenUpdate {
if t != nil { if v != nil {
ptu.SetCreatedAt(*t) _u.SetCreatedAt(*v)
} }
return ptu return _u
}
// SetUserID sets the "user" edge to the User entity by ID.
func (ptu *PasswordTokenUpdate) SetUserID(id int) *PasswordTokenUpdate {
ptu.mutation.SetUserID(id)
return ptu
} }
// SetUser sets the "user" edge to the User entity. // SetUser sets the "user" edge to the User entity.
func (ptu *PasswordTokenUpdate) SetUser(u *User) *PasswordTokenUpdate { func (_u *PasswordTokenUpdate) SetUser(v *User) *PasswordTokenUpdate {
return ptu.SetUserID(u.ID) return _u.SetUserID(v.ID)
} }
// Mutation returns the PasswordTokenMutation object of the builder. // Mutation returns the PasswordTokenMutation object of the builder.
func (ptu *PasswordTokenUpdate) Mutation() *PasswordTokenMutation { func (_u *PasswordTokenUpdate) Mutation() *PasswordTokenMutation {
return ptu.mutation return _u.mutation
} }
// ClearUser clears the "user" edge to the User entity. // ClearUser clears the "user" edge to the User entity.
func (ptu *PasswordTokenUpdate) ClearUser() *PasswordTokenUpdate { func (_u *PasswordTokenUpdate) ClearUser() *PasswordTokenUpdate {
ptu.mutation.ClearUser() _u.mutation.ClearUser()
return ptu return _u
} }
// Save executes the query and returns the number of nodes affected by the update operation. // Save executes the query and returns the number of nodes affected by the update operation.
func (ptu *PasswordTokenUpdate) Save(ctx context.Context) (int, error) { func (_u *PasswordTokenUpdate) Save(ctx context.Context) (int, error) {
var ( return withHooks(ctx, _u.sqlSave, _u.mutation, _u.hooks)
err error
affected int
)
if len(ptu.hooks) == 0 {
if err = ptu.check(); err != nil {
return 0, err
}
affected, err = ptu.sqlSave(ctx)
} else {
var mut Mutator = MutateFunc(func(ctx context.Context, m Mutation) (Value, error) {
mutation, ok := m.(*PasswordTokenMutation)
if !ok {
return nil, fmt.Errorf("unexpected mutation type %T", m)
}
if err = ptu.check(); err != nil {
return 0, err
}
ptu.mutation = mutation
affected, err = ptu.sqlSave(ctx)
mutation.done = true
return affected, err
})
for i := len(ptu.hooks) - 1; i >= 0; i-- {
if ptu.hooks[i] == nil {
return 0, fmt.Errorf("ent: uninitialized hook (forgotten import ent/runtime?)")
}
mut = ptu.hooks[i](mut)
}
if _, err := mut.Mutate(ctx, ptu.mutation); err != nil {
return 0, err
}
}
return affected, err
} }
// SaveX is like Save, but panics if an error occurs. // SaveX is like Save, but panics if an error occurs.
func (ptu *PasswordTokenUpdate) SaveX(ctx context.Context) int { func (_u *PasswordTokenUpdate) SaveX(ctx context.Context) int {
affected, err := ptu.Save(ctx) affected, err := _u.Save(ctx)
if err != nil { if err != nil {
panic(err) panic(err)
} }
@ -119,64 +102,50 @@ func (ptu *PasswordTokenUpdate) SaveX(ctx context.Context) int {
} }
// Exec executes the query. // Exec executes the query.
func (ptu *PasswordTokenUpdate) Exec(ctx context.Context) error { func (_u *PasswordTokenUpdate) Exec(ctx context.Context) error {
_, err := ptu.Save(ctx) _, err := _u.Save(ctx)
return err return err
} }
// ExecX is like Exec, but panics if an error occurs. // ExecX is like Exec, but panics if an error occurs.
func (ptu *PasswordTokenUpdate) ExecX(ctx context.Context) { func (_u *PasswordTokenUpdate) ExecX(ctx context.Context) {
if err := ptu.Exec(ctx); err != nil { if err := _u.Exec(ctx); err != nil {
panic(err) panic(err)
} }
} }
// check runs all checks and user-defined validators on the builder. // check runs all checks and user-defined validators on the builder.
func (ptu *PasswordTokenUpdate) check() error { func (_u *PasswordTokenUpdate) check() error {
if v, ok := ptu.mutation.Hash(); ok { if v, ok := _u.mutation.Token(); ok {
if err := passwordtoken.HashValidator(v); err != nil { if err := passwordtoken.TokenValidator(v); err != nil {
return &ValidationError{Name: "hash", err: fmt.Errorf("ent: validator failed for field \"hash\": %w", err)} return &ValidationError{Name: "token", err: fmt.Errorf(`ent: validator failed for field "PasswordToken.token": %w`, err)}
} }
} }
if _, ok := ptu.mutation.UserID(); ptu.mutation.UserCleared() && !ok { if _u.mutation.UserCleared() && len(_u.mutation.UserIDs()) > 0 {
return errors.New("ent: clearing a required unique edge \"user\"") return errors.New(`ent: clearing a required unique edge "PasswordToken.user"`)
} }
return nil return nil
} }
func (ptu *PasswordTokenUpdate) sqlSave(ctx context.Context) (n int, err error) { func (_u *PasswordTokenUpdate) sqlSave(ctx context.Context) (_node int, err error) {
_spec := &sqlgraph.UpdateSpec{ if err := _u.check(); err != nil {
Node: &sqlgraph.NodeSpec{ return _node, err
Table: passwordtoken.Table,
Columns: passwordtoken.Columns,
ID: &sqlgraph.FieldSpec{
Type: field.TypeInt,
Column: passwordtoken.FieldID,
},
},
} }
if ps := ptu.mutation.predicates; len(ps) > 0 { _spec := sqlgraph.NewUpdateSpec(passwordtoken.Table, passwordtoken.Columns, sqlgraph.NewFieldSpec(passwordtoken.FieldID, field.TypeInt))
if ps := _u.mutation.predicates; len(ps) > 0 {
_spec.Predicate = func(selector *sql.Selector) { _spec.Predicate = func(selector *sql.Selector) {
for i := range ps { for i := range ps {
ps[i](selector) ps[i](selector)
} }
} }
} }
if value, ok := ptu.mutation.Hash(); ok { if value, ok := _u.mutation.Token(); ok {
_spec.Fields.Set = append(_spec.Fields.Set, &sqlgraph.FieldSpec{ _spec.SetField(passwordtoken.FieldToken, field.TypeString, value)
Type: field.TypeString,
Value: value,
Column: passwordtoken.FieldHash,
})
} }
if value, ok := ptu.mutation.CreatedAt(); ok { if value, ok := _u.mutation.CreatedAt(); ok {
_spec.Fields.Set = append(_spec.Fields.Set, &sqlgraph.FieldSpec{ _spec.SetField(passwordtoken.FieldCreatedAt, field.TypeTime, value)
Type: field.TypeTime,
Value: value,
Column: passwordtoken.FieldCreatedAt,
})
} }
if ptu.mutation.UserCleared() { if _u.mutation.UserCleared() {
edge := &sqlgraph.EdgeSpec{ edge := &sqlgraph.EdgeSpec{
Rel: sqlgraph.M2O, Rel: sqlgraph.M2O,
Inverse: false, Inverse: false,
@ -184,15 +153,12 @@ func (ptu *PasswordTokenUpdate) sqlSave(ctx context.Context) (n int, err error)
Columns: []string{passwordtoken.UserColumn}, Columns: []string{passwordtoken.UserColumn},
Bidi: false, Bidi: false,
Target: &sqlgraph.EdgeTarget{ Target: &sqlgraph.EdgeTarget{
IDSpec: &sqlgraph.FieldSpec{ IDSpec: sqlgraph.NewFieldSpec(user.FieldID, field.TypeInt),
Type: field.TypeInt,
Column: user.FieldID,
},
}, },
} }
_spec.Edges.Clear = append(_spec.Edges.Clear, edge) _spec.Edges.Clear = append(_spec.Edges.Clear, edge)
} }
if nodes := ptu.mutation.UserIDs(); len(nodes) > 0 { if nodes := _u.mutation.UserIDs(); len(nodes) > 0 {
edge := &sqlgraph.EdgeSpec{ edge := &sqlgraph.EdgeSpec{
Rel: sqlgraph.M2O, Rel: sqlgraph.M2O,
Inverse: false, Inverse: false,
@ -200,10 +166,7 @@ func (ptu *PasswordTokenUpdate) sqlSave(ctx context.Context) (n int, err error)
Columns: []string{passwordtoken.UserColumn}, Columns: []string{passwordtoken.UserColumn},
Bidi: false, Bidi: false,
Target: &sqlgraph.EdgeTarget{ Target: &sqlgraph.EdgeTarget{
IDSpec: &sqlgraph.FieldSpec{ IDSpec: sqlgraph.NewFieldSpec(user.FieldID, field.TypeInt),
Type: field.TypeInt,
Column: user.FieldID,
},
}, },
} }
for _, k := range nodes { for _, k := range nodes {
@ -211,15 +174,16 @@ func (ptu *PasswordTokenUpdate) sqlSave(ctx context.Context) (n int, err error)
} }
_spec.Edges.Add = append(_spec.Edges.Add, edge) _spec.Edges.Add = append(_spec.Edges.Add, edge)
} }
if n, err = sqlgraph.UpdateNodes(ctx, ptu.driver, _spec); err != nil { if _node, err = sqlgraph.UpdateNodes(ctx, _u.driver, _spec); err != nil {
if _, ok := err.(*sqlgraph.NotFoundError); ok { if _, ok := err.(*sqlgraph.NotFoundError); ok {
err = &NotFoundError{passwordtoken.Label} err = &NotFoundError{passwordtoken.Label}
} else if sqlgraph.IsConstraintError(err) { } else if sqlgraph.IsConstraintError(err) {
err = &ConstraintError{err.Error(), err} err = &ConstraintError{msg: err.Error(), wrap: err}
} }
return 0, err return 0, err
} }
return n, nil _u.mutation.done = true
return _node, nil
} }
// PasswordTokenUpdateOne is the builder for updating a single PasswordToken entity. // PasswordTokenUpdateOne is the builder for updating a single PasswordToken entity.
@ -230,96 +194,85 @@ type PasswordTokenUpdateOne struct {
mutation *PasswordTokenMutation mutation *PasswordTokenMutation
} }
// SetHash sets the "hash" field. // SetToken sets the "token" field.
func (ptuo *PasswordTokenUpdateOne) SetHash(s string) *PasswordTokenUpdateOne { func (_u *PasswordTokenUpdateOne) SetToken(v string) *PasswordTokenUpdateOne {
ptuo.mutation.SetHash(s) _u.mutation.SetToken(v)
return ptuo return _u
}
// SetNillableToken sets the "token" field if the given value is not nil.
func (_u *PasswordTokenUpdateOne) SetNillableToken(v *string) *PasswordTokenUpdateOne {
if v != nil {
_u.SetToken(*v)
}
return _u
}
// SetUserID sets the "user_id" field.
func (_u *PasswordTokenUpdateOne) SetUserID(v int) *PasswordTokenUpdateOne {
_u.mutation.SetUserID(v)
return _u
}
// SetNillableUserID sets the "user_id" field if the given value is not nil.
func (_u *PasswordTokenUpdateOne) SetNillableUserID(v *int) *PasswordTokenUpdateOne {
if v != nil {
_u.SetUserID(*v)
}
return _u
} }
// SetCreatedAt sets the "created_at" field. // SetCreatedAt sets the "created_at" field.
func (ptuo *PasswordTokenUpdateOne) SetCreatedAt(t time.Time) *PasswordTokenUpdateOne { func (_u *PasswordTokenUpdateOne) SetCreatedAt(v time.Time) *PasswordTokenUpdateOne {
ptuo.mutation.SetCreatedAt(t) _u.mutation.SetCreatedAt(v)
return ptuo return _u
} }
// SetNillableCreatedAt sets the "created_at" field if the given value is not nil. // SetNillableCreatedAt sets the "created_at" field if the given value is not nil.
func (ptuo *PasswordTokenUpdateOne) SetNillableCreatedAt(t *time.Time) *PasswordTokenUpdateOne { func (_u *PasswordTokenUpdateOne) SetNillableCreatedAt(v *time.Time) *PasswordTokenUpdateOne {
if t != nil { if v != nil {
ptuo.SetCreatedAt(*t) _u.SetCreatedAt(*v)
} }
return ptuo return _u
}
// SetUserID sets the "user" edge to the User entity by ID.
func (ptuo *PasswordTokenUpdateOne) SetUserID(id int) *PasswordTokenUpdateOne {
ptuo.mutation.SetUserID(id)
return ptuo
} }
// SetUser sets the "user" edge to the User entity. // SetUser sets the "user" edge to the User entity.
func (ptuo *PasswordTokenUpdateOne) SetUser(u *User) *PasswordTokenUpdateOne { func (_u *PasswordTokenUpdateOne) SetUser(v *User) *PasswordTokenUpdateOne {
return ptuo.SetUserID(u.ID) return _u.SetUserID(v.ID)
} }
// Mutation returns the PasswordTokenMutation object of the builder. // Mutation returns the PasswordTokenMutation object of the builder.
func (ptuo *PasswordTokenUpdateOne) Mutation() *PasswordTokenMutation { func (_u *PasswordTokenUpdateOne) Mutation() *PasswordTokenMutation {
return ptuo.mutation return _u.mutation
} }
// ClearUser clears the "user" edge to the User entity. // ClearUser clears the "user" edge to the User entity.
func (ptuo *PasswordTokenUpdateOne) ClearUser() *PasswordTokenUpdateOne { func (_u *PasswordTokenUpdateOne) ClearUser() *PasswordTokenUpdateOne {
ptuo.mutation.ClearUser() _u.mutation.ClearUser()
return ptuo return _u
}
// Where appends a list predicates to the PasswordTokenUpdate builder.
func (_u *PasswordTokenUpdateOne) Where(ps ...predicate.PasswordToken) *PasswordTokenUpdateOne {
_u.mutation.Where(ps...)
return _u
} }
// Select allows selecting one or more fields (columns) of the returned entity. // Select allows selecting one or more fields (columns) of the returned entity.
// The default is selecting all fields defined in the entity schema. // The default is selecting all fields defined in the entity schema.
func (ptuo *PasswordTokenUpdateOne) Select(field string, fields ...string) *PasswordTokenUpdateOne { func (_u *PasswordTokenUpdateOne) Select(field string, fields ...string) *PasswordTokenUpdateOne {
ptuo.fields = append([]string{field}, fields...) _u.fields = append([]string{field}, fields...)
return ptuo return _u
} }
// Save executes the query and returns the updated PasswordToken entity. // Save executes the query and returns the updated PasswordToken entity.
func (ptuo *PasswordTokenUpdateOne) Save(ctx context.Context) (*PasswordToken, error) { func (_u *PasswordTokenUpdateOne) Save(ctx context.Context) (*PasswordToken, error) {
var ( return withHooks(ctx, _u.sqlSave, _u.mutation, _u.hooks)
err error
node *PasswordToken
)
if len(ptuo.hooks) == 0 {
if err = ptuo.check(); err != nil {
return nil, err
}
node, err = ptuo.sqlSave(ctx)
} else {
var mut Mutator = MutateFunc(func(ctx context.Context, m Mutation) (Value, error) {
mutation, ok := m.(*PasswordTokenMutation)
if !ok {
return nil, fmt.Errorf("unexpected mutation type %T", m)
}
if err = ptuo.check(); err != nil {
return nil, err
}
ptuo.mutation = mutation
node, err = ptuo.sqlSave(ctx)
mutation.done = true
return node, err
})
for i := len(ptuo.hooks) - 1; i >= 0; i-- {
if ptuo.hooks[i] == nil {
return nil, fmt.Errorf("ent: uninitialized hook (forgotten import ent/runtime?)")
}
mut = ptuo.hooks[i](mut)
}
if _, err := mut.Mutate(ctx, ptuo.mutation); err != nil {
return nil, err
}
}
return node, err
} }
// SaveX is like Save, but panics if an error occurs. // SaveX is like Save, but panics if an error occurs.
func (ptuo *PasswordTokenUpdateOne) SaveX(ctx context.Context) *PasswordToken { func (_u *PasswordTokenUpdateOne) SaveX(ctx context.Context) *PasswordToken {
node, err := ptuo.Save(ctx) node, err := _u.Save(ctx)
if err != nil { if err != nil {
panic(err) panic(err)
} }
@ -327,48 +280,42 @@ func (ptuo *PasswordTokenUpdateOne) SaveX(ctx context.Context) *PasswordToken {
} }
// Exec executes the query on the entity. // Exec executes the query on the entity.
func (ptuo *PasswordTokenUpdateOne) Exec(ctx context.Context) error { func (_u *PasswordTokenUpdateOne) Exec(ctx context.Context) error {
_, err := ptuo.Save(ctx) _, err := _u.Save(ctx)
return err return err
} }
// ExecX is like Exec, but panics if an error occurs. // ExecX is like Exec, but panics if an error occurs.
func (ptuo *PasswordTokenUpdateOne) ExecX(ctx context.Context) { func (_u *PasswordTokenUpdateOne) ExecX(ctx context.Context) {
if err := ptuo.Exec(ctx); err != nil { if err := _u.Exec(ctx); err != nil {
panic(err) panic(err)
} }
} }
// check runs all checks and user-defined validators on the builder. // check runs all checks and user-defined validators on the builder.
func (ptuo *PasswordTokenUpdateOne) check() error { func (_u *PasswordTokenUpdateOne) check() error {
if v, ok := ptuo.mutation.Hash(); ok { if v, ok := _u.mutation.Token(); ok {
if err := passwordtoken.HashValidator(v); err != nil { if err := passwordtoken.TokenValidator(v); err != nil {
return &ValidationError{Name: "hash", err: fmt.Errorf("ent: validator failed for field \"hash\": %w", err)} return &ValidationError{Name: "token", err: fmt.Errorf(`ent: validator failed for field "PasswordToken.token": %w`, err)}
} }
} }
if _, ok := ptuo.mutation.UserID(); ptuo.mutation.UserCleared() && !ok { if _u.mutation.UserCleared() && len(_u.mutation.UserIDs()) > 0 {
return errors.New("ent: clearing a required unique edge \"user\"") return errors.New(`ent: clearing a required unique edge "PasswordToken.user"`)
} }
return nil return nil
} }
func (ptuo *PasswordTokenUpdateOne) sqlSave(ctx context.Context) (_node *PasswordToken, err error) { func (_u *PasswordTokenUpdateOne) sqlSave(ctx context.Context) (_node *PasswordToken, err error) {
_spec := &sqlgraph.UpdateSpec{ if err := _u.check(); err != nil {
Node: &sqlgraph.NodeSpec{ return _node, err
Table: passwordtoken.Table,
Columns: passwordtoken.Columns,
ID: &sqlgraph.FieldSpec{
Type: field.TypeInt,
Column: passwordtoken.FieldID,
},
},
} }
id, ok := ptuo.mutation.ID() _spec := sqlgraph.NewUpdateSpec(passwordtoken.Table, passwordtoken.Columns, sqlgraph.NewFieldSpec(passwordtoken.FieldID, field.TypeInt))
id, ok := _u.mutation.ID()
if !ok { if !ok {
return nil, &ValidationError{Name: "ID", err: fmt.Errorf("missing PasswordToken.ID for update")} return nil, &ValidationError{Name: "id", err: errors.New(`ent: missing "PasswordToken.id" for update`)}
} }
_spec.Node.ID.Value = id _spec.Node.ID.Value = id
if fields := ptuo.fields; len(fields) > 0 { if fields := _u.fields; len(fields) > 0 {
_spec.Node.Columns = make([]string, 0, len(fields)) _spec.Node.Columns = make([]string, 0, len(fields))
_spec.Node.Columns = append(_spec.Node.Columns, passwordtoken.FieldID) _spec.Node.Columns = append(_spec.Node.Columns, passwordtoken.FieldID)
for _, f := range fields { for _, f := range fields {
@ -380,28 +327,20 @@ func (ptuo *PasswordTokenUpdateOne) sqlSave(ctx context.Context) (_node *Passwor
} }
} }
} }
if ps := ptuo.mutation.predicates; len(ps) > 0 { if ps := _u.mutation.predicates; len(ps) > 0 {
_spec.Predicate = func(selector *sql.Selector) { _spec.Predicate = func(selector *sql.Selector) {
for i := range ps { for i := range ps {
ps[i](selector) ps[i](selector)
} }
} }
} }
if value, ok := ptuo.mutation.Hash(); ok { if value, ok := _u.mutation.Token(); ok {
_spec.Fields.Set = append(_spec.Fields.Set, &sqlgraph.FieldSpec{ _spec.SetField(passwordtoken.FieldToken, field.TypeString, value)
Type: field.TypeString,
Value: value,
Column: passwordtoken.FieldHash,
})
} }
if value, ok := ptuo.mutation.CreatedAt(); ok { if value, ok := _u.mutation.CreatedAt(); ok {
_spec.Fields.Set = append(_spec.Fields.Set, &sqlgraph.FieldSpec{ _spec.SetField(passwordtoken.FieldCreatedAt, field.TypeTime, value)
Type: field.TypeTime,
Value: value,
Column: passwordtoken.FieldCreatedAt,
})
} }
if ptuo.mutation.UserCleared() { if _u.mutation.UserCleared() {
edge := &sqlgraph.EdgeSpec{ edge := &sqlgraph.EdgeSpec{
Rel: sqlgraph.M2O, Rel: sqlgraph.M2O,
Inverse: false, Inverse: false,
@ -409,15 +348,12 @@ func (ptuo *PasswordTokenUpdateOne) sqlSave(ctx context.Context) (_node *Passwor
Columns: []string{passwordtoken.UserColumn}, Columns: []string{passwordtoken.UserColumn},
Bidi: false, Bidi: false,
Target: &sqlgraph.EdgeTarget{ Target: &sqlgraph.EdgeTarget{
IDSpec: &sqlgraph.FieldSpec{ IDSpec: sqlgraph.NewFieldSpec(user.FieldID, field.TypeInt),
Type: field.TypeInt,
Column: user.FieldID,
},
}, },
} }
_spec.Edges.Clear = append(_spec.Edges.Clear, edge) _spec.Edges.Clear = append(_spec.Edges.Clear, edge)
} }
if nodes := ptuo.mutation.UserIDs(); len(nodes) > 0 { if nodes := _u.mutation.UserIDs(); len(nodes) > 0 {
edge := &sqlgraph.EdgeSpec{ edge := &sqlgraph.EdgeSpec{
Rel: sqlgraph.M2O, Rel: sqlgraph.M2O,
Inverse: false, Inverse: false,
@ -425,10 +361,7 @@ func (ptuo *PasswordTokenUpdateOne) sqlSave(ctx context.Context) (_node *Passwor
Columns: []string{passwordtoken.UserColumn}, Columns: []string{passwordtoken.UserColumn},
Bidi: false, Bidi: false,
Target: &sqlgraph.EdgeTarget{ Target: &sqlgraph.EdgeTarget{
IDSpec: &sqlgraph.FieldSpec{ IDSpec: sqlgraph.NewFieldSpec(user.FieldID, field.TypeInt),
Type: field.TypeInt,
Column: user.FieldID,
},
}, },
} }
for _, k := range nodes { for _, k := range nodes {
@ -436,16 +369,17 @@ func (ptuo *PasswordTokenUpdateOne) sqlSave(ctx context.Context) (_node *Passwor
} }
_spec.Edges.Add = append(_spec.Edges.Add, edge) _spec.Edges.Add = append(_spec.Edges.Add, edge)
} }
_node = &PasswordToken{config: ptuo.config} _node = &PasswordToken{config: _u.config}
_spec.Assign = _node.assignValues _spec.Assign = _node.assignValues
_spec.ScanValues = _node.scanValues _spec.ScanValues = _node.scanValues
if err = sqlgraph.UpdateNode(ctx, ptuo.driver, _spec); err != nil { if err = sqlgraph.UpdateNode(ctx, _u.driver, _spec); err != nil {
if _, ok := err.(*sqlgraph.NotFoundError); ok { if _, ok := err.(*sqlgraph.NotFoundError); ok {
err = &NotFoundError{passwordtoken.Label} err = &NotFoundError{passwordtoken.Label}
} else if sqlgraph.IsConstraintError(err) { } else if sqlgraph.IsConstraintError(err) {
err = &ConstraintError{err.Error(), err} err = &ConstraintError{msg: err.Error(), wrap: err}
} }
return nil, err return nil, err
} }
_u.mutation.done = true
return _node, nil return _node, nil
} }

View file

@ -1,4 +1,4 @@
// Code generated by entc, DO NOT EDIT. // Code generated by ent, DO NOT EDIT.
package predicate package predicate

View file

@ -1,5 +1,5 @@
// Code generated by entc, DO NOT EDIT. // Code generated by ent, DO NOT EDIT.
package ent package ent
// The schema-stitching logic is generated in goweb/ent/runtime/runtime.go // The schema-stitching logic is generated in github.com/mikestefanello/pagoda/ent/runtime/runtime.go

View file

@ -1,26 +1,29 @@
// Code generated by entc, DO NOT EDIT. // Code generated by ent, DO NOT EDIT.
package runtime package runtime
import ( import (
"goweb/ent/passwordtoken"
"goweb/ent/schema"
"goweb/ent/user"
"time" "time"
"github.com/mikestefanello/pagoda/ent/passwordtoken"
"github.com/mikestefanello/pagoda/ent/schema"
"github.com/mikestefanello/pagoda/ent/user"
) )
// The init function reads all schema descriptors with runtime code // The init function reads all schema descriptors with runtime code
// (default values, validators, hooks and policies) and stitches it // (default values, validators, hooks and policies) and stitches it
// to their package variables. // to their package variables.
func init() { func init() {
passwordtokenHooks := schema.PasswordToken{}.Hooks()
passwordtoken.Hooks[0] = passwordtokenHooks[0]
passwordtokenFields := schema.PasswordToken{}.Fields() passwordtokenFields := schema.PasswordToken{}.Fields()
_ = passwordtokenFields _ = passwordtokenFields
// passwordtokenDescHash is the schema descriptor for hash field. // passwordtokenDescToken is the schema descriptor for token field.
passwordtokenDescHash := passwordtokenFields[0].Descriptor() passwordtokenDescToken := passwordtokenFields[0].Descriptor()
// passwordtoken.HashValidator is a validator for the "hash" field. It is called by the builders before save. // passwordtoken.TokenValidator is a validator for the "token" field. It is called by the builders before save.
passwordtoken.HashValidator = passwordtokenDescHash.Validators[0].(func(string) error) passwordtoken.TokenValidator = passwordtokenDescToken.Validators[0].(func(string) error)
// passwordtokenDescCreatedAt is the schema descriptor for created_at field. // passwordtokenDescCreatedAt is the schema descriptor for created_at field.
passwordtokenDescCreatedAt := passwordtokenFields[1].Descriptor() passwordtokenDescCreatedAt := passwordtokenFields[2].Descriptor()
// passwordtoken.DefaultCreatedAt holds the default value on creation for the created_at field. // passwordtoken.DefaultCreatedAt holds the default value on creation for the created_at field.
passwordtoken.DefaultCreatedAt = passwordtokenDescCreatedAt.Default.(func() time.Time) passwordtoken.DefaultCreatedAt = passwordtokenDescCreatedAt.Default.(func() time.Time)
userHooks := schema.User{}.Hooks() userHooks := schema.User{}.Hooks()
@ -34,18 +37,40 @@ func init() {
// userDescEmail is the schema descriptor for email field. // userDescEmail is the schema descriptor for email field.
userDescEmail := userFields[1].Descriptor() userDescEmail := userFields[1].Descriptor()
// user.EmailValidator is a validator for the "email" field. It is called by the builders before save. // user.EmailValidator is a validator for the "email" field. It is called by the builders before save.
user.EmailValidator = userDescEmail.Validators[0].(func(string) error) user.EmailValidator = func() func(string) error {
validators := userDescEmail.Validators
fns := [...]func(string) error{
validators[0].(func(string) error),
validators[1].(func(string) error),
}
return func(email string) error {
for _, fn := range fns {
if err := fn(email); err != nil {
return err
}
}
return nil
}
}()
// userDescPassword is the schema descriptor for password field. // userDescPassword is the schema descriptor for password field.
userDescPassword := userFields[2].Descriptor() userDescPassword := userFields[2].Descriptor()
// user.PasswordValidator is a validator for the "password" field. It is called by the builders before save. // user.PasswordValidator is a validator for the "password" field. It is called by the builders before save.
user.PasswordValidator = userDescPassword.Validators[0].(func(string) error) user.PasswordValidator = userDescPassword.Validators[0].(func(string) error)
// userDescVerified is the schema descriptor for verified field.
userDescVerified := userFields[3].Descriptor()
// user.DefaultVerified holds the default value on creation for the verified field.
user.DefaultVerified = userDescVerified.Default.(bool)
// userDescAdmin is the schema descriptor for admin field.
userDescAdmin := userFields[4].Descriptor()
// user.DefaultAdmin holds the default value on creation for the admin field.
user.DefaultAdmin = userDescAdmin.Default.(bool)
// userDescCreatedAt is the schema descriptor for created_at field. // userDescCreatedAt is the schema descriptor for created_at field.
userDescCreatedAt := userFields[3].Descriptor() userDescCreatedAt := userFields[5].Descriptor()
// user.DefaultCreatedAt holds the default value on creation for the created_at field. // user.DefaultCreatedAt holds the default value on creation for the created_at field.
user.DefaultCreatedAt = userDescCreatedAt.Default.(func() time.Time) user.DefaultCreatedAt = userDescCreatedAt.Default.(func() time.Time)
} }
const ( const (
Version = "v0.9.1" // Version of ent codegen. Version = "v0.14.5" // Version of ent codegen.
Sum = "h1:IG8andyeD79GG24U8Q+1Y45hQXj6gY5evSBcva5gtBk=" // Sum of ent codegen. Sum = "h1:Rj2WOYJtCkWyFo6a+5wB3EfBRP0rnx1fMk6gGA0UUe4=" // Sum of ent codegen.
) )

View file

@ -1,11 +1,15 @@
package schema package schema
import ( import (
"context"
"time" "time"
"entgo.io/ent" "entgo.io/ent"
"entgo.io/ent/schema/edge" "entgo.io/ent/schema/edge"
"entgo.io/ent/schema/field" "entgo.io/ent/schema/field"
ge "github.com/mikestefanello/pagoda/ent"
"github.com/mikestefanello/pagoda/ent/hook"
"golang.org/x/crypto/bcrypt"
) )
// PasswordToken holds the schema definition for the PasswordToken entity. // PasswordToken holds the schema definition for the PasswordToken entity.
@ -16,9 +20,10 @@ type PasswordToken struct {
// Fields of the PasswordToken. // Fields of the PasswordToken.
func (PasswordToken) Fields() []ent.Field { func (PasswordToken) Fields() []ent.Field {
return []ent.Field{ return []ent.Field{
field.String("hash"). field.String("token").
Sensitive(). Sensitive().
NotEmpty(), NotEmpty(),
field.Int("user_id"),
field.Time("created_at"). field.Time("created_at").
Default(time.Now), Default(time.Now),
} }
@ -28,7 +33,30 @@ func (PasswordToken) Fields() []ent.Field {
func (PasswordToken) Edges() []ent.Edge { func (PasswordToken) Edges() []ent.Edge {
return []ent.Edge{ return []ent.Edge{
edge.To("user", User.Type). edge.To("user", User.Type).
Field("user_id").
Required(). Required().
Unique(), Unique(),
} }
} }
// Hooks of the PasswordToken.
func (PasswordToken) Hooks() []ent.Hook {
return []ent.Hook{
hook.On(
func(next ent.Mutator) ent.Mutator {
return hook.PasswordTokenFunc(func(ctx context.Context, m *ge.PasswordTokenMutation) (ent.Value, error) {
if v, exists := m.Token(); exists {
hash, err := bcrypt.GenerateFromPassword([]byte(v), bcrypt.DefaultCost)
if err != nil {
return "", err
}
m.SetToken(string(hash))
}
return next.Mutate(ctx, m)
})
},
// Limit the hook only for these operations.
ent.OpCreate|ent.OpUpdate|ent.OpUpdateOne,
),
}
}

View file

@ -2,11 +2,13 @@ package schema
import ( import (
"context" "context"
"net/mail"
"strings" "strings"
"time" "time"
ge "goweb/ent" ge "github.com/mikestefanello/pagoda/ent"
"goweb/ent/hook" "github.com/mikestefanello/pagoda/ent/hook"
"golang.org/x/crypto/bcrypt"
"entgo.io/ent" "entgo.io/ent"
"entgo.io/ent/schema/edge" "entgo.io/ent/schema/edge"
@ -25,10 +27,18 @@ func (User) Fields() []ent.Field {
NotEmpty(), NotEmpty(),
field.String("email"). field.String("email").
NotEmpty(). NotEmpty().
Unique(), Unique().
Validate(func(s string) error {
_, err := mail.ParseAddress(s)
return err
}),
field.String("password"). field.String("password").
Sensitive(). Sensitive().
NotEmpty(), NotEmpty(),
field.Bool("verified").
Default(false),
field.Bool("admin").
Default(false),
field.Time("created_at"). field.Time("created_at").
Default(time.Now). Default(time.Now).
Immutable(), Immutable(),
@ -52,6 +62,14 @@ func (User) Hooks() []ent.Hook {
if v, exists := m.Email(); exists { if v, exists := m.Email(); exists {
m.SetEmail(strings.ToLower(v)) m.SetEmail(strings.ToLower(v))
} }
if v, exists := m.Password(); exists {
hash, err := bcrypt.GenerateFromPassword([]byte(v), bcrypt.DefaultCost)
if err != nil {
return "", err
}
m.SetPassword(string(hash))
}
return next.Mutate(ctx, m) return next.Mutate(ctx, m)
}) })
}, },

View file

@ -1,4 +1,4 @@
// Code generated by entc, DO NOT EDIT. // Code generated by ent, DO NOT EDIT.
package ent package ent
@ -20,19 +20,13 @@ type Tx struct {
// lazily loaded. // lazily loaded.
client *Client client *Client
clientOnce sync.Once clientOnce sync.Once
// completion callbacks.
mu sync.Mutex
onCommit []CommitHook
onRollback []RollbackHook
// ctx lives for the life of the transaction. It is // ctx lives for the life of the transaction. It is
// the same context used by the underlying connection. // the same context used by the underlying connection.
ctx context.Context ctx context.Context
} }
type ( type (
// Committer is the interface that wraps the Committer method. // Committer is the interface that wraps the Commit method.
Committer interface { Committer interface {
Commit(context.Context, *Tx) error Commit(context.Context, *Tx) error
} }
@ -46,7 +40,7 @@ type (
// and returns a Committer. For example: // and returns a Committer. For example:
// //
// hook := func(next ent.Committer) ent.Committer { // hook := func(next ent.Committer) ent.Committer {
// return ent.CommitFunc(func(context.Context, tx *ent.Tx) error { // return ent.CommitFunc(func(ctx context.Context, tx *ent.Tx) error {
// // Do some stuff before. // // Do some stuff before.
// if err := next.Commit(ctx, tx); err != nil { // if err := next.Commit(ctx, tx); err != nil {
// return err // return err
@ -70,9 +64,9 @@ func (tx *Tx) Commit() error {
var fn Committer = CommitFunc(func(context.Context, *Tx) error { var fn Committer = CommitFunc(func(context.Context, *Tx) error {
return txDriver.tx.Commit() return txDriver.tx.Commit()
}) })
tx.mu.Lock() txDriver.mu.Lock()
hooks := append([]CommitHook(nil), tx.onCommit...) hooks := append([]CommitHook(nil), txDriver.onCommit...)
tx.mu.Unlock() txDriver.mu.Unlock()
for i := len(hooks) - 1; i >= 0; i-- { for i := len(hooks) - 1; i >= 0; i-- {
fn = hooks[i](fn) fn = hooks[i](fn)
} }
@ -81,13 +75,14 @@ func (tx *Tx) Commit() error {
// OnCommit adds a hook to call on commit. // OnCommit adds a hook to call on commit.
func (tx *Tx) OnCommit(f CommitHook) { func (tx *Tx) OnCommit(f CommitHook) {
tx.mu.Lock() txDriver := tx.config.driver.(*txDriver)
defer tx.mu.Unlock() txDriver.mu.Lock()
tx.onCommit = append(tx.onCommit, f) txDriver.onCommit = append(txDriver.onCommit, f)
txDriver.mu.Unlock()
} }
type ( type (
// Rollbacker is the interface that wraps the Rollbacker method. // Rollbacker is the interface that wraps the Rollback method.
Rollbacker interface { Rollbacker interface {
Rollback(context.Context, *Tx) error Rollback(context.Context, *Tx) error
} }
@ -101,7 +96,7 @@ type (
// and returns a Rollbacker. For example: // and returns a Rollbacker. For example:
// //
// hook := func(next ent.Rollbacker) ent.Rollbacker { // hook := func(next ent.Rollbacker) ent.Rollbacker {
// return ent.RollbackFunc(func(context.Context, tx *ent.Tx) error { // return ent.RollbackFunc(func(ctx context.Context, tx *ent.Tx) error {
// // Do some stuff before. // // Do some stuff before.
// if err := next.Rollback(ctx, tx); err != nil { // if err := next.Rollback(ctx, tx); err != nil {
// return err // return err
@ -125,9 +120,9 @@ func (tx *Tx) Rollback() error {
var fn Rollbacker = RollbackFunc(func(context.Context, *Tx) error { var fn Rollbacker = RollbackFunc(func(context.Context, *Tx) error {
return txDriver.tx.Rollback() return txDriver.tx.Rollback()
}) })
tx.mu.Lock() txDriver.mu.Lock()
hooks := append([]RollbackHook(nil), tx.onRollback...) hooks := append([]RollbackHook(nil), txDriver.onRollback...)
tx.mu.Unlock() txDriver.mu.Unlock()
for i := len(hooks) - 1; i >= 0; i-- { for i := len(hooks) - 1; i >= 0; i-- {
fn = hooks[i](fn) fn = hooks[i](fn)
} }
@ -136,9 +131,10 @@ func (tx *Tx) Rollback() error {
// OnRollback adds a hook to call on rollback. // OnRollback adds a hook to call on rollback.
func (tx *Tx) OnRollback(f RollbackHook) { func (tx *Tx) OnRollback(f RollbackHook) {
tx.mu.Lock() txDriver := tx.config.driver.(*txDriver)
defer tx.mu.Unlock() txDriver.mu.Lock()
tx.onRollback = append(tx.onRollback, f) txDriver.onRollback = append(txDriver.onRollback, f)
txDriver.mu.Unlock()
} }
// Client returns a Client that binds to current transaction. // Client returns a Client that binds to current transaction.
@ -171,6 +167,10 @@ type txDriver struct {
drv dialect.Driver drv dialect.Driver
// tx is the underlying transaction. // tx is the underlying transaction.
tx dialect.Tx tx dialect.Tx
// completion hooks.
mu sync.Mutex
onCommit []CommitHook
onRollback []RollbackHook
} }
// newTx creates a new transactional driver. // newTx creates a new transactional driver.
@ -201,12 +201,12 @@ func (*txDriver) Commit() error { return nil }
func (*txDriver) Rollback() error { return nil } func (*txDriver) Rollback() error { return nil }
// Exec calls tx.Exec. // Exec calls tx.Exec.
func (tx *txDriver) Exec(ctx context.Context, query string, args, v interface{}) error { func (tx *txDriver) Exec(ctx context.Context, query string, args, v any) error {
return tx.tx.Exec(ctx, query, args, v) return tx.tx.Exec(ctx, query, args, v)
} }
// Query calls tx.Query. // Query calls tx.Query.
func (tx *txDriver) Query(ctx context.Context, query string, args, v interface{}) error { func (tx *txDriver) Query(ctx context.Context, query string, args, v any) error {
return tx.tx.Query(ctx, query, args, v) return tx.tx.Query(ctx, query, args, v)
} }

View file

@ -1,14 +1,15 @@
// Code generated by entc, DO NOT EDIT. // Code generated by ent, DO NOT EDIT.
package ent package ent
import ( import (
"fmt" "fmt"
"goweb/ent/user"
"strings" "strings"
"time" "time"
"entgo.io/ent"
"entgo.io/ent/dialect/sql" "entgo.io/ent/dialect/sql"
"github.com/mikestefanello/pagoda/ent/user"
) )
// User is the model entity for the User schema. // User is the model entity for the User schema.
@ -22,11 +23,16 @@ type User struct {
Email string `json:"email,omitempty"` Email string `json:"email,omitempty"`
// Password holds the value of the "password" field. // Password holds the value of the "password" field.
Password string `json:"-"` Password string `json:"-"`
// Verified holds the value of the "verified" field.
Verified bool `json:"verified,omitempty"`
// Admin holds the value of the "admin" field.
Admin bool `json:"admin,omitempty"`
// CreatedAt holds the value of the "created_at" field. // CreatedAt holds the value of the "created_at" field.
CreatedAt time.Time `json:"created_at,omitempty"` CreatedAt time.Time `json:"created_at,omitempty"`
// Edges holds the relations/edges for other nodes in the graph. // Edges holds the relations/edges for other nodes in the graph.
// The values are being populated by the UserQuery when eager-loading is set. // The values are being populated by the UserQuery when eager-loading is set.
Edges UserEdges `json:"edges"` Edges UserEdges `json:"edges"`
selectValues sql.SelectValues
} }
// UserEdges holds the relations/edges for other nodes in the graph. // UserEdges holds the relations/edges for other nodes in the graph.
@ -48,10 +54,12 @@ func (e UserEdges) OwnerOrErr() ([]*PasswordToken, error) {
} }
// scanValues returns the types for scanning values from sql.Rows. // scanValues returns the types for scanning values from sql.Rows.
func (*User) scanValues(columns []string) ([]interface{}, error) { func (*User) scanValues(columns []string) ([]any, error) {
values := make([]interface{}, len(columns)) values := make([]any, len(columns))
for i := range columns { for i := range columns {
switch columns[i] { switch columns[i] {
case user.FieldVerified, user.FieldAdmin:
values[i] = new(sql.NullBool)
case user.FieldID: case user.FieldID:
values[i] = new(sql.NullInt64) values[i] = new(sql.NullInt64)
case user.FieldName, user.FieldEmail, user.FieldPassword: case user.FieldName, user.FieldEmail, user.FieldPassword:
@ -59,7 +67,7 @@ func (*User) scanValues(columns []string) ([]interface{}, error) {
case user.FieldCreatedAt: case user.FieldCreatedAt:
values[i] = new(sql.NullTime) values[i] = new(sql.NullTime)
default: default:
return nil, fmt.Errorf("unexpected column %q for type User", columns[i]) values[i] = new(sql.UnknownType)
} }
} }
return values, nil return values, nil
@ -67,7 +75,7 @@ func (*User) scanValues(columns []string) ([]interface{}, error) {
// assignValues assigns the values that were returned from sql.Rows (after scanning) // assignValues assigns the values that were returned from sql.Rows (after scanning)
// to the User fields. // to the User fields.
func (u *User) assignValues(columns []string, values []interface{}) error { func (_m *User) assignValues(columns []string, values []any) error {
if m, n := len(values), len(columns); m < n { if m, n := len(values), len(columns); m < n {
return fmt.Errorf("mismatch number of scan values: %d != %d", m, n) return fmt.Errorf("mismatch number of scan values: %d != %d", m, n)
} }
@ -78,80 +86,103 @@ func (u *User) assignValues(columns []string, values []interface{}) error {
if !ok { if !ok {
return fmt.Errorf("unexpected type %T for field id", value) return fmt.Errorf("unexpected type %T for field id", value)
} }
u.ID = int(value.Int64) _m.ID = int(value.Int64)
case user.FieldName: case user.FieldName:
if value, ok := values[i].(*sql.NullString); !ok { if value, ok := values[i].(*sql.NullString); !ok {
return fmt.Errorf("unexpected type %T for field name", values[i]) return fmt.Errorf("unexpected type %T for field name", values[i])
} else if value.Valid { } else if value.Valid {
u.Name = value.String _m.Name = value.String
} }
case user.FieldEmail: case user.FieldEmail:
if value, ok := values[i].(*sql.NullString); !ok { if value, ok := values[i].(*sql.NullString); !ok {
return fmt.Errorf("unexpected type %T for field email", values[i]) return fmt.Errorf("unexpected type %T for field email", values[i])
} else if value.Valid { } else if value.Valid {
u.Email = value.String _m.Email = value.String
} }
case user.FieldPassword: case user.FieldPassword:
if value, ok := values[i].(*sql.NullString); !ok { if value, ok := values[i].(*sql.NullString); !ok {
return fmt.Errorf("unexpected type %T for field password", values[i]) return fmt.Errorf("unexpected type %T for field password", values[i])
} else if value.Valid { } else if value.Valid {
u.Password = value.String _m.Password = value.String
}
case user.FieldVerified:
if value, ok := values[i].(*sql.NullBool); !ok {
return fmt.Errorf("unexpected type %T for field verified", values[i])
} else if value.Valid {
_m.Verified = value.Bool
}
case user.FieldAdmin:
if value, ok := values[i].(*sql.NullBool); !ok {
return fmt.Errorf("unexpected type %T for field admin", values[i])
} else if value.Valid {
_m.Admin = value.Bool
} }
case user.FieldCreatedAt: case user.FieldCreatedAt:
if value, ok := values[i].(*sql.NullTime); !ok { if value, ok := values[i].(*sql.NullTime); !ok {
return fmt.Errorf("unexpected type %T for field created_at", values[i]) return fmt.Errorf("unexpected type %T for field created_at", values[i])
} else if value.Valid { } else if value.Valid {
u.CreatedAt = value.Time _m.CreatedAt = value.Time
} }
default:
_m.selectValues.Set(columns[i], values[i])
} }
} }
return nil return nil
} }
// Value returns the ent.Value that was dynamically selected and assigned to the User.
// This includes values selected through modifiers, order, etc.
func (_m *User) Value(name string) (ent.Value, error) {
return _m.selectValues.Get(name)
}
// QueryOwner queries the "owner" edge of the User entity. // QueryOwner queries the "owner" edge of the User entity.
func (u *User) QueryOwner() *PasswordTokenQuery { func (_m *User) QueryOwner() *PasswordTokenQuery {
return (&UserClient{config: u.config}).QueryOwner(u) return NewUserClient(_m.config).QueryOwner(_m)
} }
// Update returns a builder for updating this User. // Update returns a builder for updating this User.
// Note that you need to call User.Unwrap() before calling this method if this User // Note that you need to call User.Unwrap() before calling this method if this User
// was returned from a transaction, and the transaction was committed or rolled back. // was returned from a transaction, and the transaction was committed or rolled back.
func (u *User) Update() *UserUpdateOne { func (_m *User) Update() *UserUpdateOne {
return (&UserClient{config: u.config}).UpdateOne(u) return NewUserClient(_m.config).UpdateOne(_m)
} }
// Unwrap unwraps the User entity that was returned from a transaction after it was closed, // Unwrap unwraps the User 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. // so that all future queries will be executed through the driver which created the transaction.
func (u *User) Unwrap() *User { func (_m *User) Unwrap() *User {
tx, ok := u.config.driver.(*txDriver) _tx, ok := _m.config.driver.(*txDriver)
if !ok { if !ok {
panic("ent: User is not a transactional entity") panic("ent: User is not a transactional entity")
} }
u.config.driver = tx.drv _m.config.driver = _tx.drv
return u return _m
} }
// String implements the fmt.Stringer. // String implements the fmt.Stringer.
func (u *User) String() string { func (_m *User) String() string {
var builder strings.Builder var builder strings.Builder
builder.WriteString("User(") builder.WriteString("User(")
builder.WriteString(fmt.Sprintf("id=%v", u.ID)) builder.WriteString(fmt.Sprintf("id=%v, ", _m.ID))
builder.WriteString(", name=") builder.WriteString("name=")
builder.WriteString(u.Name) builder.WriteString(_m.Name)
builder.WriteString(", email=") builder.WriteString(", ")
builder.WriteString(u.Email) builder.WriteString("email=")
builder.WriteString(", password=<sensitive>") builder.WriteString(_m.Email)
builder.WriteString(", created_at=") builder.WriteString(", ")
builder.WriteString(u.CreatedAt.Format(time.ANSIC)) builder.WriteString("password=<sensitive>")
builder.WriteString(", ")
builder.WriteString("verified=")
builder.WriteString(fmt.Sprintf("%v", _m.Verified))
builder.WriteString(", ")
builder.WriteString("admin=")
builder.WriteString(fmt.Sprintf("%v", _m.Admin))
builder.WriteString(", ")
builder.WriteString("created_at=")
builder.WriteString(_m.CreatedAt.Format(time.ANSIC))
builder.WriteByte(')') builder.WriteByte(')')
return builder.String() return builder.String()
} }
// Users is a parsable slice of User. // Users is a parsable slice of User.
type Users []*User type Users []*User
func (u Users) config(cfg config) {
for _i := range u {
u[_i].config = cfg
}
}

View file

@ -1,4 +1,4 @@
// Code generated by entc, DO NOT EDIT. // Code generated by ent, DO NOT EDIT.
package user package user
@ -6,6 +6,8 @@ import (
"time" "time"
"entgo.io/ent" "entgo.io/ent"
"entgo.io/ent/dialect/sql"
"entgo.io/ent/dialect/sql/sqlgraph"
) )
const ( const (
@ -19,6 +21,10 @@ const (
FieldEmail = "email" FieldEmail = "email"
// FieldPassword holds the string denoting the password field in the database. // FieldPassword holds the string denoting the password field in the database.
FieldPassword = "password" FieldPassword = "password"
// FieldVerified holds the string denoting the verified field in the database.
FieldVerified = "verified"
// FieldAdmin holds the string denoting the admin field in the database.
FieldAdmin = "admin"
// FieldCreatedAt holds the string denoting the created_at field in the database. // FieldCreatedAt holds the string denoting the created_at field in the database.
FieldCreatedAt = "created_at" FieldCreatedAt = "created_at"
// EdgeOwner holds the string denoting the owner edge name in mutations. // EdgeOwner holds the string denoting the owner edge name in mutations.
@ -31,7 +37,7 @@ const (
// It exists in this package in order to avoid circular dependency with the "passwordtoken" package. // It exists in this package in order to avoid circular dependency with the "passwordtoken" package.
OwnerInverseTable = "password_tokens" OwnerInverseTable = "password_tokens"
// OwnerColumn is the table column denoting the owner relation/edge. // OwnerColumn is the table column denoting the owner relation/edge.
OwnerColumn = "password_token_user" OwnerColumn = "user_id"
) )
// Columns holds all SQL columns for user fields. // Columns holds all SQL columns for user fields.
@ -40,6 +46,8 @@ var Columns = []string{
FieldName, FieldName,
FieldEmail, FieldEmail,
FieldPassword, FieldPassword,
FieldVerified,
FieldAdmin,
FieldCreatedAt, FieldCreatedAt,
} }
@ -57,8 +65,7 @@ func ValidColumn(column string) bool {
// package on the initialization of the application. Therefore, // package on the initialization of the application. Therefore,
// it should be imported in the main as follows: // it should be imported in the main as follows:
// //
// import _ "goweb/ent/runtime" // import _ "github.com/mikestefanello/pagoda/ent/runtime"
//
var ( var (
Hooks [1]ent.Hook Hooks [1]ent.Hook
// NameValidator is a validator for the "name" field. It is called by the builders before save. // NameValidator is a validator for the "name" field. It is called by the builders before save.
@ -67,6 +74,69 @@ var (
EmailValidator func(string) error EmailValidator func(string) error
// PasswordValidator is a validator for the "password" field. It is called by the builders before save. // PasswordValidator is a validator for the "password" field. It is called by the builders before save.
PasswordValidator func(string) error PasswordValidator func(string) error
// DefaultVerified holds the default value on creation for the "verified" field.
DefaultVerified bool
// DefaultAdmin holds the default value on creation for the "admin" field.
DefaultAdmin bool
// DefaultCreatedAt holds the default value on creation for the "created_at" field. // DefaultCreatedAt holds the default value on creation for the "created_at" field.
DefaultCreatedAt func() time.Time DefaultCreatedAt func() time.Time
) )
// OrderOption defines the ordering options for the User queries.
type OrderOption func(*sql.Selector)
// ByID orders the results by the id field.
func ByID(opts ...sql.OrderTermOption) OrderOption {
return sql.OrderByField(FieldID, opts...).ToFunc()
}
// ByName orders the results by the name field.
func ByName(opts ...sql.OrderTermOption) OrderOption {
return sql.OrderByField(FieldName, opts...).ToFunc()
}
// ByEmail orders the results by the email field.
func ByEmail(opts ...sql.OrderTermOption) OrderOption {
return sql.OrderByField(FieldEmail, opts...).ToFunc()
}
// ByPassword orders the results by the password field.
func ByPassword(opts ...sql.OrderTermOption) OrderOption {
return sql.OrderByField(FieldPassword, opts...).ToFunc()
}
// ByVerified orders the results by the verified field.
func ByVerified(opts ...sql.OrderTermOption) OrderOption {
return sql.OrderByField(FieldVerified, opts...).ToFunc()
}
// ByAdmin orders the results by the admin field.
func ByAdmin(opts ...sql.OrderTermOption) OrderOption {
return sql.OrderByField(FieldAdmin, opts...).ToFunc()
}
// ByCreatedAt orders the results by the created_at field.
func ByCreatedAt(opts ...sql.OrderTermOption) OrderOption {
return sql.OrderByField(FieldCreatedAt, opts...).ToFunc()
}
// ByOwnerCount orders the results by owner count.
func ByOwnerCount(opts ...sql.OrderTermOption) OrderOption {
return func(s *sql.Selector) {
sqlgraph.OrderByNeighborsCount(s, newOwnerStep(), opts...)
}
}
// ByOwner orders the results by owner terms.
func ByOwner(term sql.OrderTerm, terms ...sql.OrderTerm) OrderOption {
return func(s *sql.Selector) {
sqlgraph.OrderByNeighborTerms(s, newOwnerStep(), append([]sql.OrderTerm{term}, terms...)...)
}
}
func newOwnerStep() *sqlgraph.Step {
return sqlgraph.NewStep(
sqlgraph.From(Table, FieldID),
sqlgraph.To(OwnerInverseTable, FieldID),
sqlgraph.Edge(sqlgraph.O2M, true, OwnerTable, OwnerColumn),
)
}

View file

@ -1,533 +1,343 @@
// Code generated by entc, DO NOT EDIT. // Code generated by ent, DO NOT EDIT.
package user package user
import ( import (
"goweb/ent/predicate"
"time" "time"
"entgo.io/ent/dialect/sql" "entgo.io/ent/dialect/sql"
"entgo.io/ent/dialect/sql/sqlgraph" "entgo.io/ent/dialect/sql/sqlgraph"
"github.com/mikestefanello/pagoda/ent/predicate"
) )
// ID filters vertices based on their ID field. // ID filters vertices based on their ID field.
func ID(id int) predicate.User { func ID(id int) predicate.User {
return predicate.User(func(s *sql.Selector) { return predicate.User(sql.FieldEQ(FieldID, id))
s.Where(sql.EQ(s.C(FieldID), id))
})
} }
// IDEQ applies the EQ predicate on the ID field. // IDEQ applies the EQ predicate on the ID field.
func IDEQ(id int) predicate.User { func IDEQ(id int) predicate.User {
return predicate.User(func(s *sql.Selector) { return predicate.User(sql.FieldEQ(FieldID, id))
s.Where(sql.EQ(s.C(FieldID), id))
})
} }
// IDNEQ applies the NEQ predicate on the ID field. // IDNEQ applies the NEQ predicate on the ID field.
func IDNEQ(id int) predicate.User { func IDNEQ(id int) predicate.User {
return predicate.User(func(s *sql.Selector) { return predicate.User(sql.FieldNEQ(FieldID, id))
s.Where(sql.NEQ(s.C(FieldID), id))
})
} }
// IDIn applies the In predicate on the ID field. // IDIn applies the In predicate on the ID field.
func IDIn(ids ...int) predicate.User { func IDIn(ids ...int) predicate.User {
return predicate.User(func(s *sql.Selector) { return predicate.User(sql.FieldIn(FieldID, ids...))
// if not arguments were provided, append the FALSE constants,
// since we can't apply "IN ()". This will make this predicate falsy.
if len(ids) == 0 {
s.Where(sql.False())
return
}
v := make([]interface{}, len(ids))
for i := range v {
v[i] = ids[i]
}
s.Where(sql.In(s.C(FieldID), v...))
})
} }
// IDNotIn applies the NotIn predicate on the ID field. // IDNotIn applies the NotIn predicate on the ID field.
func IDNotIn(ids ...int) predicate.User { func IDNotIn(ids ...int) predicate.User {
return predicate.User(func(s *sql.Selector) { return predicate.User(sql.FieldNotIn(FieldID, ids...))
// if not arguments were provided, append the FALSE constants,
// since we can't apply "IN ()". This will make this predicate falsy.
if len(ids) == 0 {
s.Where(sql.False())
return
}
v := make([]interface{}, len(ids))
for i := range v {
v[i] = ids[i]
}
s.Where(sql.NotIn(s.C(FieldID), v...))
})
} }
// IDGT applies the GT predicate on the ID field. // IDGT applies the GT predicate on the ID field.
func IDGT(id int) predicate.User { func IDGT(id int) predicate.User {
return predicate.User(func(s *sql.Selector) { return predicate.User(sql.FieldGT(FieldID, id))
s.Where(sql.GT(s.C(FieldID), id))
})
} }
// IDGTE applies the GTE predicate on the ID field. // IDGTE applies the GTE predicate on the ID field.
func IDGTE(id int) predicate.User { func IDGTE(id int) predicate.User {
return predicate.User(func(s *sql.Selector) { return predicate.User(sql.FieldGTE(FieldID, id))
s.Where(sql.GTE(s.C(FieldID), id))
})
} }
// IDLT applies the LT predicate on the ID field. // IDLT applies the LT predicate on the ID field.
func IDLT(id int) predicate.User { func IDLT(id int) predicate.User {
return predicate.User(func(s *sql.Selector) { return predicate.User(sql.FieldLT(FieldID, id))
s.Where(sql.LT(s.C(FieldID), id))
})
} }
// IDLTE applies the LTE predicate on the ID field. // IDLTE applies the LTE predicate on the ID field.
func IDLTE(id int) predicate.User { func IDLTE(id int) predicate.User {
return predicate.User(func(s *sql.Selector) { return predicate.User(sql.FieldLTE(FieldID, id))
s.Where(sql.LTE(s.C(FieldID), id))
})
} }
// Name applies equality check predicate on the "name" field. It's identical to NameEQ. // Name applies equality check predicate on the "name" field. It's identical to NameEQ.
func Name(v string) predicate.User { func Name(v string) predicate.User {
return predicate.User(func(s *sql.Selector) { return predicate.User(sql.FieldEQ(FieldName, v))
s.Where(sql.EQ(s.C(FieldName), v))
})
} }
// Email applies equality check predicate on the "email" field. It's identical to EmailEQ. // Email applies equality check predicate on the "email" field. It's identical to EmailEQ.
func Email(v string) predicate.User { func Email(v string) predicate.User {
return predicate.User(func(s *sql.Selector) { return predicate.User(sql.FieldEQ(FieldEmail, v))
s.Where(sql.EQ(s.C(FieldEmail), v))
})
} }
// Password applies equality check predicate on the "password" field. It's identical to PasswordEQ. // Password applies equality check predicate on the "password" field. It's identical to PasswordEQ.
func Password(v string) predicate.User { func Password(v string) predicate.User {
return predicate.User(func(s *sql.Selector) { return predicate.User(sql.FieldEQ(FieldPassword, v))
s.Where(sql.EQ(s.C(FieldPassword), v)) }
})
// Verified applies equality check predicate on the "verified" field. It's identical to VerifiedEQ.
func Verified(v bool) predicate.User {
return predicate.User(sql.FieldEQ(FieldVerified, v))
}
// Admin applies equality check predicate on the "admin" field. It's identical to AdminEQ.
func Admin(v bool) predicate.User {
return predicate.User(sql.FieldEQ(FieldAdmin, v))
} }
// CreatedAt applies equality check predicate on the "created_at" field. It's identical to CreatedAtEQ. // CreatedAt applies equality check predicate on the "created_at" field. It's identical to CreatedAtEQ.
func CreatedAt(v time.Time) predicate.User { func CreatedAt(v time.Time) predicate.User {
return predicate.User(func(s *sql.Selector) { return predicate.User(sql.FieldEQ(FieldCreatedAt, v))
s.Where(sql.EQ(s.C(FieldCreatedAt), v))
})
} }
// NameEQ applies the EQ predicate on the "name" field. // NameEQ applies the EQ predicate on the "name" field.
func NameEQ(v string) predicate.User { func NameEQ(v string) predicate.User {
return predicate.User(func(s *sql.Selector) { return predicate.User(sql.FieldEQ(FieldName, v))
s.Where(sql.EQ(s.C(FieldName), v))
})
} }
// NameNEQ applies the NEQ predicate on the "name" field. // NameNEQ applies the NEQ predicate on the "name" field.
func NameNEQ(v string) predicate.User { func NameNEQ(v string) predicate.User {
return predicate.User(func(s *sql.Selector) { return predicate.User(sql.FieldNEQ(FieldName, v))
s.Where(sql.NEQ(s.C(FieldName), v))
})
} }
// NameIn applies the In predicate on the "name" field. // NameIn applies the In predicate on the "name" field.
func NameIn(vs ...string) predicate.User { func NameIn(vs ...string) predicate.User {
v := make([]interface{}, len(vs)) return predicate.User(sql.FieldIn(FieldName, vs...))
for i := range v {
v[i] = vs[i]
}
return predicate.User(func(s *sql.Selector) {
// if not arguments were provided, append the FALSE constants,
// since we can't apply "IN ()". This will make this predicate falsy.
if len(v) == 0 {
s.Where(sql.False())
return
}
s.Where(sql.In(s.C(FieldName), v...))
})
} }
// NameNotIn applies the NotIn predicate on the "name" field. // NameNotIn applies the NotIn predicate on the "name" field.
func NameNotIn(vs ...string) predicate.User { func NameNotIn(vs ...string) predicate.User {
v := make([]interface{}, len(vs)) return predicate.User(sql.FieldNotIn(FieldName, vs...))
for i := range v {
v[i] = vs[i]
}
return predicate.User(func(s *sql.Selector) {
// if not arguments were provided, append the FALSE constants,
// since we can't apply "IN ()". This will make this predicate falsy.
if len(v) == 0 {
s.Where(sql.False())
return
}
s.Where(sql.NotIn(s.C(FieldName), v...))
})
} }
// NameGT applies the GT predicate on the "name" field. // NameGT applies the GT predicate on the "name" field.
func NameGT(v string) predicate.User { func NameGT(v string) predicate.User {
return predicate.User(func(s *sql.Selector) { return predicate.User(sql.FieldGT(FieldName, v))
s.Where(sql.GT(s.C(FieldName), v))
})
} }
// NameGTE applies the GTE predicate on the "name" field. // NameGTE applies the GTE predicate on the "name" field.
func NameGTE(v string) predicate.User { func NameGTE(v string) predicate.User {
return predicate.User(func(s *sql.Selector) { return predicate.User(sql.FieldGTE(FieldName, v))
s.Where(sql.GTE(s.C(FieldName), v))
})
} }
// NameLT applies the LT predicate on the "name" field. // NameLT applies the LT predicate on the "name" field.
func NameLT(v string) predicate.User { func NameLT(v string) predicate.User {
return predicate.User(func(s *sql.Selector) { return predicate.User(sql.FieldLT(FieldName, v))
s.Where(sql.LT(s.C(FieldName), v))
})
} }
// NameLTE applies the LTE predicate on the "name" field. // NameLTE applies the LTE predicate on the "name" field.
func NameLTE(v string) predicate.User { func NameLTE(v string) predicate.User {
return predicate.User(func(s *sql.Selector) { return predicate.User(sql.FieldLTE(FieldName, v))
s.Where(sql.LTE(s.C(FieldName), v))
})
} }
// NameContains applies the Contains predicate on the "name" field. // NameContains applies the Contains predicate on the "name" field.
func NameContains(v string) predicate.User { func NameContains(v string) predicate.User {
return predicate.User(func(s *sql.Selector) { return predicate.User(sql.FieldContains(FieldName, v))
s.Where(sql.Contains(s.C(FieldName), v))
})
} }
// NameHasPrefix applies the HasPrefix predicate on the "name" field. // NameHasPrefix applies the HasPrefix predicate on the "name" field.
func NameHasPrefix(v string) predicate.User { func NameHasPrefix(v string) predicate.User {
return predicate.User(func(s *sql.Selector) { return predicate.User(sql.FieldHasPrefix(FieldName, v))
s.Where(sql.HasPrefix(s.C(FieldName), v))
})
} }
// NameHasSuffix applies the HasSuffix predicate on the "name" field. // NameHasSuffix applies the HasSuffix predicate on the "name" field.
func NameHasSuffix(v string) predicate.User { func NameHasSuffix(v string) predicate.User {
return predicate.User(func(s *sql.Selector) { return predicate.User(sql.FieldHasSuffix(FieldName, v))
s.Where(sql.HasSuffix(s.C(FieldName), v))
})
} }
// NameEqualFold applies the EqualFold predicate on the "name" field. // NameEqualFold applies the EqualFold predicate on the "name" field.
func NameEqualFold(v string) predicate.User { func NameEqualFold(v string) predicate.User {
return predicate.User(func(s *sql.Selector) { return predicate.User(sql.FieldEqualFold(FieldName, v))
s.Where(sql.EqualFold(s.C(FieldName), v))
})
} }
// NameContainsFold applies the ContainsFold predicate on the "name" field. // NameContainsFold applies the ContainsFold predicate on the "name" field.
func NameContainsFold(v string) predicate.User { func NameContainsFold(v string) predicate.User {
return predicate.User(func(s *sql.Selector) { return predicate.User(sql.FieldContainsFold(FieldName, v))
s.Where(sql.ContainsFold(s.C(FieldName), v))
})
} }
// EmailEQ applies the EQ predicate on the "email" field. // EmailEQ applies the EQ predicate on the "email" field.
func EmailEQ(v string) predicate.User { func EmailEQ(v string) predicate.User {
return predicate.User(func(s *sql.Selector) { return predicate.User(sql.FieldEQ(FieldEmail, v))
s.Where(sql.EQ(s.C(FieldEmail), v))
})
} }
// EmailNEQ applies the NEQ predicate on the "email" field. // EmailNEQ applies the NEQ predicate on the "email" field.
func EmailNEQ(v string) predicate.User { func EmailNEQ(v string) predicate.User {
return predicate.User(func(s *sql.Selector) { return predicate.User(sql.FieldNEQ(FieldEmail, v))
s.Where(sql.NEQ(s.C(FieldEmail), v))
})
} }
// EmailIn applies the In predicate on the "email" field. // EmailIn applies the In predicate on the "email" field.
func EmailIn(vs ...string) predicate.User { func EmailIn(vs ...string) predicate.User {
v := make([]interface{}, len(vs)) return predicate.User(sql.FieldIn(FieldEmail, vs...))
for i := range v {
v[i] = vs[i]
}
return predicate.User(func(s *sql.Selector) {
// if not arguments were provided, append the FALSE constants,
// since we can't apply "IN ()". This will make this predicate falsy.
if len(v) == 0 {
s.Where(sql.False())
return
}
s.Where(sql.In(s.C(FieldEmail), v...))
})
} }
// EmailNotIn applies the NotIn predicate on the "email" field. // EmailNotIn applies the NotIn predicate on the "email" field.
func EmailNotIn(vs ...string) predicate.User { func EmailNotIn(vs ...string) predicate.User {
v := make([]interface{}, len(vs)) return predicate.User(sql.FieldNotIn(FieldEmail, vs...))
for i := range v {
v[i] = vs[i]
}
return predicate.User(func(s *sql.Selector) {
// if not arguments were provided, append the FALSE constants,
// since we can't apply "IN ()". This will make this predicate falsy.
if len(v) == 0 {
s.Where(sql.False())
return
}
s.Where(sql.NotIn(s.C(FieldEmail), v...))
})
} }
// EmailGT applies the GT predicate on the "email" field. // EmailGT applies the GT predicate on the "email" field.
func EmailGT(v string) predicate.User { func EmailGT(v string) predicate.User {
return predicate.User(func(s *sql.Selector) { return predicate.User(sql.FieldGT(FieldEmail, v))
s.Where(sql.GT(s.C(FieldEmail), v))
})
} }
// EmailGTE applies the GTE predicate on the "email" field. // EmailGTE applies the GTE predicate on the "email" field.
func EmailGTE(v string) predicate.User { func EmailGTE(v string) predicate.User {
return predicate.User(func(s *sql.Selector) { return predicate.User(sql.FieldGTE(FieldEmail, v))
s.Where(sql.GTE(s.C(FieldEmail), v))
})
} }
// EmailLT applies the LT predicate on the "email" field. // EmailLT applies the LT predicate on the "email" field.
func EmailLT(v string) predicate.User { func EmailLT(v string) predicate.User {
return predicate.User(func(s *sql.Selector) { return predicate.User(sql.FieldLT(FieldEmail, v))
s.Where(sql.LT(s.C(FieldEmail), v))
})
} }
// EmailLTE applies the LTE predicate on the "email" field. // EmailLTE applies the LTE predicate on the "email" field.
func EmailLTE(v string) predicate.User { func EmailLTE(v string) predicate.User {
return predicate.User(func(s *sql.Selector) { return predicate.User(sql.FieldLTE(FieldEmail, v))
s.Where(sql.LTE(s.C(FieldEmail), v))
})
} }
// EmailContains applies the Contains predicate on the "email" field. // EmailContains applies the Contains predicate on the "email" field.
func EmailContains(v string) predicate.User { func EmailContains(v string) predicate.User {
return predicate.User(func(s *sql.Selector) { return predicate.User(sql.FieldContains(FieldEmail, v))
s.Where(sql.Contains(s.C(FieldEmail), v))
})
} }
// EmailHasPrefix applies the HasPrefix predicate on the "email" field. // EmailHasPrefix applies the HasPrefix predicate on the "email" field.
func EmailHasPrefix(v string) predicate.User { func EmailHasPrefix(v string) predicate.User {
return predicate.User(func(s *sql.Selector) { return predicate.User(sql.FieldHasPrefix(FieldEmail, v))
s.Where(sql.HasPrefix(s.C(FieldEmail), v))
})
} }
// EmailHasSuffix applies the HasSuffix predicate on the "email" field. // EmailHasSuffix applies the HasSuffix predicate on the "email" field.
func EmailHasSuffix(v string) predicate.User { func EmailHasSuffix(v string) predicate.User {
return predicate.User(func(s *sql.Selector) { return predicate.User(sql.FieldHasSuffix(FieldEmail, v))
s.Where(sql.HasSuffix(s.C(FieldEmail), v))
})
} }
// EmailEqualFold applies the EqualFold predicate on the "email" field. // EmailEqualFold applies the EqualFold predicate on the "email" field.
func EmailEqualFold(v string) predicate.User { func EmailEqualFold(v string) predicate.User {
return predicate.User(func(s *sql.Selector) { return predicate.User(sql.FieldEqualFold(FieldEmail, v))
s.Where(sql.EqualFold(s.C(FieldEmail), v))
})
} }
// EmailContainsFold applies the ContainsFold predicate on the "email" field. // EmailContainsFold applies the ContainsFold predicate on the "email" field.
func EmailContainsFold(v string) predicate.User { func EmailContainsFold(v string) predicate.User {
return predicate.User(func(s *sql.Selector) { return predicate.User(sql.FieldContainsFold(FieldEmail, v))
s.Where(sql.ContainsFold(s.C(FieldEmail), v))
})
} }
// PasswordEQ applies the EQ predicate on the "password" field. // PasswordEQ applies the EQ predicate on the "password" field.
func PasswordEQ(v string) predicate.User { func PasswordEQ(v string) predicate.User {
return predicate.User(func(s *sql.Selector) { return predicate.User(sql.FieldEQ(FieldPassword, v))
s.Where(sql.EQ(s.C(FieldPassword), v))
})
} }
// PasswordNEQ applies the NEQ predicate on the "password" field. // PasswordNEQ applies the NEQ predicate on the "password" field.
func PasswordNEQ(v string) predicate.User { func PasswordNEQ(v string) predicate.User {
return predicate.User(func(s *sql.Selector) { return predicate.User(sql.FieldNEQ(FieldPassword, v))
s.Where(sql.NEQ(s.C(FieldPassword), v))
})
} }
// PasswordIn applies the In predicate on the "password" field. // PasswordIn applies the In predicate on the "password" field.
func PasswordIn(vs ...string) predicate.User { func PasswordIn(vs ...string) predicate.User {
v := make([]interface{}, len(vs)) return predicate.User(sql.FieldIn(FieldPassword, vs...))
for i := range v {
v[i] = vs[i]
}
return predicate.User(func(s *sql.Selector) {
// if not arguments were provided, append the FALSE constants,
// since we can't apply "IN ()". This will make this predicate falsy.
if len(v) == 0 {
s.Where(sql.False())
return
}
s.Where(sql.In(s.C(FieldPassword), v...))
})
} }
// PasswordNotIn applies the NotIn predicate on the "password" field. // PasswordNotIn applies the NotIn predicate on the "password" field.
func PasswordNotIn(vs ...string) predicate.User { func PasswordNotIn(vs ...string) predicate.User {
v := make([]interface{}, len(vs)) return predicate.User(sql.FieldNotIn(FieldPassword, vs...))
for i := range v {
v[i] = vs[i]
}
return predicate.User(func(s *sql.Selector) {
// if not arguments were provided, append the FALSE constants,
// since we can't apply "IN ()". This will make this predicate falsy.
if len(v) == 0 {
s.Where(sql.False())
return
}
s.Where(sql.NotIn(s.C(FieldPassword), v...))
})
} }
// PasswordGT applies the GT predicate on the "password" field. // PasswordGT applies the GT predicate on the "password" field.
func PasswordGT(v string) predicate.User { func PasswordGT(v string) predicate.User {
return predicate.User(func(s *sql.Selector) { return predicate.User(sql.FieldGT(FieldPassword, v))
s.Where(sql.GT(s.C(FieldPassword), v))
})
} }
// PasswordGTE applies the GTE predicate on the "password" field. // PasswordGTE applies the GTE predicate on the "password" field.
func PasswordGTE(v string) predicate.User { func PasswordGTE(v string) predicate.User {
return predicate.User(func(s *sql.Selector) { return predicate.User(sql.FieldGTE(FieldPassword, v))
s.Where(sql.GTE(s.C(FieldPassword), v))
})
} }
// PasswordLT applies the LT predicate on the "password" field. // PasswordLT applies the LT predicate on the "password" field.
func PasswordLT(v string) predicate.User { func PasswordLT(v string) predicate.User {
return predicate.User(func(s *sql.Selector) { return predicate.User(sql.FieldLT(FieldPassword, v))
s.Where(sql.LT(s.C(FieldPassword), v))
})
} }
// PasswordLTE applies the LTE predicate on the "password" field. // PasswordLTE applies the LTE predicate on the "password" field.
func PasswordLTE(v string) predicate.User { func PasswordLTE(v string) predicate.User {
return predicate.User(func(s *sql.Selector) { return predicate.User(sql.FieldLTE(FieldPassword, v))
s.Where(sql.LTE(s.C(FieldPassword), v))
})
} }
// PasswordContains applies the Contains predicate on the "password" field. // PasswordContains applies the Contains predicate on the "password" field.
func PasswordContains(v string) predicate.User { func PasswordContains(v string) predicate.User {
return predicate.User(func(s *sql.Selector) { return predicate.User(sql.FieldContains(FieldPassword, v))
s.Where(sql.Contains(s.C(FieldPassword), v))
})
} }
// PasswordHasPrefix applies the HasPrefix predicate on the "password" field. // PasswordHasPrefix applies the HasPrefix predicate on the "password" field.
func PasswordHasPrefix(v string) predicate.User { func PasswordHasPrefix(v string) predicate.User {
return predicate.User(func(s *sql.Selector) { return predicate.User(sql.FieldHasPrefix(FieldPassword, v))
s.Where(sql.HasPrefix(s.C(FieldPassword), v))
})
} }
// PasswordHasSuffix applies the HasSuffix predicate on the "password" field. // PasswordHasSuffix applies the HasSuffix predicate on the "password" field.
func PasswordHasSuffix(v string) predicate.User { func PasswordHasSuffix(v string) predicate.User {
return predicate.User(func(s *sql.Selector) { return predicate.User(sql.FieldHasSuffix(FieldPassword, v))
s.Where(sql.HasSuffix(s.C(FieldPassword), v))
})
} }
// PasswordEqualFold applies the EqualFold predicate on the "password" field. // PasswordEqualFold applies the EqualFold predicate on the "password" field.
func PasswordEqualFold(v string) predicate.User { func PasswordEqualFold(v string) predicate.User {
return predicate.User(func(s *sql.Selector) { return predicate.User(sql.FieldEqualFold(FieldPassword, v))
s.Where(sql.EqualFold(s.C(FieldPassword), v))
})
} }
// PasswordContainsFold applies the ContainsFold predicate on the "password" field. // PasswordContainsFold applies the ContainsFold predicate on the "password" field.
func PasswordContainsFold(v string) predicate.User { func PasswordContainsFold(v string) predicate.User {
return predicate.User(func(s *sql.Selector) { return predicate.User(sql.FieldContainsFold(FieldPassword, v))
s.Where(sql.ContainsFold(s.C(FieldPassword), v)) }
})
// VerifiedEQ applies the EQ predicate on the "verified" field.
func VerifiedEQ(v bool) predicate.User {
return predicate.User(sql.FieldEQ(FieldVerified, v))
}
// VerifiedNEQ applies the NEQ predicate on the "verified" field.
func VerifiedNEQ(v bool) predicate.User {
return predicate.User(sql.FieldNEQ(FieldVerified, v))
}
// AdminEQ applies the EQ predicate on the "admin" field.
func AdminEQ(v bool) predicate.User {
return predicate.User(sql.FieldEQ(FieldAdmin, v))
}
// AdminNEQ applies the NEQ predicate on the "admin" field.
func AdminNEQ(v bool) predicate.User {
return predicate.User(sql.FieldNEQ(FieldAdmin, v))
} }
// CreatedAtEQ applies the EQ predicate on the "created_at" field. // CreatedAtEQ applies the EQ predicate on the "created_at" field.
func CreatedAtEQ(v time.Time) predicate.User { func CreatedAtEQ(v time.Time) predicate.User {
return predicate.User(func(s *sql.Selector) { return predicate.User(sql.FieldEQ(FieldCreatedAt, v))
s.Where(sql.EQ(s.C(FieldCreatedAt), v))
})
} }
// CreatedAtNEQ applies the NEQ predicate on the "created_at" field. // CreatedAtNEQ applies the NEQ predicate on the "created_at" field.
func CreatedAtNEQ(v time.Time) predicate.User { func CreatedAtNEQ(v time.Time) predicate.User {
return predicate.User(func(s *sql.Selector) { return predicate.User(sql.FieldNEQ(FieldCreatedAt, v))
s.Where(sql.NEQ(s.C(FieldCreatedAt), v))
})
} }
// CreatedAtIn applies the In predicate on the "created_at" field. // CreatedAtIn applies the In predicate on the "created_at" field.
func CreatedAtIn(vs ...time.Time) predicate.User { func CreatedAtIn(vs ...time.Time) predicate.User {
v := make([]interface{}, len(vs)) return predicate.User(sql.FieldIn(FieldCreatedAt, vs...))
for i := range v {
v[i] = vs[i]
}
return predicate.User(func(s *sql.Selector) {
// if not arguments were provided, append the FALSE constants,
// since we can't apply "IN ()". This will make this predicate falsy.
if len(v) == 0 {
s.Where(sql.False())
return
}
s.Where(sql.In(s.C(FieldCreatedAt), v...))
})
} }
// CreatedAtNotIn applies the NotIn predicate on the "created_at" field. // CreatedAtNotIn applies the NotIn predicate on the "created_at" field.
func CreatedAtNotIn(vs ...time.Time) predicate.User { func CreatedAtNotIn(vs ...time.Time) predicate.User {
v := make([]interface{}, len(vs)) return predicate.User(sql.FieldNotIn(FieldCreatedAt, vs...))
for i := range v {
v[i] = vs[i]
}
return predicate.User(func(s *sql.Selector) {
// if not arguments were provided, append the FALSE constants,
// since we can't apply "IN ()". This will make this predicate falsy.
if len(v) == 0 {
s.Where(sql.False())
return
}
s.Where(sql.NotIn(s.C(FieldCreatedAt), v...))
})
} }
// CreatedAtGT applies the GT predicate on the "created_at" field. // CreatedAtGT applies the GT predicate on the "created_at" field.
func CreatedAtGT(v time.Time) predicate.User { func CreatedAtGT(v time.Time) predicate.User {
return predicate.User(func(s *sql.Selector) { return predicate.User(sql.FieldGT(FieldCreatedAt, v))
s.Where(sql.GT(s.C(FieldCreatedAt), v))
})
} }
// CreatedAtGTE applies the GTE predicate on the "created_at" field. // CreatedAtGTE applies the GTE predicate on the "created_at" field.
func CreatedAtGTE(v time.Time) predicate.User { func CreatedAtGTE(v time.Time) predicate.User {
return predicate.User(func(s *sql.Selector) { return predicate.User(sql.FieldGTE(FieldCreatedAt, v))
s.Where(sql.GTE(s.C(FieldCreatedAt), v))
})
} }
// CreatedAtLT applies the LT predicate on the "created_at" field. // CreatedAtLT applies the LT predicate on the "created_at" field.
func CreatedAtLT(v time.Time) predicate.User { func CreatedAtLT(v time.Time) predicate.User {
return predicate.User(func(s *sql.Selector) { return predicate.User(sql.FieldLT(FieldCreatedAt, v))
s.Where(sql.LT(s.C(FieldCreatedAt), v))
})
} }
// CreatedAtLTE applies the LTE predicate on the "created_at" field. // CreatedAtLTE applies the LTE predicate on the "created_at" field.
func CreatedAtLTE(v time.Time) predicate.User { func CreatedAtLTE(v time.Time) predicate.User {
return predicate.User(func(s *sql.Selector) { return predicate.User(sql.FieldLTE(FieldCreatedAt, v))
s.Where(sql.LTE(s.C(FieldCreatedAt), v))
})
} }
// HasOwner applies the HasEdge predicate on the "owner" edge. // HasOwner applies the HasEdge predicate on the "owner" edge.
@ -535,7 +345,6 @@ func HasOwner() predicate.User {
return predicate.User(func(s *sql.Selector) { return predicate.User(func(s *sql.Selector) {
step := sqlgraph.NewStep( step := sqlgraph.NewStep(
sqlgraph.From(Table, FieldID), sqlgraph.From(Table, FieldID),
sqlgraph.To(OwnerTable, FieldID),
sqlgraph.Edge(sqlgraph.O2M, true, OwnerTable, OwnerColumn), sqlgraph.Edge(sqlgraph.O2M, true, OwnerTable, OwnerColumn),
) )
sqlgraph.HasNeighbors(s, step) sqlgraph.HasNeighbors(s, step)
@ -545,11 +354,7 @@ func HasOwner() predicate.User {
// HasOwnerWith applies the HasEdge predicate on the "owner" edge with a given conditions (other predicates). // HasOwnerWith applies the HasEdge predicate on the "owner" edge with a given conditions (other predicates).
func HasOwnerWith(preds ...predicate.PasswordToken) predicate.User { func HasOwnerWith(preds ...predicate.PasswordToken) predicate.User {
return predicate.User(func(s *sql.Selector) { return predicate.User(func(s *sql.Selector) {
step := sqlgraph.NewStep( step := newOwnerStep()
sqlgraph.From(Table, FieldID),
sqlgraph.To(OwnerInverseTable, FieldID),
sqlgraph.Edge(sqlgraph.O2M, true, OwnerTable, OwnerColumn),
)
sqlgraph.HasNeighborsWith(s, step, func(s *sql.Selector) { sqlgraph.HasNeighborsWith(s, step, func(s *sql.Selector) {
for _, p := range preds { for _, p := range preds {
p(s) p(s)
@ -560,32 +365,15 @@ func HasOwnerWith(preds ...predicate.PasswordToken) predicate.User {
// And groups predicates with the AND operator between them. // And groups predicates with the AND operator between them.
func And(predicates ...predicate.User) predicate.User { func And(predicates ...predicate.User) predicate.User {
return predicate.User(func(s *sql.Selector) { return predicate.User(sql.AndPredicates(predicates...))
s1 := s.Clone().SetP(nil)
for _, p := range predicates {
p(s1)
}
s.Where(s1.P())
})
} }
// Or groups predicates with the OR operator between them. // Or groups predicates with the OR operator between them.
func Or(predicates ...predicate.User) predicate.User { func Or(predicates ...predicate.User) predicate.User {
return predicate.User(func(s *sql.Selector) { return predicate.User(sql.OrPredicates(predicates...))
s1 := s.Clone().SetP(nil)
for i, p := range predicates {
if i > 0 {
s1.Or()
}
p(s1)
}
s.Where(s1.P())
})
} }
// Not applies the not operator on the given predicate. // Not applies the not operator on the given predicate.
func Not(p predicate.User) predicate.User { func Not(p predicate.User) predicate.User {
return predicate.User(func(s *sql.Selector) { return predicate.User(sql.NotPredicates(p))
p(s.Not())
})
} }

View file

@ -1,4 +1,4 @@
// Code generated by entc, DO NOT EDIT. // Code generated by ent, DO NOT EDIT.
package ent package ent
@ -6,12 +6,12 @@ import (
"context" "context"
"errors" "errors"
"fmt" "fmt"
"goweb/ent/passwordtoken"
"goweb/ent/user"
"time" "time"
"entgo.io/ent/dialect/sql/sqlgraph" "entgo.io/ent/dialect/sql/sqlgraph"
"entgo.io/ent/schema/field" "entgo.io/ent/schema/field"
"github.com/mikestefanello/pagoda/ent/passwordtoken"
"github.com/mikestefanello/pagoda/ent/user"
) )
// UserCreate is the builder for creating a User entity. // UserCreate is the builder for creating a User entity.
@ -22,104 +22,96 @@ type UserCreate struct {
} }
// SetName sets the "name" field. // SetName sets the "name" field.
func (uc *UserCreate) SetName(s string) *UserCreate { func (_c *UserCreate) SetName(v string) *UserCreate {
uc.mutation.SetName(s) _c.mutation.SetName(v)
return uc return _c
} }
// SetEmail sets the "email" field. // SetEmail sets the "email" field.
func (uc *UserCreate) SetEmail(s string) *UserCreate { func (_c *UserCreate) SetEmail(v string) *UserCreate {
uc.mutation.SetEmail(s) _c.mutation.SetEmail(v)
return uc return _c
} }
// SetPassword sets the "password" field. // SetPassword sets the "password" field.
func (uc *UserCreate) SetPassword(s string) *UserCreate { func (_c *UserCreate) SetPassword(v string) *UserCreate {
uc.mutation.SetPassword(s) _c.mutation.SetPassword(v)
return uc return _c
}
// SetVerified sets the "verified" field.
func (_c *UserCreate) SetVerified(v bool) *UserCreate {
_c.mutation.SetVerified(v)
return _c
}
// SetNillableVerified sets the "verified" field if the given value is not nil.
func (_c *UserCreate) SetNillableVerified(v *bool) *UserCreate {
if v != nil {
_c.SetVerified(*v)
}
return _c
}
// SetAdmin sets the "admin" field.
func (_c *UserCreate) SetAdmin(v bool) *UserCreate {
_c.mutation.SetAdmin(v)
return _c
}
// SetNillableAdmin sets the "admin" field if the given value is not nil.
func (_c *UserCreate) SetNillableAdmin(v *bool) *UserCreate {
if v != nil {
_c.SetAdmin(*v)
}
return _c
} }
// SetCreatedAt sets the "created_at" field. // SetCreatedAt sets the "created_at" field.
func (uc *UserCreate) SetCreatedAt(t time.Time) *UserCreate { func (_c *UserCreate) SetCreatedAt(v time.Time) *UserCreate {
uc.mutation.SetCreatedAt(t) _c.mutation.SetCreatedAt(v)
return uc return _c
} }
// SetNillableCreatedAt sets the "created_at" field if the given value is not nil. // SetNillableCreatedAt sets the "created_at" field if the given value is not nil.
func (uc *UserCreate) SetNillableCreatedAt(t *time.Time) *UserCreate { func (_c *UserCreate) SetNillableCreatedAt(v *time.Time) *UserCreate {
if t != nil { if v != nil {
uc.SetCreatedAt(*t) _c.SetCreatedAt(*v)
} }
return uc return _c
} }
// AddOwnerIDs adds the "owner" edge to the PasswordToken entity by IDs. // AddOwnerIDs adds the "owner" edge to the PasswordToken entity by IDs.
func (uc *UserCreate) AddOwnerIDs(ids ...int) *UserCreate { func (_c *UserCreate) AddOwnerIDs(ids ...int) *UserCreate {
uc.mutation.AddOwnerIDs(ids...) _c.mutation.AddOwnerIDs(ids...)
return uc return _c
} }
// AddOwner adds the "owner" edges to the PasswordToken entity. // AddOwner adds the "owner" edges to the PasswordToken entity.
func (uc *UserCreate) AddOwner(p ...*PasswordToken) *UserCreate { func (_c *UserCreate) AddOwner(v ...*PasswordToken) *UserCreate {
ids := make([]int, len(p)) ids := make([]int, len(v))
for i := range p { for i := range v {
ids[i] = p[i].ID ids[i] = v[i].ID
} }
return uc.AddOwnerIDs(ids...) return _c.AddOwnerIDs(ids...)
} }
// Mutation returns the UserMutation object of the builder. // Mutation returns the UserMutation object of the builder.
func (uc *UserCreate) Mutation() *UserMutation { func (_c *UserCreate) Mutation() *UserMutation {
return uc.mutation return _c.mutation
} }
// Save creates the User in the database. // Save creates the User in the database.
func (uc *UserCreate) Save(ctx context.Context) (*User, error) { func (_c *UserCreate) Save(ctx context.Context) (*User, error) {
var ( if err := _c.defaults(); err != nil {
err error
node *User
)
if err := uc.defaults(); err != nil {
return nil, err return nil, err
} }
if len(uc.hooks) == 0 { return withHooks(ctx, _c.sqlSave, _c.mutation, _c.hooks)
if err = uc.check(); err != nil {
return nil, err
}
node, err = uc.sqlSave(ctx)
} else {
var mut Mutator = MutateFunc(func(ctx context.Context, m Mutation) (Value, error) {
mutation, ok := m.(*UserMutation)
if !ok {
return nil, fmt.Errorf("unexpected mutation type %T", m)
}
if err = uc.check(); err != nil {
return nil, err
}
uc.mutation = mutation
if node, err = uc.sqlSave(ctx); err != nil {
return nil, err
}
mutation.id = &node.ID
mutation.done = true
return node, err
})
for i := len(uc.hooks) - 1; i >= 0; i-- {
if uc.hooks[i] == nil {
return nil, fmt.Errorf("ent: uninitialized hook (forgotten import ent/runtime?)")
}
mut = uc.hooks[i](mut)
}
if _, err := mut.Mutate(ctx, uc.mutation); err != nil {
return nil, err
}
}
return node, err
} }
// SaveX calls Save and panics if Save returns an error. // SaveX calls Save and panics if Save returns an error.
func (uc *UserCreate) SaveX(ctx context.Context) *User { func (_c *UserCreate) SaveX(ctx context.Context) *User {
v, err := uc.Save(ctx) v, err := _c.Save(ctx)
if err != nil { if err != nil {
panic(err) panic(err)
} }
@ -127,119 +119,124 @@ func (uc *UserCreate) SaveX(ctx context.Context) *User {
} }
// Exec executes the query. // Exec executes the query.
func (uc *UserCreate) Exec(ctx context.Context) error { func (_c *UserCreate) Exec(ctx context.Context) error {
_, err := uc.Save(ctx) _, err := _c.Save(ctx)
return err return err
} }
// ExecX is like Exec, but panics if an error occurs. // ExecX is like Exec, but panics if an error occurs.
func (uc *UserCreate) ExecX(ctx context.Context) { func (_c *UserCreate) ExecX(ctx context.Context) {
if err := uc.Exec(ctx); err != nil { if err := _c.Exec(ctx); err != nil {
panic(err) panic(err)
} }
} }
// defaults sets the default values of the builder before save. // defaults sets the default values of the builder before save.
func (uc *UserCreate) defaults() error { func (_c *UserCreate) defaults() error {
if _, ok := uc.mutation.CreatedAt(); !ok { if _, ok := _c.mutation.Verified(); !ok {
v := user.DefaultVerified
_c.mutation.SetVerified(v)
}
if _, ok := _c.mutation.Admin(); !ok {
v := user.DefaultAdmin
_c.mutation.SetAdmin(v)
}
if _, ok := _c.mutation.CreatedAt(); !ok {
if user.DefaultCreatedAt == nil { if user.DefaultCreatedAt == nil {
return fmt.Errorf("ent: uninitialized user.DefaultCreatedAt (forgotten import ent/runtime?)") return fmt.Errorf("ent: uninitialized user.DefaultCreatedAt (forgotten import ent/runtime?)")
} }
v := user.DefaultCreatedAt() v := user.DefaultCreatedAt()
uc.mutation.SetCreatedAt(v) _c.mutation.SetCreatedAt(v)
} }
return nil return nil
} }
// check runs all checks and user-defined validators on the builder. // check runs all checks and user-defined validators on the builder.
func (uc *UserCreate) check() error { func (_c *UserCreate) check() error {
if _, ok := uc.mutation.Name(); !ok { if _, ok := _c.mutation.Name(); !ok {
return &ValidationError{Name: "name", err: errors.New(`ent: missing required field "name"`)} return &ValidationError{Name: "name", err: errors.New(`ent: missing required field "User.name"`)}
} }
if v, ok := uc.mutation.Name(); ok { if v, ok := _c.mutation.Name(); ok {
if err := user.NameValidator(v); err != nil { if err := user.NameValidator(v); err != nil {
return &ValidationError{Name: "name", err: fmt.Errorf(`ent: validator failed for field "name": %w`, err)} return &ValidationError{Name: "name", err: fmt.Errorf(`ent: validator failed for field "User.name": %w`, err)}
} }
} }
if _, ok := uc.mutation.Email(); !ok { if _, ok := _c.mutation.Email(); !ok {
return &ValidationError{Name: "email", err: errors.New(`ent: missing required field "email"`)} return &ValidationError{Name: "email", err: errors.New(`ent: missing required field "User.email"`)}
} }
if v, ok := uc.mutation.Email(); ok { if v, ok := _c.mutation.Email(); ok {
if err := user.EmailValidator(v); err != nil { if err := user.EmailValidator(v); err != nil {
return &ValidationError{Name: "email", err: fmt.Errorf(`ent: validator failed for field "email": %w`, err)} return &ValidationError{Name: "email", err: fmt.Errorf(`ent: validator failed for field "User.email": %w`, err)}
} }
} }
if _, ok := uc.mutation.Password(); !ok { if _, ok := _c.mutation.Password(); !ok {
return &ValidationError{Name: "password", err: errors.New(`ent: missing required field "password"`)} return &ValidationError{Name: "password", err: errors.New(`ent: missing required field "User.password"`)}
} }
if v, ok := uc.mutation.Password(); ok { if v, ok := _c.mutation.Password(); ok {
if err := user.PasswordValidator(v); err != nil { if err := user.PasswordValidator(v); err != nil {
return &ValidationError{Name: "password", err: fmt.Errorf(`ent: validator failed for field "password": %w`, err)} return &ValidationError{Name: "password", err: fmt.Errorf(`ent: validator failed for field "User.password": %w`, err)}
} }
} }
if _, ok := uc.mutation.CreatedAt(); !ok { if _, ok := _c.mutation.Verified(); !ok {
return &ValidationError{Name: "created_at", err: errors.New(`ent: missing required field "created_at"`)} return &ValidationError{Name: "verified", err: errors.New(`ent: missing required field "User.verified"`)}
}
if _, ok := _c.mutation.Admin(); !ok {
return &ValidationError{Name: "admin", err: errors.New(`ent: missing required field "User.admin"`)}
}
if _, ok := _c.mutation.CreatedAt(); !ok {
return &ValidationError{Name: "created_at", err: errors.New(`ent: missing required field "User.created_at"`)}
} }
return nil return nil
} }
func (uc *UserCreate) sqlSave(ctx context.Context) (*User, error) { func (_c *UserCreate) sqlSave(ctx context.Context) (*User, error) {
_node, _spec := uc.createSpec() if err := _c.check(); err != nil {
if err := sqlgraph.CreateNode(ctx, uc.driver, _spec); err != nil { return nil, err
}
_node, _spec := _c.createSpec()
if err := sqlgraph.CreateNode(ctx, _c.driver, _spec); err != nil {
if sqlgraph.IsConstraintError(err) { if sqlgraph.IsConstraintError(err) {
err = &ConstraintError{err.Error(), err} err = &ConstraintError{msg: err.Error(), wrap: err}
} }
return nil, err return nil, err
} }
id := _spec.ID.Value.(int64) id := _spec.ID.Value.(int64)
_node.ID = int(id) _node.ID = int(id)
_c.mutation.id = &_node.ID
_c.mutation.done = true
return _node, nil return _node, nil
} }
func (uc *UserCreate) createSpec() (*User, *sqlgraph.CreateSpec) { func (_c *UserCreate) createSpec() (*User, *sqlgraph.CreateSpec) {
var ( var (
_node = &User{config: uc.config} _node = &User{config: _c.config}
_spec = &sqlgraph.CreateSpec{ _spec = sqlgraph.NewCreateSpec(user.Table, sqlgraph.NewFieldSpec(user.FieldID, field.TypeInt))
Table: user.Table,
ID: &sqlgraph.FieldSpec{
Type: field.TypeInt,
Column: user.FieldID,
},
}
) )
if value, ok := uc.mutation.Name(); ok { if value, ok := _c.mutation.Name(); ok {
_spec.Fields = append(_spec.Fields, &sqlgraph.FieldSpec{ _spec.SetField(user.FieldName, field.TypeString, value)
Type: field.TypeString,
Value: value,
Column: user.FieldName,
})
_node.Name = value _node.Name = value
} }
if value, ok := uc.mutation.Email(); ok { if value, ok := _c.mutation.Email(); ok {
_spec.Fields = append(_spec.Fields, &sqlgraph.FieldSpec{ _spec.SetField(user.FieldEmail, field.TypeString, value)
Type: field.TypeString,
Value: value,
Column: user.FieldEmail,
})
_node.Email = value _node.Email = value
} }
if value, ok := uc.mutation.Password(); ok { if value, ok := _c.mutation.Password(); ok {
_spec.Fields = append(_spec.Fields, &sqlgraph.FieldSpec{ _spec.SetField(user.FieldPassword, field.TypeString, value)
Type: field.TypeString,
Value: value,
Column: user.FieldPassword,
})
_node.Password = value _node.Password = value
} }
if value, ok := uc.mutation.CreatedAt(); ok { if value, ok := _c.mutation.Verified(); ok {
_spec.Fields = append(_spec.Fields, &sqlgraph.FieldSpec{ _spec.SetField(user.FieldVerified, field.TypeBool, value)
Type: field.TypeTime, _node.Verified = value
Value: value, }
Column: user.FieldCreatedAt, if value, ok := _c.mutation.Admin(); ok {
}) _spec.SetField(user.FieldAdmin, field.TypeBool, value)
_node.Admin = value
}
if value, ok := _c.mutation.CreatedAt(); ok {
_spec.SetField(user.FieldCreatedAt, field.TypeTime, value)
_node.CreatedAt = value _node.CreatedAt = value
} }
if nodes := uc.mutation.OwnerIDs(); len(nodes) > 0 { if nodes := _c.mutation.OwnerIDs(); len(nodes) > 0 {
edge := &sqlgraph.EdgeSpec{ edge := &sqlgraph.EdgeSpec{
Rel: sqlgraph.O2M, Rel: sqlgraph.O2M,
Inverse: true, Inverse: true,
@ -247,10 +244,7 @@ func (uc *UserCreate) createSpec() (*User, *sqlgraph.CreateSpec) {
Columns: []string{user.OwnerColumn}, Columns: []string{user.OwnerColumn},
Bidi: false, Bidi: false,
Target: &sqlgraph.EdgeTarget{ Target: &sqlgraph.EdgeTarget{
IDSpec: &sqlgraph.FieldSpec{ IDSpec: sqlgraph.NewFieldSpec(passwordtoken.FieldID, field.TypeInt),
Type: field.TypeInt,
Column: passwordtoken.FieldID,
},
}, },
} }
for _, k := range nodes { for _, k := range nodes {
@ -264,17 +258,21 @@ func (uc *UserCreate) createSpec() (*User, *sqlgraph.CreateSpec) {
// UserCreateBulk is the builder for creating many User entities in bulk. // UserCreateBulk is the builder for creating many User entities in bulk.
type UserCreateBulk struct { type UserCreateBulk struct {
config config
err error
builders []*UserCreate builders []*UserCreate
} }
// Save creates the User entities in the database. // Save creates the User entities in the database.
func (ucb *UserCreateBulk) Save(ctx context.Context) ([]*User, error) { func (_c *UserCreateBulk) Save(ctx context.Context) ([]*User, error) {
specs := make([]*sqlgraph.CreateSpec, len(ucb.builders)) if _c.err != nil {
nodes := make([]*User, len(ucb.builders)) return nil, _c.err
mutators := make([]Mutator, len(ucb.builders)) }
for i := range ucb.builders { specs := make([]*sqlgraph.CreateSpec, len(_c.builders))
nodes := make([]*User, len(_c.builders))
mutators := make([]Mutator, len(_c.builders))
for i := range _c.builders {
func(i int, root context.Context) { func(i int, root context.Context) {
builder := ucb.builders[i] builder := _c.builders[i]
builder.defaults() builder.defaults()
var mut Mutator = MutateFunc(func(ctx context.Context, m Mutation) (Value, error) { var mut Mutator = MutateFunc(func(ctx context.Context, m Mutation) (Value, error) {
mutation, ok := m.(*UserMutation) mutation, ok := m.(*UserMutation)
@ -285,16 +283,16 @@ func (ucb *UserCreateBulk) Save(ctx context.Context) ([]*User, error) {
return nil, err return nil, err
} }
builder.mutation = mutation builder.mutation = mutation
nodes[i], specs[i] = builder.createSpec()
var err error var err error
nodes[i], specs[i] = builder.createSpec()
if i < len(mutators)-1 { if i < len(mutators)-1 {
_, err = mutators[i+1].Mutate(root, ucb.builders[i+1].mutation) _, err = mutators[i+1].Mutate(root, _c.builders[i+1].mutation)
} else { } else {
spec := &sqlgraph.BatchCreateSpec{Nodes: specs} spec := &sqlgraph.BatchCreateSpec{Nodes: specs}
// Invoke the actual operation on the latest mutation in the chain. // Invoke the actual operation on the latest mutation in the chain.
if err = sqlgraph.BatchCreate(ctx, ucb.driver, spec); err != nil { if err = sqlgraph.BatchCreate(ctx, _c.driver, spec); err != nil {
if sqlgraph.IsConstraintError(err) { if sqlgraph.IsConstraintError(err) {
err = &ConstraintError{err.Error(), err} err = &ConstraintError{msg: err.Error(), wrap: err}
} }
} }
} }
@ -302,11 +300,11 @@ func (ucb *UserCreateBulk) Save(ctx context.Context) ([]*User, error) {
return nil, err return nil, err
} }
mutation.id = &nodes[i].ID mutation.id = &nodes[i].ID
mutation.done = true
if specs[i].ID.Value != nil { if specs[i].ID.Value != nil {
id := specs[i].ID.Value.(int64) id := specs[i].ID.Value.(int64)
nodes[i].ID = int(id) nodes[i].ID = int(id)
} }
mutation.done = true
return nodes[i], nil return nodes[i], nil
}) })
for i := len(builder.hooks) - 1; i >= 0; i-- { for i := len(builder.hooks) - 1; i >= 0; i-- {
@ -316,7 +314,7 @@ func (ucb *UserCreateBulk) Save(ctx context.Context) ([]*User, error) {
}(i, ctx) }(i, ctx)
} }
if len(mutators) > 0 { if len(mutators) > 0 {
if _, err := mutators[0].Mutate(ctx, ucb.builders[0].mutation); err != nil { if _, err := mutators[0].Mutate(ctx, _c.builders[0].mutation); err != nil {
return nil, err return nil, err
} }
} }
@ -324,8 +322,8 @@ func (ucb *UserCreateBulk) Save(ctx context.Context) ([]*User, error) {
} }
// SaveX is like Save, but panics if an error occurs. // SaveX is like Save, but panics if an error occurs.
func (ucb *UserCreateBulk) SaveX(ctx context.Context) []*User { func (_c *UserCreateBulk) SaveX(ctx context.Context) []*User {
v, err := ucb.Save(ctx) v, err := _c.Save(ctx)
if err != nil { if err != nil {
panic(err) panic(err)
} }
@ -333,14 +331,14 @@ func (ucb *UserCreateBulk) SaveX(ctx context.Context) []*User {
} }
// Exec executes the query. // Exec executes the query.
func (ucb *UserCreateBulk) Exec(ctx context.Context) error { func (_c *UserCreateBulk) Exec(ctx context.Context) error {
_, err := ucb.Save(ctx) _, err := _c.Save(ctx)
return err return err
} }
// ExecX is like Exec, but panics if an error occurs. // ExecX is like Exec, but panics if an error occurs.
func (ucb *UserCreateBulk) ExecX(ctx context.Context) { func (_c *UserCreateBulk) ExecX(ctx context.Context) {
if err := ucb.Exec(ctx); err != nil { if err := _c.Exec(ctx); err != nil {
panic(err) panic(err)
} }
} }

View file

@ -1,16 +1,15 @@
// Code generated by entc, DO NOT EDIT. // Code generated by ent, DO NOT EDIT.
package ent package ent
import ( import (
"context" "context"
"fmt"
"goweb/ent/predicate"
"goweb/ent/user"
"entgo.io/ent/dialect/sql" "entgo.io/ent/dialect/sql"
"entgo.io/ent/dialect/sql/sqlgraph" "entgo.io/ent/dialect/sql/sqlgraph"
"entgo.io/ent/schema/field" "entgo.io/ent/schema/field"
"github.com/mikestefanello/pagoda/ent/predicate"
"github.com/mikestefanello/pagoda/ent/user"
) )
// UserDelete is the builder for deleting a User entity. // UserDelete is the builder for deleting a User entity.
@ -21,80 +20,56 @@ type UserDelete struct {
} }
// Where appends a list predicates to the UserDelete builder. // Where appends a list predicates to the UserDelete builder.
func (ud *UserDelete) Where(ps ...predicate.User) *UserDelete { func (_d *UserDelete) Where(ps ...predicate.User) *UserDelete {
ud.mutation.Where(ps...) _d.mutation.Where(ps...)
return ud return _d
} }
// Exec executes the deletion query and returns how many vertices were deleted. // Exec executes the deletion query and returns how many vertices were deleted.
func (ud *UserDelete) Exec(ctx context.Context) (int, error) { func (_d *UserDelete) Exec(ctx context.Context) (int, error) {
var ( return withHooks(ctx, _d.sqlExec, _d.mutation, _d.hooks)
err error
affected int
)
if len(ud.hooks) == 0 {
affected, err = ud.sqlExec(ctx)
} else {
var mut Mutator = MutateFunc(func(ctx context.Context, m Mutation) (Value, error) {
mutation, ok := m.(*UserMutation)
if !ok {
return nil, fmt.Errorf("unexpected mutation type %T", m)
}
ud.mutation = mutation
affected, err = ud.sqlExec(ctx)
mutation.done = true
return affected, err
})
for i := len(ud.hooks) - 1; i >= 0; i-- {
if ud.hooks[i] == nil {
return 0, fmt.Errorf("ent: uninitialized hook (forgotten import ent/runtime?)")
}
mut = ud.hooks[i](mut)
}
if _, err := mut.Mutate(ctx, ud.mutation); err != nil {
return 0, err
}
}
return affected, err
} }
// ExecX is like Exec, but panics if an error occurs. // ExecX is like Exec, but panics if an error occurs.
func (ud *UserDelete) ExecX(ctx context.Context) int { func (_d *UserDelete) ExecX(ctx context.Context) int {
n, err := ud.Exec(ctx) n, err := _d.Exec(ctx)
if err != nil { if err != nil {
panic(err) panic(err)
} }
return n return n
} }
func (ud *UserDelete) sqlExec(ctx context.Context) (int, error) { func (_d *UserDelete) sqlExec(ctx context.Context) (int, error) {
_spec := &sqlgraph.DeleteSpec{ _spec := sqlgraph.NewDeleteSpec(user.Table, sqlgraph.NewFieldSpec(user.FieldID, field.TypeInt))
Node: &sqlgraph.NodeSpec{ if ps := _d.mutation.predicates; len(ps) > 0 {
Table: user.Table,
ID: &sqlgraph.FieldSpec{
Type: field.TypeInt,
Column: user.FieldID,
},
},
}
if ps := ud.mutation.predicates; len(ps) > 0 {
_spec.Predicate = func(selector *sql.Selector) { _spec.Predicate = func(selector *sql.Selector) {
for i := range ps { for i := range ps {
ps[i](selector) ps[i](selector)
} }
} }
} }
return sqlgraph.DeleteNodes(ctx, ud.driver, _spec) 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
} }
// UserDeleteOne is the builder for deleting a single User entity. // UserDeleteOne is the builder for deleting a single User entity.
type UserDeleteOne struct { type UserDeleteOne struct {
ud *UserDelete _d *UserDelete
}
// Where appends a list predicates to the UserDelete builder.
func (_d *UserDeleteOne) Where(ps ...predicate.User) *UserDeleteOne {
_d._d.mutation.Where(ps...)
return _d
} }
// Exec executes the deletion query. // Exec executes the deletion query.
func (udo *UserDeleteOne) Exec(ctx context.Context) error { func (_d *UserDeleteOne) Exec(ctx context.Context) error {
n, err := udo.ud.Exec(ctx) n, err := _d._d.Exec(ctx)
switch { switch {
case err != nil: case err != nil:
return err return err
@ -106,6 +81,8 @@ func (udo *UserDeleteOne) Exec(ctx context.Context) error {
} }
// ExecX is like Exec, but panics if an error occurs. // ExecX is like Exec, but panics if an error occurs.
func (udo *UserDeleteOne) ExecX(ctx context.Context) { func (_d *UserDeleteOne) ExecX(ctx context.Context) {
udo.ud.ExecX(ctx) if err := _d.Exec(ctx); err != nil {
panic(err)
}
} }

File diff suppressed because it is too large Load diff

View file

@ -1,17 +1,18 @@
// Code generated by entc, DO NOT EDIT. // Code generated by ent, DO NOT EDIT.
package ent package ent
import ( import (
"context" "context"
"errors"
"fmt" "fmt"
"goweb/ent/passwordtoken"
"goweb/ent/predicate"
"goweb/ent/user"
"entgo.io/ent/dialect/sql" "entgo.io/ent/dialect/sql"
"entgo.io/ent/dialect/sql/sqlgraph" "entgo.io/ent/dialect/sql/sqlgraph"
"entgo.io/ent/schema/field" "entgo.io/ent/schema/field"
"github.com/mikestefanello/pagoda/ent/passwordtoken"
"github.com/mikestefanello/pagoda/ent/predicate"
"github.com/mikestefanello/pagoda/ent/user"
) )
// UserUpdate is the builder for updating User entities. // UserUpdate is the builder for updating User entities.
@ -22,111 +23,130 @@ type UserUpdate struct {
} }
// Where appends a list predicates to the UserUpdate builder. // Where appends a list predicates to the UserUpdate builder.
func (uu *UserUpdate) Where(ps ...predicate.User) *UserUpdate { func (_u *UserUpdate) Where(ps ...predicate.User) *UserUpdate {
uu.mutation.Where(ps...) _u.mutation.Where(ps...)
return uu return _u
} }
// SetName sets the "name" field. // SetName sets the "name" field.
func (uu *UserUpdate) SetName(s string) *UserUpdate { func (_u *UserUpdate) SetName(v string) *UserUpdate {
uu.mutation.SetName(s) _u.mutation.SetName(v)
return uu return _u
}
// SetNillableName sets the "name" field if the given value is not nil.
func (_u *UserUpdate) SetNillableName(v *string) *UserUpdate {
if v != nil {
_u.SetName(*v)
}
return _u
} }
// SetEmail sets the "email" field. // SetEmail sets the "email" field.
func (uu *UserUpdate) SetEmail(s string) *UserUpdate { func (_u *UserUpdate) SetEmail(v string) *UserUpdate {
uu.mutation.SetEmail(s) _u.mutation.SetEmail(v)
return uu return _u
}
// SetNillableEmail sets the "email" field if the given value is not nil.
func (_u *UserUpdate) SetNillableEmail(v *string) *UserUpdate {
if v != nil {
_u.SetEmail(*v)
}
return _u
} }
// SetPassword sets the "password" field. // SetPassword sets the "password" field.
func (uu *UserUpdate) SetPassword(s string) *UserUpdate { func (_u *UserUpdate) SetPassword(v string) *UserUpdate {
uu.mutation.SetPassword(s) _u.mutation.SetPassword(v)
return uu return _u
}
// SetNillablePassword sets the "password" field if the given value is not nil.
func (_u *UserUpdate) SetNillablePassword(v *string) *UserUpdate {
if v != nil {
_u.SetPassword(*v)
}
return _u
}
// SetVerified sets the "verified" field.
func (_u *UserUpdate) SetVerified(v bool) *UserUpdate {
_u.mutation.SetVerified(v)
return _u
}
// SetNillableVerified sets the "verified" field if the given value is not nil.
func (_u *UserUpdate) SetNillableVerified(v *bool) *UserUpdate {
if v != nil {
_u.SetVerified(*v)
}
return _u
}
// SetAdmin sets the "admin" field.
func (_u *UserUpdate) SetAdmin(v bool) *UserUpdate {
_u.mutation.SetAdmin(v)
return _u
}
// SetNillableAdmin sets the "admin" field if the given value is not nil.
func (_u *UserUpdate) SetNillableAdmin(v *bool) *UserUpdate {
if v != nil {
_u.SetAdmin(*v)
}
return _u
} }
// AddOwnerIDs adds the "owner" edge to the PasswordToken entity by IDs. // AddOwnerIDs adds the "owner" edge to the PasswordToken entity by IDs.
func (uu *UserUpdate) AddOwnerIDs(ids ...int) *UserUpdate { func (_u *UserUpdate) AddOwnerIDs(ids ...int) *UserUpdate {
uu.mutation.AddOwnerIDs(ids...) _u.mutation.AddOwnerIDs(ids...)
return uu return _u
} }
// AddOwner adds the "owner" edges to the PasswordToken entity. // AddOwner adds the "owner" edges to the PasswordToken entity.
func (uu *UserUpdate) AddOwner(p ...*PasswordToken) *UserUpdate { func (_u *UserUpdate) AddOwner(v ...*PasswordToken) *UserUpdate {
ids := make([]int, len(p)) ids := make([]int, len(v))
for i := range p { for i := range v {
ids[i] = p[i].ID ids[i] = v[i].ID
} }
return uu.AddOwnerIDs(ids...) return _u.AddOwnerIDs(ids...)
} }
// Mutation returns the UserMutation object of the builder. // Mutation returns the UserMutation object of the builder.
func (uu *UserUpdate) Mutation() *UserMutation { func (_u *UserUpdate) Mutation() *UserMutation {
return uu.mutation return _u.mutation
} }
// ClearOwner clears all "owner" edges to the PasswordToken entity. // ClearOwner clears all "owner" edges to the PasswordToken entity.
func (uu *UserUpdate) ClearOwner() *UserUpdate { func (_u *UserUpdate) ClearOwner() *UserUpdate {
uu.mutation.ClearOwner() _u.mutation.ClearOwner()
return uu return _u
} }
// RemoveOwnerIDs removes the "owner" edge to PasswordToken entities by IDs. // RemoveOwnerIDs removes the "owner" edge to PasswordToken entities by IDs.
func (uu *UserUpdate) RemoveOwnerIDs(ids ...int) *UserUpdate { func (_u *UserUpdate) RemoveOwnerIDs(ids ...int) *UserUpdate {
uu.mutation.RemoveOwnerIDs(ids...) _u.mutation.RemoveOwnerIDs(ids...)
return uu return _u
} }
// RemoveOwner removes "owner" edges to PasswordToken entities. // RemoveOwner removes "owner" edges to PasswordToken entities.
func (uu *UserUpdate) RemoveOwner(p ...*PasswordToken) *UserUpdate { func (_u *UserUpdate) RemoveOwner(v ...*PasswordToken) *UserUpdate {
ids := make([]int, len(p)) ids := make([]int, len(v))
for i := range p { for i := range v {
ids[i] = p[i].ID ids[i] = v[i].ID
} }
return uu.RemoveOwnerIDs(ids...) return _u.RemoveOwnerIDs(ids...)
} }
// Save executes the query and returns the number of nodes affected by the update operation. // Save executes the query and returns the number of nodes affected by the update operation.
func (uu *UserUpdate) Save(ctx context.Context) (int, error) { func (_u *UserUpdate) Save(ctx context.Context) (int, error) {
var ( return withHooks(ctx, _u.sqlSave, _u.mutation, _u.hooks)
err error
affected int
)
if len(uu.hooks) == 0 {
if err = uu.check(); err != nil {
return 0, err
}
affected, err = uu.sqlSave(ctx)
} else {
var mut Mutator = MutateFunc(func(ctx context.Context, m Mutation) (Value, error) {
mutation, ok := m.(*UserMutation)
if !ok {
return nil, fmt.Errorf("unexpected mutation type %T", m)
}
if err = uu.check(); err != nil {
return 0, err
}
uu.mutation = mutation
affected, err = uu.sqlSave(ctx)
mutation.done = true
return affected, err
})
for i := len(uu.hooks) - 1; i >= 0; i-- {
if uu.hooks[i] == nil {
return 0, fmt.Errorf("ent: uninitialized hook (forgotten import ent/runtime?)")
}
mut = uu.hooks[i](mut)
}
if _, err := mut.Mutate(ctx, uu.mutation); err != nil {
return 0, err
}
}
return affected, err
} }
// SaveX is like Save, but panics if an error occurs. // SaveX is like Save, but panics if an error occurs.
func (uu *UserUpdate) SaveX(ctx context.Context) int { func (_u *UserUpdate) SaveX(ctx context.Context) int {
affected, err := uu.Save(ctx) affected, err := _u.Save(ctx)
if err != nil { if err != nil {
panic(err) panic(err)
} }
@ -134,78 +154,66 @@ func (uu *UserUpdate) SaveX(ctx context.Context) int {
} }
// Exec executes the query. // Exec executes the query.
func (uu *UserUpdate) Exec(ctx context.Context) error { func (_u *UserUpdate) Exec(ctx context.Context) error {
_, err := uu.Save(ctx) _, err := _u.Save(ctx)
return err return err
} }
// ExecX is like Exec, but panics if an error occurs. // ExecX is like Exec, but panics if an error occurs.
func (uu *UserUpdate) ExecX(ctx context.Context) { func (_u *UserUpdate) ExecX(ctx context.Context) {
if err := uu.Exec(ctx); err != nil { if err := _u.Exec(ctx); err != nil {
panic(err) panic(err)
} }
} }
// check runs all checks and user-defined validators on the builder. // check runs all checks and user-defined validators on the builder.
func (uu *UserUpdate) check() error { func (_u *UserUpdate) check() error {
if v, ok := uu.mutation.Name(); ok { if v, ok := _u.mutation.Name(); ok {
if err := user.NameValidator(v); err != nil { if err := user.NameValidator(v); err != nil {
return &ValidationError{Name: "name", err: fmt.Errorf("ent: validator failed for field \"name\": %w", err)} return &ValidationError{Name: "name", err: fmt.Errorf(`ent: validator failed for field "User.name": %w`, err)}
} }
} }
if v, ok := uu.mutation.Email(); ok { if v, ok := _u.mutation.Email(); ok {
if err := user.EmailValidator(v); err != nil { if err := user.EmailValidator(v); err != nil {
return &ValidationError{Name: "email", err: fmt.Errorf("ent: validator failed for field \"email\": %w", err)} return &ValidationError{Name: "email", err: fmt.Errorf(`ent: validator failed for field "User.email": %w`, err)}
} }
} }
if v, ok := uu.mutation.Password(); ok { if v, ok := _u.mutation.Password(); ok {
if err := user.PasswordValidator(v); err != nil { if err := user.PasswordValidator(v); err != nil {
return &ValidationError{Name: "password", err: fmt.Errorf("ent: validator failed for field \"password\": %w", err)} return &ValidationError{Name: "password", err: fmt.Errorf(`ent: validator failed for field "User.password": %w`, err)}
} }
} }
return nil return nil
} }
func (uu *UserUpdate) sqlSave(ctx context.Context) (n int, err error) { func (_u *UserUpdate) sqlSave(ctx context.Context) (_node int, err error) {
_spec := &sqlgraph.UpdateSpec{ if err := _u.check(); err != nil {
Node: &sqlgraph.NodeSpec{ return _node, err
Table: user.Table,
Columns: user.Columns,
ID: &sqlgraph.FieldSpec{
Type: field.TypeInt,
Column: user.FieldID,
},
},
} }
if ps := uu.mutation.predicates; len(ps) > 0 { _spec := sqlgraph.NewUpdateSpec(user.Table, user.Columns, sqlgraph.NewFieldSpec(user.FieldID, field.TypeInt))
if ps := _u.mutation.predicates; len(ps) > 0 {
_spec.Predicate = func(selector *sql.Selector) { _spec.Predicate = func(selector *sql.Selector) {
for i := range ps { for i := range ps {
ps[i](selector) ps[i](selector)
} }
} }
} }
if value, ok := uu.mutation.Name(); ok { if value, ok := _u.mutation.Name(); ok {
_spec.Fields.Set = append(_spec.Fields.Set, &sqlgraph.FieldSpec{ _spec.SetField(user.FieldName, field.TypeString, value)
Type: field.TypeString,
Value: value,
Column: user.FieldName,
})
} }
if value, ok := uu.mutation.Email(); ok { if value, ok := _u.mutation.Email(); ok {
_spec.Fields.Set = append(_spec.Fields.Set, &sqlgraph.FieldSpec{ _spec.SetField(user.FieldEmail, field.TypeString, value)
Type: field.TypeString,
Value: value,
Column: user.FieldEmail,
})
} }
if value, ok := uu.mutation.Password(); ok { if value, ok := _u.mutation.Password(); ok {
_spec.Fields.Set = append(_spec.Fields.Set, &sqlgraph.FieldSpec{ _spec.SetField(user.FieldPassword, field.TypeString, value)
Type: field.TypeString,
Value: value,
Column: user.FieldPassword,
})
} }
if uu.mutation.OwnerCleared() { if value, ok := _u.mutation.Verified(); ok {
_spec.SetField(user.FieldVerified, field.TypeBool, value)
}
if value, ok := _u.mutation.Admin(); ok {
_spec.SetField(user.FieldAdmin, field.TypeBool, value)
}
if _u.mutation.OwnerCleared() {
edge := &sqlgraph.EdgeSpec{ edge := &sqlgraph.EdgeSpec{
Rel: sqlgraph.O2M, Rel: sqlgraph.O2M,
Inverse: true, Inverse: true,
@ -213,15 +221,12 @@ func (uu *UserUpdate) sqlSave(ctx context.Context) (n int, err error) {
Columns: []string{user.OwnerColumn}, Columns: []string{user.OwnerColumn},
Bidi: false, Bidi: false,
Target: &sqlgraph.EdgeTarget{ Target: &sqlgraph.EdgeTarget{
IDSpec: &sqlgraph.FieldSpec{ IDSpec: sqlgraph.NewFieldSpec(passwordtoken.FieldID, field.TypeInt),
Type: field.TypeInt,
Column: passwordtoken.FieldID,
},
}, },
} }
_spec.Edges.Clear = append(_spec.Edges.Clear, edge) _spec.Edges.Clear = append(_spec.Edges.Clear, edge)
} }
if nodes := uu.mutation.RemovedOwnerIDs(); len(nodes) > 0 && !uu.mutation.OwnerCleared() { if nodes := _u.mutation.RemovedOwnerIDs(); len(nodes) > 0 && !_u.mutation.OwnerCleared() {
edge := &sqlgraph.EdgeSpec{ edge := &sqlgraph.EdgeSpec{
Rel: sqlgraph.O2M, Rel: sqlgraph.O2M,
Inverse: true, Inverse: true,
@ -229,10 +234,7 @@ func (uu *UserUpdate) sqlSave(ctx context.Context) (n int, err error) {
Columns: []string{user.OwnerColumn}, Columns: []string{user.OwnerColumn},
Bidi: false, Bidi: false,
Target: &sqlgraph.EdgeTarget{ Target: &sqlgraph.EdgeTarget{
IDSpec: &sqlgraph.FieldSpec{ IDSpec: sqlgraph.NewFieldSpec(passwordtoken.FieldID, field.TypeInt),
Type: field.TypeInt,
Column: passwordtoken.FieldID,
},
}, },
} }
for _, k := range nodes { for _, k := range nodes {
@ -240,7 +242,7 @@ func (uu *UserUpdate) sqlSave(ctx context.Context) (n int, err error) {
} }
_spec.Edges.Clear = append(_spec.Edges.Clear, edge) _spec.Edges.Clear = append(_spec.Edges.Clear, edge)
} }
if nodes := uu.mutation.OwnerIDs(); len(nodes) > 0 { if nodes := _u.mutation.OwnerIDs(); len(nodes) > 0 {
edge := &sqlgraph.EdgeSpec{ edge := &sqlgraph.EdgeSpec{
Rel: sqlgraph.O2M, Rel: sqlgraph.O2M,
Inverse: true, Inverse: true,
@ -248,10 +250,7 @@ func (uu *UserUpdate) sqlSave(ctx context.Context) (n int, err error) {
Columns: []string{user.OwnerColumn}, Columns: []string{user.OwnerColumn},
Bidi: false, Bidi: false,
Target: &sqlgraph.EdgeTarget{ Target: &sqlgraph.EdgeTarget{
IDSpec: &sqlgraph.FieldSpec{ IDSpec: sqlgraph.NewFieldSpec(passwordtoken.FieldID, field.TypeInt),
Type: field.TypeInt,
Column: passwordtoken.FieldID,
},
}, },
} }
for _, k := range nodes { for _, k := range nodes {
@ -259,15 +258,16 @@ func (uu *UserUpdate) sqlSave(ctx context.Context) (n int, err error) {
} }
_spec.Edges.Add = append(_spec.Edges.Add, edge) _spec.Edges.Add = append(_spec.Edges.Add, edge)
} }
if n, err = sqlgraph.UpdateNodes(ctx, uu.driver, _spec); err != nil { if _node, err = sqlgraph.UpdateNodes(ctx, _u.driver, _spec); err != nil {
if _, ok := err.(*sqlgraph.NotFoundError); ok { if _, ok := err.(*sqlgraph.NotFoundError); ok {
err = &NotFoundError{user.Label} err = &NotFoundError{user.Label}
} else if sqlgraph.IsConstraintError(err) { } else if sqlgraph.IsConstraintError(err) {
err = &ConstraintError{err.Error(), err} err = &ConstraintError{msg: err.Error(), wrap: err}
} }
return 0, err return 0, err
} }
return n, nil _u.mutation.done = true
return _node, nil
} }
// UserUpdateOne is the builder for updating a single User entity. // UserUpdateOne is the builder for updating a single User entity.
@ -279,112 +279,137 @@ type UserUpdateOne struct {
} }
// SetName sets the "name" field. // SetName sets the "name" field.
func (uuo *UserUpdateOne) SetName(s string) *UserUpdateOne { func (_u *UserUpdateOne) SetName(v string) *UserUpdateOne {
uuo.mutation.SetName(s) _u.mutation.SetName(v)
return uuo return _u
}
// SetNillableName sets the "name" field if the given value is not nil.
func (_u *UserUpdateOne) SetNillableName(v *string) *UserUpdateOne {
if v != nil {
_u.SetName(*v)
}
return _u
} }
// SetEmail sets the "email" field. // SetEmail sets the "email" field.
func (uuo *UserUpdateOne) SetEmail(s string) *UserUpdateOne { func (_u *UserUpdateOne) SetEmail(v string) *UserUpdateOne {
uuo.mutation.SetEmail(s) _u.mutation.SetEmail(v)
return uuo return _u
}
// SetNillableEmail sets the "email" field if the given value is not nil.
func (_u *UserUpdateOne) SetNillableEmail(v *string) *UserUpdateOne {
if v != nil {
_u.SetEmail(*v)
}
return _u
} }
// SetPassword sets the "password" field. // SetPassword sets the "password" field.
func (uuo *UserUpdateOne) SetPassword(s string) *UserUpdateOne { func (_u *UserUpdateOne) SetPassword(v string) *UserUpdateOne {
uuo.mutation.SetPassword(s) _u.mutation.SetPassword(v)
return uuo return _u
}
// SetNillablePassword sets the "password" field if the given value is not nil.
func (_u *UserUpdateOne) SetNillablePassword(v *string) *UserUpdateOne {
if v != nil {
_u.SetPassword(*v)
}
return _u
}
// SetVerified sets the "verified" field.
func (_u *UserUpdateOne) SetVerified(v bool) *UserUpdateOne {
_u.mutation.SetVerified(v)
return _u
}
// SetNillableVerified sets the "verified" field if the given value is not nil.
func (_u *UserUpdateOne) SetNillableVerified(v *bool) *UserUpdateOne {
if v != nil {
_u.SetVerified(*v)
}
return _u
}
// SetAdmin sets the "admin" field.
func (_u *UserUpdateOne) SetAdmin(v bool) *UserUpdateOne {
_u.mutation.SetAdmin(v)
return _u
}
// SetNillableAdmin sets the "admin" field if the given value is not nil.
func (_u *UserUpdateOne) SetNillableAdmin(v *bool) *UserUpdateOne {
if v != nil {
_u.SetAdmin(*v)
}
return _u
} }
// AddOwnerIDs adds the "owner" edge to the PasswordToken entity by IDs. // AddOwnerIDs adds the "owner" edge to the PasswordToken entity by IDs.
func (uuo *UserUpdateOne) AddOwnerIDs(ids ...int) *UserUpdateOne { func (_u *UserUpdateOne) AddOwnerIDs(ids ...int) *UserUpdateOne {
uuo.mutation.AddOwnerIDs(ids...) _u.mutation.AddOwnerIDs(ids...)
return uuo return _u
} }
// AddOwner adds the "owner" edges to the PasswordToken entity. // AddOwner adds the "owner" edges to the PasswordToken entity.
func (uuo *UserUpdateOne) AddOwner(p ...*PasswordToken) *UserUpdateOne { func (_u *UserUpdateOne) AddOwner(v ...*PasswordToken) *UserUpdateOne {
ids := make([]int, len(p)) ids := make([]int, len(v))
for i := range p { for i := range v {
ids[i] = p[i].ID ids[i] = v[i].ID
} }
return uuo.AddOwnerIDs(ids...) return _u.AddOwnerIDs(ids...)
} }
// Mutation returns the UserMutation object of the builder. // Mutation returns the UserMutation object of the builder.
func (uuo *UserUpdateOne) Mutation() *UserMutation { func (_u *UserUpdateOne) Mutation() *UserMutation {
return uuo.mutation return _u.mutation
} }
// ClearOwner clears all "owner" edges to the PasswordToken entity. // ClearOwner clears all "owner" edges to the PasswordToken entity.
func (uuo *UserUpdateOne) ClearOwner() *UserUpdateOne { func (_u *UserUpdateOne) ClearOwner() *UserUpdateOne {
uuo.mutation.ClearOwner() _u.mutation.ClearOwner()
return uuo return _u
} }
// RemoveOwnerIDs removes the "owner" edge to PasswordToken entities by IDs. // RemoveOwnerIDs removes the "owner" edge to PasswordToken entities by IDs.
func (uuo *UserUpdateOne) RemoveOwnerIDs(ids ...int) *UserUpdateOne { func (_u *UserUpdateOne) RemoveOwnerIDs(ids ...int) *UserUpdateOne {
uuo.mutation.RemoveOwnerIDs(ids...) _u.mutation.RemoveOwnerIDs(ids...)
return uuo return _u
} }
// RemoveOwner removes "owner" edges to PasswordToken entities. // RemoveOwner removes "owner" edges to PasswordToken entities.
func (uuo *UserUpdateOne) RemoveOwner(p ...*PasswordToken) *UserUpdateOne { func (_u *UserUpdateOne) RemoveOwner(v ...*PasswordToken) *UserUpdateOne {
ids := make([]int, len(p)) ids := make([]int, len(v))
for i := range p { for i := range v {
ids[i] = p[i].ID ids[i] = v[i].ID
} }
return uuo.RemoveOwnerIDs(ids...) return _u.RemoveOwnerIDs(ids...)
}
// Where appends a list predicates to the UserUpdate builder.
func (_u *UserUpdateOne) Where(ps ...predicate.User) *UserUpdateOne {
_u.mutation.Where(ps...)
return _u
} }
// Select allows selecting one or more fields (columns) of the returned entity. // Select allows selecting one or more fields (columns) of the returned entity.
// The default is selecting all fields defined in the entity schema. // The default is selecting all fields defined in the entity schema.
func (uuo *UserUpdateOne) Select(field string, fields ...string) *UserUpdateOne { func (_u *UserUpdateOne) Select(field string, fields ...string) *UserUpdateOne {
uuo.fields = append([]string{field}, fields...) _u.fields = append([]string{field}, fields...)
return uuo return _u
} }
// Save executes the query and returns the updated User entity. // Save executes the query and returns the updated User entity.
func (uuo *UserUpdateOne) Save(ctx context.Context) (*User, error) { func (_u *UserUpdateOne) Save(ctx context.Context) (*User, error) {
var ( return withHooks(ctx, _u.sqlSave, _u.mutation, _u.hooks)
err error
node *User
)
if len(uuo.hooks) == 0 {
if err = uuo.check(); err != nil {
return nil, err
}
node, err = uuo.sqlSave(ctx)
} else {
var mut Mutator = MutateFunc(func(ctx context.Context, m Mutation) (Value, error) {
mutation, ok := m.(*UserMutation)
if !ok {
return nil, fmt.Errorf("unexpected mutation type %T", m)
}
if err = uuo.check(); err != nil {
return nil, err
}
uuo.mutation = mutation
node, err = uuo.sqlSave(ctx)
mutation.done = true
return node, err
})
for i := len(uuo.hooks) - 1; i >= 0; i-- {
if uuo.hooks[i] == nil {
return nil, fmt.Errorf("ent: uninitialized hook (forgotten import ent/runtime?)")
}
mut = uuo.hooks[i](mut)
}
if _, err := mut.Mutate(ctx, uuo.mutation); err != nil {
return nil, err
}
}
return node, err
} }
// SaveX is like Save, but panics if an error occurs. // SaveX is like Save, but panics if an error occurs.
func (uuo *UserUpdateOne) SaveX(ctx context.Context) *User { func (_u *UserUpdateOne) SaveX(ctx context.Context) *User {
node, err := uuo.Save(ctx) node, err := _u.Save(ctx)
if err != nil { if err != nil {
panic(err) panic(err)
} }
@ -392,55 +417,49 @@ func (uuo *UserUpdateOne) SaveX(ctx context.Context) *User {
} }
// Exec executes the query on the entity. // Exec executes the query on the entity.
func (uuo *UserUpdateOne) Exec(ctx context.Context) error { func (_u *UserUpdateOne) Exec(ctx context.Context) error {
_, err := uuo.Save(ctx) _, err := _u.Save(ctx)
return err return err
} }
// ExecX is like Exec, but panics if an error occurs. // ExecX is like Exec, but panics if an error occurs.
func (uuo *UserUpdateOne) ExecX(ctx context.Context) { func (_u *UserUpdateOne) ExecX(ctx context.Context) {
if err := uuo.Exec(ctx); err != nil { if err := _u.Exec(ctx); err != nil {
panic(err) panic(err)
} }
} }
// check runs all checks and user-defined validators on the builder. // check runs all checks and user-defined validators on the builder.
func (uuo *UserUpdateOne) check() error { func (_u *UserUpdateOne) check() error {
if v, ok := uuo.mutation.Name(); ok { if v, ok := _u.mutation.Name(); ok {
if err := user.NameValidator(v); err != nil { if err := user.NameValidator(v); err != nil {
return &ValidationError{Name: "name", err: fmt.Errorf("ent: validator failed for field \"name\": %w", err)} return &ValidationError{Name: "name", err: fmt.Errorf(`ent: validator failed for field "User.name": %w`, err)}
} }
} }
if v, ok := uuo.mutation.Email(); ok { if v, ok := _u.mutation.Email(); ok {
if err := user.EmailValidator(v); err != nil { if err := user.EmailValidator(v); err != nil {
return &ValidationError{Name: "email", err: fmt.Errorf("ent: validator failed for field \"email\": %w", err)} return &ValidationError{Name: "email", err: fmt.Errorf(`ent: validator failed for field "User.email": %w`, err)}
} }
} }
if v, ok := uuo.mutation.Password(); ok { if v, ok := _u.mutation.Password(); ok {
if err := user.PasswordValidator(v); err != nil { if err := user.PasswordValidator(v); err != nil {
return &ValidationError{Name: "password", err: fmt.Errorf("ent: validator failed for field \"password\": %w", err)} return &ValidationError{Name: "password", err: fmt.Errorf(`ent: validator failed for field "User.password": %w`, err)}
} }
} }
return nil return nil
} }
func (uuo *UserUpdateOne) sqlSave(ctx context.Context) (_node *User, err error) { func (_u *UserUpdateOne) sqlSave(ctx context.Context) (_node *User, err error) {
_spec := &sqlgraph.UpdateSpec{ if err := _u.check(); err != nil {
Node: &sqlgraph.NodeSpec{ return _node, err
Table: user.Table,
Columns: user.Columns,
ID: &sqlgraph.FieldSpec{
Type: field.TypeInt,
Column: user.FieldID,
},
},
} }
id, ok := uuo.mutation.ID() _spec := sqlgraph.NewUpdateSpec(user.Table, user.Columns, sqlgraph.NewFieldSpec(user.FieldID, field.TypeInt))
id, ok := _u.mutation.ID()
if !ok { if !ok {
return nil, &ValidationError{Name: "ID", err: fmt.Errorf("missing User.ID for update")} return nil, &ValidationError{Name: "id", err: errors.New(`ent: missing "User.id" for update`)}
} }
_spec.Node.ID.Value = id _spec.Node.ID.Value = id
if fields := uuo.fields; len(fields) > 0 { if fields := _u.fields; len(fields) > 0 {
_spec.Node.Columns = make([]string, 0, len(fields)) _spec.Node.Columns = make([]string, 0, len(fields))
_spec.Node.Columns = append(_spec.Node.Columns, user.FieldID) _spec.Node.Columns = append(_spec.Node.Columns, user.FieldID)
for _, f := range fields { for _, f := range fields {
@ -452,35 +471,29 @@ func (uuo *UserUpdateOne) sqlSave(ctx context.Context) (_node *User, err error)
} }
} }
} }
if ps := uuo.mutation.predicates; len(ps) > 0 { if ps := _u.mutation.predicates; len(ps) > 0 {
_spec.Predicate = func(selector *sql.Selector) { _spec.Predicate = func(selector *sql.Selector) {
for i := range ps { for i := range ps {
ps[i](selector) ps[i](selector)
} }
} }
} }
if value, ok := uuo.mutation.Name(); ok { if value, ok := _u.mutation.Name(); ok {
_spec.Fields.Set = append(_spec.Fields.Set, &sqlgraph.FieldSpec{ _spec.SetField(user.FieldName, field.TypeString, value)
Type: field.TypeString,
Value: value,
Column: user.FieldName,
})
} }
if value, ok := uuo.mutation.Email(); ok { if value, ok := _u.mutation.Email(); ok {
_spec.Fields.Set = append(_spec.Fields.Set, &sqlgraph.FieldSpec{ _spec.SetField(user.FieldEmail, field.TypeString, value)
Type: field.TypeString,
Value: value,
Column: user.FieldEmail,
})
} }
if value, ok := uuo.mutation.Password(); ok { if value, ok := _u.mutation.Password(); ok {
_spec.Fields.Set = append(_spec.Fields.Set, &sqlgraph.FieldSpec{ _spec.SetField(user.FieldPassword, field.TypeString, value)
Type: field.TypeString,
Value: value,
Column: user.FieldPassword,
})
} }
if uuo.mutation.OwnerCleared() { if value, ok := _u.mutation.Verified(); ok {
_spec.SetField(user.FieldVerified, field.TypeBool, value)
}
if value, ok := _u.mutation.Admin(); ok {
_spec.SetField(user.FieldAdmin, field.TypeBool, value)
}
if _u.mutation.OwnerCleared() {
edge := &sqlgraph.EdgeSpec{ edge := &sqlgraph.EdgeSpec{
Rel: sqlgraph.O2M, Rel: sqlgraph.O2M,
Inverse: true, Inverse: true,
@ -488,15 +501,12 @@ func (uuo *UserUpdateOne) sqlSave(ctx context.Context) (_node *User, err error)
Columns: []string{user.OwnerColumn}, Columns: []string{user.OwnerColumn},
Bidi: false, Bidi: false,
Target: &sqlgraph.EdgeTarget{ Target: &sqlgraph.EdgeTarget{
IDSpec: &sqlgraph.FieldSpec{ IDSpec: sqlgraph.NewFieldSpec(passwordtoken.FieldID, field.TypeInt),
Type: field.TypeInt,
Column: passwordtoken.FieldID,
},
}, },
} }
_spec.Edges.Clear = append(_spec.Edges.Clear, edge) _spec.Edges.Clear = append(_spec.Edges.Clear, edge)
} }
if nodes := uuo.mutation.RemovedOwnerIDs(); len(nodes) > 0 && !uuo.mutation.OwnerCleared() { if nodes := _u.mutation.RemovedOwnerIDs(); len(nodes) > 0 && !_u.mutation.OwnerCleared() {
edge := &sqlgraph.EdgeSpec{ edge := &sqlgraph.EdgeSpec{
Rel: sqlgraph.O2M, Rel: sqlgraph.O2M,
Inverse: true, Inverse: true,
@ -504,10 +514,7 @@ func (uuo *UserUpdateOne) sqlSave(ctx context.Context) (_node *User, err error)
Columns: []string{user.OwnerColumn}, Columns: []string{user.OwnerColumn},
Bidi: false, Bidi: false,
Target: &sqlgraph.EdgeTarget{ Target: &sqlgraph.EdgeTarget{
IDSpec: &sqlgraph.FieldSpec{ IDSpec: sqlgraph.NewFieldSpec(passwordtoken.FieldID, field.TypeInt),
Type: field.TypeInt,
Column: passwordtoken.FieldID,
},
}, },
} }
for _, k := range nodes { for _, k := range nodes {
@ -515,7 +522,7 @@ func (uuo *UserUpdateOne) sqlSave(ctx context.Context) (_node *User, err error)
} }
_spec.Edges.Clear = append(_spec.Edges.Clear, edge) _spec.Edges.Clear = append(_spec.Edges.Clear, edge)
} }
if nodes := uuo.mutation.OwnerIDs(); len(nodes) > 0 { if nodes := _u.mutation.OwnerIDs(); len(nodes) > 0 {
edge := &sqlgraph.EdgeSpec{ edge := &sqlgraph.EdgeSpec{
Rel: sqlgraph.O2M, Rel: sqlgraph.O2M,
Inverse: true, Inverse: true,
@ -523,10 +530,7 @@ func (uuo *UserUpdateOne) sqlSave(ctx context.Context) (_node *User, err error)
Columns: []string{user.OwnerColumn}, Columns: []string{user.OwnerColumn},
Bidi: false, Bidi: false,
Target: &sqlgraph.EdgeTarget{ Target: &sqlgraph.EdgeTarget{
IDSpec: &sqlgraph.FieldSpec{ IDSpec: sqlgraph.NewFieldSpec(passwordtoken.FieldID, field.TypeInt),
Type: field.TypeInt,
Column: passwordtoken.FieldID,
},
}, },
} }
for _, k := range nodes { for _, k := range nodes {
@ -534,16 +538,17 @@ func (uuo *UserUpdateOne) sqlSave(ctx context.Context) (_node *User, err error)
} }
_spec.Edges.Add = append(_spec.Edges.Add, edge) _spec.Edges.Add = append(_spec.Edges.Add, edge)
} }
_node = &User{config: uuo.config} _node = &User{config: _u.config}
_spec.Assign = _node.assignValues _spec.Assign = _node.assignValues
_spec.ScanValues = _node.scanValues _spec.ScanValues = _node.scanValues
if err = sqlgraph.UpdateNode(ctx, uuo.driver, _spec); err != nil { if err = sqlgraph.UpdateNode(ctx, _u.driver, _spec); err != nil {
if _, ok := err.(*sqlgraph.NotFoundError); ok { if _, ok := err.(*sqlgraph.NotFoundError); ok {
err = &NotFoundError{user.Label} err = &NotFoundError{user.Label}
} else if sqlgraph.IsConstraintError(err) { } else if sqlgraph.IsConstraintError(err) {
err = &ConstraintError{err.Error(), err} err = &ConstraintError{msg: err.Error(), wrap: err}
} }
return nil, err return nil, err
} }
_u.mutation.done = true
return _node, nil return _node, nil
} }

View file

@ -1,66 +0,0 @@
package funcmap
import (
"fmt"
"html/template"
"reflect"
"strings"
"goweb/config"
"github.com/Masterminds/sprig"
"github.com/labstack/gommon/random"
)
var (
// CacheBuster stores a random string used as a cache buster for static files.
CacheBuster = random.String(10)
)
// GetFuncMap provides a template function map
func GetFuncMap() template.FuncMap {
// See http://masterminds.github.io/sprig/ for available funcs
funcMap := sprig.FuncMap()
// Provide a list of custom functions
// Expand this as you add more functions to this package
// Avoid using a name already in use by sprig
f := template.FuncMap{
"hasField": HasField,
"file": File,
"link": Link,
}
for k, v := range f {
funcMap[k] = v
}
return funcMap
}
// HasField checks if an interface contains a given field
func HasField(v interface{}, name string) bool {
rv := reflect.ValueOf(v)
if rv.Kind() == reflect.Ptr {
rv = rv.Elem()
}
if rv.Kind() != reflect.Struct {
return false
}
return rv.FieldByName(name).IsValid()
}
// File appends a cache buster to a given filepath so it can remain cached until the app is restarted
func File(filepath string) string {
return fmt.Sprintf("/%s/%s?v=%s", config.StaticPrefix, filepath, CacheBuster)
}
// Link outputs HTML for a link element, providing the ability to dynamically set the active class
func Link(url, text, currentPath string, classes ...string) template.HTML {
if currentPath == url {
classes = append(classes, "is-active")
}
html := fmt.Sprintf(`<a class="%s" href="%s">%s</a>`, strings.Join(classes, " "), url, text)
return template.HTML(html)
}

View file

@ -1,39 +0,0 @@
package funcmap
import (
"fmt"
"testing"
"goweb/config"
"github.com/stretchr/testify/assert"
)
func TestHasField(t *testing.T) {
type example struct {
name string
}
var e example
assert.True(t, HasField(e, "name"))
assert.False(t, HasField(e, "abcd"))
}
func TestLink(t *testing.T) {
link := string(Link("/abc", "Text", "/abc"))
expected := `<a class="is-active" href="/abc">Text</a>`
assert.Equal(t, expected, link)
link = string(Link("/abc", "Text", "/abc", "first", "second"))
expected = `<a class="first second is-active" href="/abc">Text</a>`
assert.Equal(t, expected, link)
link = string(Link("/abc", "Text", "/def"))
expected = `<a class="" href="/abc">Text</a>`
assert.Equal(t, expected, link)
}
func TestGetFuncMap(t *testing.T) {
file := File("test.png")
expected := fmt.Sprintf("/%s/test.png?v=%s", config.StaticPrefix, CacheBuster)
assert.Equal(t, expected, file)
}

130
go.mod
View file

@ -1,78 +1,68 @@
module goweb module github.com/mikestefanello/pagoda
go 1.17 go 1.24.6
require ( require (
entgo.io/ent v0.9.1 entgo.io/ent v0.14.5
github.com/Masterminds/sprig v2.22.0+incompatible github.com/PuerkitoBio/goquery v1.10.3
github.com/PuerkitoBio/goquery v1.8.0 github.com/go-playground/validator/v10 v10.29.0
github.com/eko/gocache/v2 v2.1.0 github.com/golang-jwt/jwt/v5 v5.3.0
github.com/go-playground/assert/v2 v2.0.1 github.com/gorilla/context v1.1.2
github.com/go-playground/validator/v10 v10.9.0 github.com/gorilla/sessions v1.4.0
github.com/go-redis/redis/v8 v8.11.4 github.com/labstack/echo/v4 v4.14.0
github.com/gorilla/sessions v1.2.1 github.com/mattn/go-sqlite3 v1.14.32
github.com/jackc/pgx/v4 v4.14.1 github.com/maypok86/otter v1.2.4
github.com/joeshaw/envdecode v0.0.0-20200121155833-099f1fc765bd github.com/mikestefanello/backlite v0.6.0
github.com/labstack/echo-contrib v0.11.0 github.com/spf13/afero v1.15.0
github.com/labstack/echo/v4 v4.6.1 github.com/spf13/viper v1.21.0
github.com/labstack/gommon v0.3.1 github.com/stretchr/testify v1.11.1
github.com/stretchr/testify v1.7.0 golang.org/x/crypto v0.46.0
golang.org/x/crypto v0.0.0-20211117183948-ae814b36b871 maragu.dev/gomponents v1.2.0
) )
require ( require (
github.com/Masterminds/goutils v1.1.1 // indirect ariga.io/atlas v0.38.0 // indirect
github.com/Masterminds/semver v1.5.0 // indirect github.com/agext/levenshtein v1.2.3 // indirect
github.com/XiaoMi/pegasus-go-client v0.0.0-20210427083443-f3b6b08bc4c2 // indirect github.com/andybalholm/cascadia v1.3.3 // indirect
github.com/andybalholm/cascadia v1.3.1 // indirect github.com/apparentlymart/go-textseg/v15 v15.0.0 // indirect
github.com/beorn7/perks v1.0.1 // indirect github.com/bmatcuk/doublestar v1.3.4 // indirect
github.com/bradfitz/gomemcache v0.0.0-20190913173617-a41fca850d0b // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
github.com/cenkalti/backoff/v4 v4.1.0 // indirect github.com/dolthub/maphash v0.1.0 // indirect
github.com/cespare/xxhash/v2 v2.1.2 // indirect github.com/fsnotify/fsnotify v1.9.0 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect github.com/gabriel-vasile/mimetype v1.4.12 // indirect
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect github.com/gammazero/deque v1.2.0 // indirect
github.com/go-playground/locales v0.14.0 // indirect github.com/go-openapi/inflect v0.21.5 // indirect
github.com/go-playground/universal-translator v0.18.0 // indirect github.com/go-playground/locales v0.14.1 // indirect
github.com/golang-jwt/jwt v3.2.2+incompatible // indirect github.com/go-playground/universal-translator v0.18.1 // indirect
github.com/golang/protobuf v1.5.2 // indirect github.com/go-viper/mapstructure/v2 v2.4.0 // indirect
github.com/google/uuid v1.3.0 // indirect github.com/google/go-cmp v0.7.0 // indirect
github.com/gorilla/context v1.1.1 // indirect github.com/google/uuid v1.6.0 // indirect
github.com/gorilla/securecookie v1.1.1 // indirect github.com/gorilla/securecookie v1.1.2 // indirect
github.com/huandu/xstrings v1.3.2 // indirect github.com/hashicorp/hcl/v2 v2.24.0 // indirect
github.com/imdario/mergo v0.3.12 // indirect github.com/labstack/gommon v0.4.2 // indirect
github.com/jackc/chunkreader/v2 v2.0.1 // indirect github.com/leodido/go-urn v1.4.0 // indirect
github.com/jackc/pgconn v1.10.1 // indirect github.com/mattn/go-colorable v0.1.14 // indirect
github.com/jackc/pgio v1.0.0 // indirect github.com/mattn/go-isatty v0.0.20 // indirect
github.com/jackc/pgpassfile v1.0.0 // indirect github.com/mitchellh/go-wordwrap v1.0.1 // indirect
github.com/jackc/pgproto3/v2 v2.2.0 // indirect github.com/pelletier/go-toml/v2 v2.2.4 // indirect
github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b // indirect github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
github.com/jackc/pgtype v1.9.1 // indirect github.com/rogpeppe/go-internal v1.10.0 // indirect
github.com/konsorten/go-windows-terminal-sequences v1.0.3 // indirect github.com/sagikazarmark/locafero v0.12.0 // indirect
github.com/leodido/go-urn v1.2.1 // indirect github.com/spf13/cast v1.10.0 // indirect
github.com/mattn/go-colorable v0.1.12 // indirect github.com/spf13/pflag v1.0.10 // indirect
github.com/mattn/go-isatty v0.0.14 // indirect github.com/subosito/gotenv v1.6.0 // indirect
github.com/matttproud/golang_protobuf_extensions v1.0.1 // indirect
github.com/mitchellh/copystructure v1.2.0 // indirect
github.com/mitchellh/reflectwalk v1.0.2 // indirect
github.com/pegasus-kv/thrift v0.13.0 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/prometheus/client_golang v1.10.0 // indirect
github.com/prometheus/client_model v0.2.0 // indirect
github.com/prometheus/common v0.25.0 // indirect
github.com/prometheus/procfs v0.6.0 // indirect
github.com/sirupsen/logrus v1.6.0 // indirect
github.com/spf13/cast v1.3.1 // indirect
github.com/valyala/bytebufferpool v1.0.0 // indirect github.com/valyala/bytebufferpool v1.0.0 // indirect
github.com/valyala/fasttemplate v1.2.1 // indirect github.com/valyala/fasttemplate v1.2.2 // indirect
github.com/vmihailenco/msgpack v4.0.4+incompatible // indirect github.com/zclconf/go-cty v1.17.0 // indirect
golang.org/x/net v0.0.0-20211123203042-d83791d6bcd9 // indirect github.com/zclconf/go-cty-yaml v1.1.0 // indirect
golang.org/x/sys v0.0.0-20211124211545-fe61309f8881 // indirect go.yaml.in/yaml/v3 v3.0.4 // indirect
golang.org/x/text v0.3.7 // indirect golang.org/x/mod v0.31.0 // indirect
golang.org/x/time v0.0.0-20201208040808-7e3f01d25324 // indirect golang.org/x/net v0.48.0 // indirect
google.golang.org/appengine v1.6.1 // indirect golang.org/x/sync v0.19.0 // indirect
google.golang.org/protobuf v1.26.0 // indirect golang.org/x/sys v0.39.0 // indirect
gopkg.in/natefinch/lumberjack.v2 v2.0.0 // indirect golang.org/x/text v0.32.0 // indirect
gopkg.in/tomb.v2 v2.0.0-20161208151619-d5d1b5820637 // indirect golang.org/x/time v0.14.0 // indirect
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect golang.org/x/tools v0.40.0 // indirect
k8s.io/apimachinery v0.0.0-20191123233150-4c4803ed55e3 // indirect gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
) )

1084
go.sum

File diff suppressed because it is too large Load diff

View file

@ -1,82 +0,0 @@
package htmx
import (
"net/http"
"github.com/labstack/echo/v4"
)
// Headers (https://htmx.org/docs/#requests)
const (
HeaderRequest = "HX-Request"
HeaderBoosted = "HX-Boosted"
HeaderTrigger = "HX-Trigger"
HeaderTriggerName = "HX-Trigger-Name"
HeaderTriggerAfterSwap = "HX-Trigger-After-Swap"
HeaderTriggerAfterSettle = "HX-Trigger-After-Settle"
HeaderTarget = "HX-Target"
HeaderPrompt = "HX-Prompt"
HeaderPush = "HX-Push"
HeaderRedirect = "HX-Redirect"
HeaderRefresh = "HX-Refresh"
)
type (
// Request contains data that HTMX provides during requests
Request struct {
Enabled bool
Boosted bool
Trigger string
TriggerName string
Target string
Prompt string
}
// Response contain data that the server can communicate back to HTMX
Response struct {
Push string
Redirect string
Refresh bool
Trigger string
TriggerAfterSwap string
TriggerAfterSettle string
NoContent bool
}
)
// GetRequest extracts HTMX data from the request
func GetRequest(ctx echo.Context) Request {
return Request{
Enabled: ctx.Request().Header.Get(HeaderRequest) == "true",
Boosted: ctx.Request().Header.Get(HeaderBoosted) == "true",
Trigger: ctx.Request().Header.Get(HeaderTrigger),
TriggerName: ctx.Request().Header.Get(HeaderTriggerName),
Target: ctx.Request().Header.Get(HeaderTarget),
Prompt: ctx.Request().Header.Get(HeaderPrompt),
}
}
// Apply applies data from a Response to a server response
func (r Response) Apply(ctx echo.Context) {
if r.Push != "" {
ctx.Response().Header().Set(HeaderPush, r.Push)
}
if r.Redirect != "" {
ctx.Response().Header().Set(HeaderRedirect, r.Redirect)
}
if r.Refresh {
ctx.Response().Header().Set(HeaderRefresh, "true")
}
if r.Trigger != "" {
ctx.Response().Header().Set(HeaderTrigger, r.Trigger)
}
if r.TriggerAfterSwap != "" {
ctx.Response().Header().Set(HeaderTriggerAfterSwap, r.TriggerAfterSwap)
}
if r.TriggerAfterSettle != "" {
ctx.Response().Header().Set(HeaderTriggerAfterSettle, r.TriggerAfterSettle)
}
if r.NoContent {
ctx.Response().Status = http.StatusNoContent
}
}

64
main.go
View file

@ -1,64 +0,0 @@
package main
import (
"context"
"crypto/tls"
"fmt"
"net/http"
"os"
"os/signal"
"time"
"goweb/routes"
"goweb/services"
)
func main() {
// Start a new container
c := services.NewContainer()
defer func() {
if err := c.Shutdown(); err != nil {
c.Web.Logger.Fatal(err)
}
}()
// Build the router
routes.BuildRouter(c)
// Start the server
go func() {
srv := http.Server{
Addr: fmt.Sprintf("%s:%d", c.Config.HTTP.Hostname, c.Config.HTTP.Port),
Handler: c.Web,
ReadTimeout: c.Config.HTTP.ReadTimeout,
WriteTimeout: c.Config.HTTP.WriteTimeout,
IdleTimeout: c.Config.HTTP.IdleTimeout,
}
if c.Config.HTTP.TLS.Enabled {
certs, err := tls.LoadX509KeyPair(c.Config.HTTP.TLS.Certificate, c.Config.HTTP.TLS.Key)
if err != nil {
c.Web.Logger.Fatalf("cannot load TLS certificate: %v", err)
}
srv.TLSConfig = &tls.Config{
Certificates: []tls.Certificate{certs},
}
}
if err := c.Web.StartServer(&srv); err != http.ErrServerClosed {
c.Web.Logger.Fatalf("shutting down the server: v", err)
}
}()
// Wait for interrupt signal to gracefully shutdown the server with a timeout of 10 seconds.
quit := make(chan os.Signal, 1)
signal.Notify(quit, os.Interrupt)
signal.Notify(quit, os.Kill)
<-quit
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
if err := c.Web.Shutdown(ctx); err != nil {
c.Web.Logger.Fatal(err)
}
}

View file

@ -1,89 +0,0 @@
package middleware
import (
"net/http"
"goweb/context"
"goweb/ent"
"goweb/msg"
"goweb/services"
"github.com/labstack/echo/v4"
)
// LoadAuthenticatedUser loads the authenticated user, if one, and stores in context
func LoadAuthenticatedUser(authClient *services.AuthClient) echo.MiddlewareFunc {
return func(next echo.HandlerFunc) echo.HandlerFunc {
return func(c echo.Context) error {
u, err := authClient.GetAuthenticatedUser(c)
switch err.(type) {
case *ent.NotFoundError:
c.Logger().Warn("auth user not found")
case services.NotAuthenticatedError:
case nil:
c.Set(context.AuthenticatedUserKey, u)
c.Logger().Infof("auth user loaded in to context: %d", u.ID)
default:
c.Logger().Errorf("error querying for authenticated user: %v", err)
}
return next(c)
}
}
}
// LoadValidPasswordToken loads a valid password token entity that matches the user and token
// provided in path parameters
// If the token is invalid, the user will be redirected to the forgot password route
// This requires that the user owning the token is loaded in to context
func LoadValidPasswordToken(authClient *services.AuthClient) echo.MiddlewareFunc {
return func(next echo.HandlerFunc) echo.HandlerFunc {
return func(c echo.Context) error {
// Extract the user parameter
if c.Get(context.UserKey) == nil {
return echo.NewHTTPError(http.StatusInternalServerError)
}
usr := c.Get(context.UserKey).(*ent.User)
token, err := authClient.GetValidPasswordToken(c, c.Param("password_token"), usr.ID)
switch err.(type) {
case nil:
c.Set(context.PasswordTokenKey, token)
return next(c)
case services.InvalidPasswordTokenError:
msg.Warning(c, "The link is either invalid or has expired. Please request a new one.")
return c.Redirect(http.StatusFound, c.Echo().Reverse("forgot_password"))
default:
c.Logger().Error(err)
return echo.NewHTTPError(http.StatusInternalServerError)
}
}
}
}
// RequireAuthentication requires that the user be authenticated in order to proceed
func RequireAuthentication() echo.MiddlewareFunc {
return func(next echo.HandlerFunc) echo.HandlerFunc {
return func(c echo.Context) error {
if u := c.Get(context.AuthenticatedUserKey); u == nil {
return echo.NewHTTPError(http.StatusUnauthorized)
}
return next(c)
}
}
}
// RequireNoAuthentication requires that the user not be authenticated in order to proceed
func RequireNoAuthentication() echo.MiddlewareFunc {
return func(next echo.HandlerFunc) echo.HandlerFunc {
return func(c echo.Context) error {
if u := c.Get(context.AuthenticatedUserKey); u != nil {
return echo.NewHTTPError(http.StatusForbidden)
}
return next(c)
}
}
}

View file

@ -1,94 +0,0 @@
package middleware
import (
"fmt"
"net/http"
"time"
"goweb/context"
"github.com/eko/gocache/v2/cache"
"github.com/eko/gocache/v2/marshaler"
"github.com/go-redis/redis/v8"
"github.com/labstack/echo/v4"
)
// CachedPage is what is used to store a rendered Page in the cache
type CachedPage struct {
// URL stores the URL of the requested page
URL string
// HTML stores the complete HTML of the rendered Page
HTML []byte
// StatusCode stores the HTTP status code
StatusCode int
// Headers stores the HTTP headers
Headers map[string]string
}
// ServeCachedPage attempts to load a page from the cache by matching on the complete request URL
// If a page is cached for the requested URL, it will be served here and the request terminated.
// Any request made by an authenticated user or that is not a GET will be skipped.
func ServeCachedPage(ch *cache.Cache) echo.MiddlewareFunc {
return func(next echo.HandlerFunc) echo.HandlerFunc {
return func(c echo.Context) error {
// Skip non GET requests
if c.Request().Method != http.MethodGet {
return next(c)
}
// Skip if the user is authenticated
if c.Get(context.AuthenticatedUserKey) != nil {
return next(c)
}
// Attempt to load from cache
res, err := marshaler.New(ch).Get(
c.Request().Context(),
c.Request().URL.String(),
new(CachedPage),
)
if err != nil {
if err == redis.Nil {
c.Logger().Info("no cached page found")
} else {
c.Logger().Errorf("failed getting cached page: %v", err)
}
return next(c)
}
page, ok := res.(*CachedPage)
if !ok {
c.Logger().Errorf("failed casting cached page")
return next(c)
}
// Set any headers
if page.Headers != nil {
for k, v := range page.Headers {
c.Response().Header().Set(k, v)
}
}
c.Logger().Info("serving cached page")
return c.HTMLBlob(page.StatusCode, page.HTML)
}
}
}
// CacheControl sets a Cache-Control header with a given max age
func CacheControl(maxAge time.Duration) echo.MiddlewareFunc {
return func(next echo.HandlerFunc) echo.HandlerFunc {
return func(c echo.Context) error {
v := "no-cache, no-store"
if maxAge > 0 {
v = fmt.Sprintf("public, max-age=%.0f", maxAge.Seconds())
}
c.Response().Header().Set("Cache-Control", v)
return next(c)
}
}
}

View file

@ -1,55 +0,0 @@
package middleware
import (
"context"
"net/http"
"testing"
"time"
"goweb/tests"
"github.com/stretchr/testify/require"
"github.com/eko/gocache/v2/marshaler"
"github.com/stretchr/testify/assert"
)
func TestServeCachedPage(t *testing.T) {
// Cache a page
cp := CachedPage{
URL: "/cache",
HTML: []byte("html"),
Headers: make(map[string]string),
StatusCode: http.StatusCreated,
}
cp.Headers["a"] = "b"
cp.Headers["c"] = "d"
err := marshaler.New(c.Cache).Set(context.Background(), cp.URL, cp, nil)
require.NoError(t, err)
// Request the URL of the cached page
ctx, rec := tests.NewContext(c.Web, cp.URL)
err = tests.ExecuteMiddleware(ctx, ServeCachedPage(c.Cache))
assert.NoError(t, err)
assert.Equal(t, cp.StatusCode, ctx.Response().Status)
assert.Equal(t, cp.Headers["a"], ctx.Response().Header().Get("a"))
assert.Equal(t, cp.Headers["c"], ctx.Response().Header().Get("c"))
assert.Equal(t, cp.HTML, rec.Body.Bytes())
// Login and try again
tests.InitSession(ctx)
err = c.Auth.Login(ctx, usr.ID)
require.NoError(t, err)
_ = tests.ExecuteMiddleware(ctx, LoadAuthenticatedUser(c.Auth))
err = tests.ExecuteMiddleware(ctx, ServeCachedPage(c.Cache))
assert.Nil(t, err)
}
func TestCacheControl(t *testing.T) {
ctx, _ := tests.NewContext(c.Web, "/")
_ = tests.ExecuteMiddleware(ctx, CacheControl(time.Second*5))
assert.Equal(t, "public, max-age=5", ctx.Response().Header().Get("Cache-Control"))
_ = tests.ExecuteMiddleware(ctx, CacheControl(0))
assert.Equal(t, "no-cache, no-store", ctx.Response().Header().Get("Cache-Control"))
}

View file

@ -1,20 +0,0 @@
package middleware
import (
"fmt"
"github.com/labstack/echo/v4"
)
// LogRequestID includes the request ID in all logs for the given request
// This requires that middleware that includes the request ID first execute
func LogRequestID() echo.MiddlewareFunc {
return func(next echo.HandlerFunc) echo.HandlerFunc {
return func(c echo.Context) error {
rID := c.Response().Header().Get(echo.HeaderXRequestID)
format := `{"time":"${time_rfc3339_nano}","id":"%s","level":"${level}","prefix":"${prefix}","file":"${short_file}","line":"${line}"}`
c.Logger().SetHeader(fmt.Sprintf(format, rID))
return next(c)
}
}
}

View file

@ -1,27 +0,0 @@
package middleware
import (
"bytes"
"fmt"
"testing"
"goweb/tests"
"github.com/labstack/echo/v4"
"github.com/stretchr/testify/assert"
echomw "github.com/labstack/echo/v4/middleware"
)
func TestLogRequestID(t *testing.T) {
ctx, _ := tests.NewContext(c.Web, "/")
_ = tests.ExecuteMiddleware(ctx, echomw.RequestID())
_ = tests.ExecuteMiddleware(ctx, LogRequestID())
var buf bytes.Buffer
ctx.Logger().SetOutput(&buf)
ctx.Logger().Info("test")
rID := ctx.Response().Header().Get(echo.HeaderXRequestID)
assert.Contains(t, buf.String(), fmt.Sprintf(`id":"%s"`, rID))
}

62
pkg/context/context.go Normal file
View file

@ -0,0 +1,62 @@
package context
import (
"context"
"errors"
"github.com/labstack/echo/v4"
)
const (
// AuthenticatedUserKey is the key used to store the authenticated user in context.
AuthenticatedUserKey = "auth_user"
// UserKey is the key used to store a user in context.
UserKey = "user"
// FormKey is the key used to store a form in context.
FormKey = "form"
// PasswordTokenKey is the key used to store a password token in context.
PasswordTokenKey = "password_token"
// LoggerKey is the key used to store a structured logger in context.
LoggerKey = "logger"
// SessionKey is the key used to store the session data in context.
SessionKey = "session"
// HTMXRequestKey is the key used to store the HTMX request data in context.
HTMXRequestKey = "htmx"
// CSRFKey is the key used to store the CSRF token in context.
CSRFKey = "csrf"
// ConfigKey is the key used to store the configuration in context.
ConfigKey = "config"
// AdminEntityKey is the key used to store the entity being operated on in the admin panel.
AdminEntityKey = "admin:entity"
// AdminEntityIDKey is the key used to store the ID of the entity being operated on in the admin panel.
AdminEntityIDKey = "admin:entity_id"
)
// IsCanceledError determines if an error is due to a context cancellation.
func IsCanceledError(err error) bool {
return errors.Is(err, context.Canceled)
}
// Cache checks if a value of a given type exists in the Echo context for a given key and returns that, otherwise
// it will use a callback to generate a value, which is stored in the context then returned. This allows you to
// only generate items only once for a given request.
func Cache[T any](ctx echo.Context, key string, gen func(echo.Context) T) T {
if val := ctx.Get(key); val != nil {
if v, ok := val.(T); ok {
return v
}
}
val := gen(ctx)
ctx.Set(key, val)
return val
}

View file

@ -0,0 +1,47 @@
package context
import (
"context"
"errors"
"testing"
"time"
"github.com/labstack/echo/v4"
"github.com/stretchr/testify/assert"
)
func TestIsCanceled(t *testing.T) {
ctx, cancel := context.WithCancel(context.Background())
assert.False(t, IsCanceledError(ctx.Err()))
cancel()
assert.True(t, IsCanceledError(ctx.Err()))
ctx, cancel = context.WithTimeout(context.Background(), time.Microsecond*5)
<-ctx.Done()
cancel()
assert.False(t, IsCanceledError(ctx.Err()))
assert.False(t, IsCanceledError(errors.New("test error")))
}
func TestCache(t *testing.T) {
ctx := echo.New().NewContext(nil, nil)
key := "testing"
value := "hello"
called := 0
callback := func(ctx echo.Context) string {
called++
return value
}
assert.Nil(t, ctx.Get(key))
got := Cache(ctx, key, callback)
assert.Equal(t, value, got)
assert.Equal(t, 1, called)
got = Cache(ctx, key, callback)
assert.Equal(t, value, got)
assert.Equal(t, 1, called)
}

55
pkg/form/form.go Normal file
View file

@ -0,0 +1,55 @@
package form
import (
"github.com/labstack/echo/v4"
"github.com/mikestefanello/pagoda/pkg/context"
)
// Form represents a form that can be submitted and validated.
type Form interface {
// Submit marks the form as submitted, stores a pointer to it in the context, binds the request
// values to the struct fields, and validates the input based on the struct tags.
// Returns a validator.ValidationErrors, if the form values were not valid, or an echo.HTTPError,
// if the request failed to process.
Submit(c echo.Context, form any) error
// IsSubmitted returns true if the form was submitted.
IsSubmitted() bool
// IsValid returns true if the form has no validation errors.
IsValid() bool
// IsDone returns true if the form was submitted and has no validation errors.
IsDone() bool
// FieldHasErrors returns true if a given struct field has validation errors.
FieldHasErrors(fieldName string) bool
// SetFieldError sets a validation error message for a given struct field.
SetFieldError(fieldName string, message string)
// GetFieldErrors returns the validation errors for a given struct field.
GetFieldErrors(fieldName string) []string
}
// Get gets a form from the context or initializes a new copy if one is not set.
func Get[T any](ctx echo.Context) *T {
if v := ctx.Get(context.FormKey); v != nil {
if form, ok := v.(*T); ok {
return form
}
}
var v T
return &v
}
// Clear removes the form set in the context.
func Clear(ctx echo.Context) {
ctx.Set(context.FormKey, nil)
}
// Submit submits a form.
// See Form.Submit().
func Submit(ctx echo.Context, form Form) error {
return form.Submit(ctx, form)
}

67
pkg/form/form_test.go Normal file
View file

@ -0,0 +1,67 @@
package form
import (
"testing"
"github.com/labstack/echo/v4"
"github.com/mikestefanello/pagoda/pkg/context"
"github.com/mikestefanello/pagoda/pkg/tests"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
type mockForm struct {
called bool
Submission
}
func (m *mockForm) Submit(_ echo.Context, _ any) error {
m.called = true
return nil
}
func TestSubmit(t *testing.T) {
m := mockForm{}
ctx, _ := tests.NewContext(echo.New(), "/")
err := Submit(ctx, &m)
require.NoError(t, err)
assert.True(t, m.called)
}
func TestGetClear(t *testing.T) {
e := echo.New()
type example struct {
Name string `form:"name"`
}
t.Run("get empty context", func(t *testing.T) {
// Empty context, still return a form.
ctx, _ := tests.NewContext(e, "/")
form := Get[example](ctx)
assert.NotNil(t, form)
})
t.Run("get non-empty context", func(t *testing.T) {
form := example{
Name: "test",
}
ctx, _ := tests.NewContext(e, "/")
ctx.Set(context.FormKey, &form)
// Get again and expect the values were stored.
got := Get[example](ctx)
require.NotNil(t, got)
assert.Equal(t, "test", got.Name)
// Attempt getting a different type to ensure there's no panic.
ret := Get[int](ctx)
require.NotNil(t, ret)
// Clear.
Clear(ctx)
got = Get[example](ctx)
require.NotNil(t, got)
assert.Empty(t, got.Name)
})
}

105
pkg/form/submission.go Normal file
View file

@ -0,0 +1,105 @@
package form
import (
"fmt"
"net/http"
"github.com/go-playground/validator/v10"
"github.com/mikestefanello/pagoda/pkg/context"
"github.com/labstack/echo/v4"
)
// Submission represents the state of the submission of a form, not including the form itself.
// This satisfies the Form interface.
type Submission struct {
// isSubmitted indicates if the form has been submitted.
isSubmitted bool
// errors stores a slice of error message strings keyed by form struct field name.
errors map[string][]string
}
func (f *Submission) Submit(ctx echo.Context, form any) error {
f.isSubmitted = true
// Set in context so the form can later be retrieved.
ctx.Set(context.FormKey, form)
// Bind the values from the incoming request to the form struct.
if err := ctx.Bind(form); err != nil {
return echo.NewHTTPError(http.StatusInternalServerError, fmt.Sprintf("unable to bind form: %v", err))
}
// Validate the form.
if err := ctx.Validate(form); err != nil {
f.setErrorMessages(err)
return err
}
return nil
}
func (f *Submission) IsSubmitted() bool {
return f.isSubmitted
}
func (f *Submission) IsValid() bool {
if f.errors == nil {
return true
}
return len(f.errors) == 0
}
func (f *Submission) IsDone() bool {
return f.IsSubmitted() && f.IsValid()
}
func (f *Submission) FieldHasErrors(fieldName string) bool {
return len(f.GetFieldErrors(fieldName)) > 0
}
func (f *Submission) SetFieldError(fieldName string, message string) {
if f.errors == nil {
f.errors = make(map[string][]string)
}
f.errors[fieldName] = append(f.errors[fieldName], message)
}
func (f *Submission) GetFieldErrors(fieldName string) []string {
if f.errors == nil {
return []string{}
}
return f.errors[fieldName]
}
// setErrorMessages sets errors messages on the submission for all fields that failed validation.
func (f *Submission) setErrorMessages(err error) {
// Only this is supported right now
ves, ok := err.(validator.ValidationErrors)
if !ok {
return
}
for _, ve := range ves {
var message string
// Provide better error messages depending on the failed validation tag.
// This should be expanded as you use additional tags in your validation.
switch ve.Tag() {
case "required":
message = "This field is required."
case "email":
message = "Enter a valid email address."
case "eqfield":
message = "Does not match."
case "gte":
message = fmt.Sprintf("Must be greater than or equal to %v.", ve.Param())
default:
message = "Invalid value."
}
// Add the error.
f.SetFieldError(ve.Field(), message)
}
}

View file

@ -0,0 +1,57 @@
package form
import (
"net/http"
"net/http/httptest"
"strings"
"testing"
"github.com/go-playground/validator/v10"
"github.com/labstack/echo/v4"
"github.com/mikestefanello/pagoda/pkg/services"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestFormSubmission(t *testing.T) {
type formTest struct {
Name string `form:"name" validate:"required"`
Email string `form:"email" validate:"required,email"`
Submission
}
e := echo.New()
e.Validator = services.NewValidator()
t.Run("valid request", func(t *testing.T) {
req := httptest.NewRequest(http.MethodPost, "/", strings.NewReader("email=a@a.com"))
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
ctx := e.NewContext(req, httptest.NewRecorder())
var form formTest
err := form.Submit(ctx, &form)
assert.IsType(t, validator.ValidationErrors{}, err)
assert.Empty(t, form.Name)
assert.Equal(t, "a@a.com", form.Email)
assert.False(t, form.IsValid())
assert.True(t, form.FieldHasErrors("Name"))
assert.False(t, form.FieldHasErrors("Email"))
require.Len(t, form.GetFieldErrors("Name"), 1)
assert.Len(t, form.GetFieldErrors("Email"), 0)
assert.Equal(t, "This field is required.", form.GetFieldErrors("Name")[0])
assert.False(t, form.IsDone())
formInCtx := Get[formTest](ctx)
require.NotNil(t, formInCtx)
assert.Equal(t, form.Email, formInCtx.Email)
})
t.Run("invalid request", func(t *testing.T) {
req := httptest.NewRequest(http.MethodPost, "/", strings.NewReader("abc=abc"))
ctx := e.NewContext(req, httptest.NewRecorder())
var form formTest
err := form.Submit(ctx, &form)
assert.IsType(t, new(echo.HTTPError), err)
})
}

198
pkg/handlers/admin.go Normal file
View file

@ -0,0 +1,198 @@
package handlers
import (
"fmt"
"net/http"
"strconv"
"strings"
"time"
"github.com/labstack/echo/v4"
"github.com/mikestefanello/backlite/ui"
"github.com/mikestefanello/pagoda/ent"
"github.com/mikestefanello/pagoda/ent/admin"
"github.com/mikestefanello/pagoda/pkg/context"
"github.com/mikestefanello/pagoda/pkg/middleware"
"github.com/mikestefanello/pagoda/pkg/msg"
"github.com/mikestefanello/pagoda/pkg/pager"
"github.com/mikestefanello/pagoda/pkg/redirect"
"github.com/mikestefanello/pagoda/pkg/routenames"
"github.com/mikestefanello/pagoda/pkg/services"
"github.com/mikestefanello/pagoda/pkg/ui/pages"
)
type Admin struct {
orm *ent.Client
admin *admin.Handler
backlite *ui.Handler
}
func init() {
Register(new(Admin))
}
func (h *Admin) Init(c *services.Container) error {
var err error
h.orm = c.ORM
h.admin = admin.NewHandler(h.orm, admin.HandlerConfig{
ItemsPerPage: 25,
PageQueryKey: pager.QueryKey,
TimeFormat: time.DateTime,
})
h.backlite, err = ui.NewHandler(ui.Config{
DB: c.Database,
BasePath: "/admin/tasks",
ItemsPerPage: 25,
ReleaseAfter: c.Config.Tasks.ReleaseAfter,
})
return err
}
func (h *Admin) Routes(g *echo.Group) {
ag := g.Group("/admin", middleware.RequireAdmin)
entities := ag.Group("/entity")
for _, n := range admin.GetEntityTypes() {
ng := entities.Group(fmt.Sprintf("/%s", strings.ToLower(n.GetName())))
ng.GET("", h.EntityList(n)).
Name = routenames.AdminEntityList(n.GetName())
ng.GET("/add", h.EntityAdd(n)).
Name = routenames.AdminEntityAdd(n.GetName())
ng.POST("/add", h.EntityAddSubmit(n)).
Name = routenames.AdminEntityAddSubmit(n.GetName())
ng.GET("/:id/edit", h.EntityEdit(n), h.middlewareEntityLoad(n)).
Name = routenames.AdminEntityEdit(n.GetName())
ng.POST("/:id/edit", h.EntityEditSubmit(n), h.middlewareEntityLoad(n)).
Name = routenames.AdminEntityEditSubmit(n.GetName())
ng.GET("/:id/delete", h.EntityDelete(n), h.middlewareEntityLoad(n)).
Name = routenames.AdminEntityDelete(n.GetName())
ng.POST("/:id/delete", h.EntityDeleteSubmit(n), h.middlewareEntityLoad(n)).
Name = routenames.AdminEntityDeleteSubmit(n.GetName())
}
tasks := ag.Group("/tasks")
tasks.GET("", h.Backlite(h.backlite.Running)).Name = routenames.AdminTasks
tasks.GET("/succeeded", h.Backlite(h.backlite.Succeeded))
tasks.GET("/failed", h.Backlite(h.backlite.Failed))
tasks.GET("/upcoming", h.Backlite(h.backlite.Upcoming))
tasks.GET("/task/:id", h.Backlite(h.backlite.Task))
tasks.GET("/completed/:id", h.Backlite(h.backlite.TaskCompleted))
}
// middlewareEntityLoad is middleware to extract the entity ID and attempt to load the given entity.
func (h *Admin) middlewareEntityLoad(n admin.EntityType) echo.MiddlewareFunc {
return func(next echo.HandlerFunc) echo.HandlerFunc {
return func(ctx echo.Context) error {
id, err := strconv.Atoi(ctx.Param("id"))
if err != nil {
return echo.NewHTTPError(http.StatusBadRequest, "invalid entity ID")
}
entity, err := h.admin.Get(ctx, n, id)
switch {
case err == nil:
ctx.Set(context.AdminEntityIDKey, id)
ctx.Set(context.AdminEntityKey, map[string][]string(entity))
return next(ctx)
case ent.IsNotFound(err):
return echo.NewHTTPError(http.StatusNotFound, "entity not found")
default:
return echo.NewHTTPError(http.StatusInternalServerError, err)
}
}
}
}
func (h *Admin) EntityList(n admin.EntityType) echo.HandlerFunc {
return func(ctx echo.Context) error {
list, err := h.admin.List(ctx, n)
if err != nil {
return echo.NewHTTPError(http.StatusInternalServerError, err)
}
return pages.AdminEntityList(ctx, n, list)
}
}
func (h *Admin) EntityAdd(n admin.EntityType) echo.HandlerFunc {
return func(ctx echo.Context) error {
return pages.AdminEntityInput(ctx, n, nil)
}
}
func (h *Admin) EntityAddSubmit(n admin.EntityType) echo.HandlerFunc {
return func(ctx echo.Context) error {
err := h.admin.Create(ctx, n)
if err != nil {
msg.Error(ctx, err.Error())
return h.EntityAdd(n)(ctx)
}
msg.Success(ctx, fmt.Sprintf("Successfully added %s.", n.GetName()))
return redirect.
New(ctx).
Route(routenames.AdminEntityList(n.GetName())).
StatusCode(http.StatusFound).
Go()
}
}
func (h *Admin) EntityEdit(n admin.EntityType) echo.HandlerFunc {
return func(ctx echo.Context) error {
v := ctx.Get(context.AdminEntityKey).(map[string][]string)
return pages.AdminEntityInput(ctx, n, v)
}
}
func (h *Admin) EntityEditSubmit(n admin.EntityType) echo.HandlerFunc {
return func(ctx echo.Context) error {
id := ctx.Get(context.AdminEntityIDKey).(int)
err := h.admin.Update(ctx, n, id)
if err != nil {
msg.Error(ctx, err.Error())
return h.EntityEdit(n)(ctx)
}
msg.Success(ctx, fmt.Sprintf("Updated %s.", n.GetName()))
return redirect.
New(ctx).
Route(routenames.AdminEntityList(n.GetName())).
StatusCode(http.StatusFound).
Go()
}
}
func (h *Admin) EntityDelete(n admin.EntityType) echo.HandlerFunc {
return func(ctx echo.Context) error {
return pages.AdminEntityDelete(ctx, n)
}
}
func (h *Admin) EntityDeleteSubmit(n admin.EntityType) echo.HandlerFunc {
return func(ctx echo.Context) error {
id := ctx.Get(context.AdminEntityIDKey).(int)
if err := h.admin.Delete(ctx, n, id); err != nil {
msg.Error(ctx, err.Error())
return h.EntityDelete(n)(ctx)
}
msg.Success(ctx, fmt.Sprintf("Successfully deleted %s (ID %d).", n.GetName(), id))
return redirect.
New(ctx).
Route(routenames.AdminEntityList(n.GetName())).
StatusCode(http.StatusFound).
Go()
}
}
func (h *Admin) Backlite(handler func(http.ResponseWriter, *http.Request) error) echo.HandlerFunc {
return func(c echo.Context) error {
if id := c.Param("id"); id != "" {
c.Request().SetPathValue("task", id)
}
return handler(c.Response().Writer, c.Request())
}
}

380
pkg/handlers/auth.go Normal file
View file

@ -0,0 +1,380 @@
package handlers
import (
"fmt"
"strings"
"github.com/go-playground/validator/v10"
"github.com/labstack/echo/v4"
"github.com/mikestefanello/pagoda/config"
"github.com/mikestefanello/pagoda/ent"
"github.com/mikestefanello/pagoda/ent/user"
"github.com/mikestefanello/pagoda/pkg/context"
"github.com/mikestefanello/pagoda/pkg/form"
"github.com/mikestefanello/pagoda/pkg/log"
"github.com/mikestefanello/pagoda/pkg/middleware"
"github.com/mikestefanello/pagoda/pkg/msg"
"github.com/mikestefanello/pagoda/pkg/redirect"
"github.com/mikestefanello/pagoda/pkg/routenames"
"github.com/mikestefanello/pagoda/pkg/services"
"github.com/mikestefanello/pagoda/pkg/ui/emails"
"github.com/mikestefanello/pagoda/pkg/ui/forms"
"github.com/mikestefanello/pagoda/pkg/ui/pages"
)
type Auth struct {
config *config.Config
auth *services.AuthClient
mail *services.MailClient
orm *ent.Client
}
func init() {
Register(new(Auth))
}
func (h *Auth) Init(c *services.Container) error {
h.config = c.Config
h.orm = c.ORM
h.auth = c.Auth
h.mail = c.Mail
return nil
}
func (h *Auth) Routes(g *echo.Group) {
g.GET("/logout", h.Logout, middleware.RequireAuthentication).Name = routenames.Logout
g.GET("/email/verify/:token", h.VerifyEmail).Name = routenames.VerifyEmail
noAuth := g.Group("/user", middleware.RequireNoAuthentication)
noAuth.GET("/login", h.LoginPage).Name = routenames.Login
noAuth.POST("/login", h.LoginSubmit).Name = routenames.LoginSubmit
noAuth.GET("/register", h.RegisterPage).Name = routenames.Register
noAuth.POST("/register", h.RegisterSubmit).Name = routenames.RegisterSubmit
noAuth.GET("/password", h.ForgotPasswordPage).Name = routenames.ForgotPassword
noAuth.POST("/password", h.ForgotPasswordSubmit).Name = routenames.ForgotPasswordSubmit
resetGroup := noAuth.Group("/password/reset",
middleware.LoadUser(h.orm),
middleware.LoadValidPasswordToken(h.auth),
)
resetGroup.GET("/token/:user/:password_token/:token", h.ResetPasswordPage).Name = routenames.ResetPassword
resetGroup.POST("/token/:user/:password_token/:token", h.ResetPasswordSubmit).Name = routenames.ResetPasswordSubmit
}
func (h *Auth) ForgotPasswordPage(ctx echo.Context) error {
return pages.ForgotPassword(ctx, form.Get[forms.ForgotPassword](ctx))
}
func (h *Auth) ForgotPasswordSubmit(ctx echo.Context) error {
var input forms.ForgotPassword
succeed := func() error {
form.Clear(ctx)
msg.Success(ctx, "An email containing a link to reset your password will be sent to this address if it exists in our system.")
return h.ForgotPasswordPage(ctx)
}
err := form.Submit(ctx, &input)
switch err.(type) {
case nil:
case validator.ValidationErrors:
return h.ForgotPasswordPage(ctx)
default:
return err
}
// Attempt to load the user.
u, err := h.orm.User.
Query().
Where(user.Email(strings.ToLower(input.Email))).
Only(ctx.Request().Context())
switch err.(type) {
case *ent.NotFoundError:
return succeed()
case nil:
default:
return fail(err, "error querying user during forgot password")
}
// Generate the token.
token, pt, err := h.auth.GeneratePasswordResetToken(ctx, u.ID)
if err != nil {
return fail(err, "error generating password reset token")
}
log.Ctx(ctx).Info("generated password reset token",
"user_id", u.ID,
)
// Email the user.
url := ctx.Echo().Reverse(routenames.ResetPassword, u.ID, pt.ID, token)
err = h.mail.
Compose().
To(u.Email).
Subject("Reset your password").
Body(fmt.Sprintf("Go here to reset your password: %s", h.config.App.Host+url)).
Send(ctx)
if err != nil {
return fail(err, "error sending password reset email")
}
return succeed()
}
func (h *Auth) LoginPage(ctx echo.Context) error {
return pages.Login(ctx, form.Get[forms.Login](ctx))
}
func (h *Auth) LoginSubmit(ctx echo.Context) error {
var input forms.Login
authFailed := func() error {
input.SetFieldError("Email", "")
input.SetFieldError("Password", "")
msg.Error(ctx, "Invalid credentials. Please try again.")
return h.LoginPage(ctx)
}
err := form.Submit(ctx, &input)
switch err.(type) {
case nil:
case validator.ValidationErrors:
return h.LoginPage(ctx)
default:
return err
}
// Attempt to load the user.
u, err := h.orm.User.
Query().
Where(user.Email(strings.ToLower(input.Email))).
Only(ctx.Request().Context())
switch err.(type) {
case *ent.NotFoundError:
return authFailed()
case nil:
default:
return fail(err, "error querying user during login")
}
// Check if the password is correct.
err = h.auth.CheckPassword(input.Password, u.Password)
if err != nil {
return authFailed()
}
// Log the user in.
err = h.auth.Login(ctx, u.ID)
if err != nil {
return fail(err, "unable to log in user")
}
msg.Success(ctx, fmt.Sprintf("Welcome back, %s. You are now logged in.", u.Name))
return redirect.New(ctx).
Route(routenames.Home).
Go()
}
func (h *Auth) Logout(ctx echo.Context) error {
if err := h.auth.Logout(ctx); err == nil {
msg.Success(ctx, "You have been logged out successfully.")
} else {
msg.Error(ctx, "An error occurred. Please try again.")
}
return redirect.New(ctx).
Route(routenames.Home).
Go()
}
func (h *Auth) RegisterPage(ctx echo.Context) error {
return pages.Register(ctx, form.Get[forms.Register](ctx))
}
func (h *Auth) RegisterSubmit(ctx echo.Context) error {
var input forms.Register
err := form.Submit(ctx, &input)
switch err.(type) {
case nil:
case validator.ValidationErrors:
return h.RegisterPage(ctx)
default:
return err
}
// Attempt creating the user.
u, err := h.orm.User.
Create().
SetName(input.Name).
SetEmail(input.Email).
SetPassword(input.Password).
Save(ctx.Request().Context())
switch err.(type) {
case nil:
log.Ctx(ctx).Info("user created",
"user_name", u.Name,
"user_id", u.ID,
)
case *ent.ConstraintError:
msg.Warning(ctx, "A user with this email address already exists. Please log in.")
return redirect.New(ctx).
Route(routenames.Login).
Go()
default:
return fail(err, "unable to create user")
}
// Log the user in.
err = h.auth.Login(ctx, u.ID)
if err != nil {
log.Ctx(ctx).Error("unable to log user in",
"error", err,
"user_id", u.ID,
)
msg.Info(ctx, "Your account has been created.")
return redirect.New(ctx).
Route(routenames.Login).
Go()
}
msg.Success(ctx, "Your account has been created. You are now logged in.")
// Send the verification email.
h.sendVerificationEmail(ctx, u)
return redirect.New(ctx).
Route(routenames.Home).
Go()
}
func (h *Auth) sendVerificationEmail(ctx echo.Context, usr *ent.User) {
// Generate a token.
token, err := h.auth.GenerateEmailVerificationToken(usr.Email)
if err != nil {
log.Ctx(ctx).Error("unable to generate email verification token",
"user_id", usr.ID,
"error", err,
)
return
}
// Send the email.
err = h.mail.
Compose().
To(usr.Email).
Subject("Confirm your email address").
Component(emails.ConfirmEmailAddress(ctx, usr.Name, token)).
Send(ctx)
if err != nil {
log.Ctx(ctx).Error("unable to send email verification link",
"user_id", usr.ID,
"error", err,
)
return
}
msg.Info(ctx, "An email was sent to you to verify your email address.")
}
func (h *Auth) ResetPasswordPage(ctx echo.Context) error {
return pages.ResetPassword(ctx, form.Get[forms.ResetPassword](ctx))
}
func (h *Auth) ResetPasswordSubmit(ctx echo.Context) error {
var input forms.ResetPassword
err := form.Submit(ctx, &input)
switch err.(type) {
case nil:
case validator.ValidationErrors:
return h.ResetPasswordPage(ctx)
default:
return err
}
// Get the requesting user.
usr := ctx.Get(context.UserKey).(*ent.User)
// Update the user.
_, err = usr.
Update().
SetPassword(input.Password).
Save(ctx.Request().Context())
if err != nil {
return fail(err, "unable to update password")
}
// Delete all password tokens for this user.
err = h.auth.DeletePasswordTokens(ctx, usr.ID)
if err != nil {
return fail(err, "unable to delete password tokens")
}
msg.Success(ctx, "Your password has been updated.")
return redirect.New(ctx).
Route(routenames.Login).
Go()
}
func (h *Auth) VerifyEmail(ctx echo.Context) error {
var usr *ent.User
// Validate the token.
token := ctx.Param("token")
email, err := h.auth.ValidateEmailVerificationToken(token)
if err != nil {
msg.Warning(ctx, "The link is either invalid or has expired.")
return redirect.New(ctx).
Route(routenames.Home).
Go()
}
// Check if it matches the authenticated user.
if u := ctx.Get(context.AuthenticatedUserKey); u != nil {
authUser := u.(*ent.User)
if authUser.Email == email {
usr = authUser
}
}
// Query to find a matching user, if needed.
if usr == nil {
usr, err = h.orm.User.
Query().
Where(user.Email(email)).
Only(ctx.Request().Context())
if err != nil {
return fail(err, "query failed loading email verification token user")
}
}
// Verify the user, if needed.
if !usr.Verified {
usr, err = usr.
Update().
SetVerified(true).
Save(ctx.Request().Context())
if err != nil {
return fail(err, "failed to set user as verified")
}
}
msg.Success(ctx, "Your email has been successfully verified.")
return redirect.New(ctx).
Route(routenames.Home).
Go()
}

76
pkg/handlers/cache.go Normal file
View file

@ -0,0 +1,76 @@
package handlers
import (
"errors"
"time"
"github.com/labstack/echo/v4"
"github.com/mikestefanello/pagoda/pkg/form"
"github.com/mikestefanello/pagoda/pkg/routenames"
"github.com/mikestefanello/pagoda/pkg/services"
"github.com/mikestefanello/pagoda/pkg/ui/forms"
"github.com/mikestefanello/pagoda/pkg/ui/pages"
)
type Cache struct {
cache *services.CacheClient
}
func init() {
Register(new(Cache))
}
func (h *Cache) Init(c *services.Container) error {
h.cache = c.Cache
return nil
}
func (h *Cache) Routes(g *echo.Group) {
g.GET("/cache", h.Page).Name = routenames.Cache
g.POST("/cache", h.Submit).Name = routenames.CacheSubmit
}
func (h *Cache) Page(ctx echo.Context) error {
f := form.Get[forms.Cache](ctx)
// Fetch the value from the cache.
value, err := h.cache.
Get().
Key("page_cache_example").
Fetch(ctx.Request().Context())
// Store the value in the form, so it can be rendered, if found.
switch {
case err == nil:
f.CurrentValue = value.(string)
case errors.Is(err, services.ErrCacheMiss):
default:
return fail(err, "failed to fetch from cache")
}
return pages.UpdateCache(ctx, f)
}
func (h *Cache) Submit(ctx echo.Context) error {
var input forms.Cache
if err := form.Submit(ctx, &input); err != nil {
return err
}
// Set the cache.
err := h.cache.
Set().
Key("page_cache_example").
Data(input.Value).
Expiration(30 * time.Minute).
Save(ctx.Request().Context())
if err != nil {
return fail(err, "unable to set cache")
}
form.Clear(ctx)
return h.Page(ctx)
}

62
pkg/handlers/contact.go Normal file
View file

@ -0,0 +1,62 @@
package handlers
import (
"fmt"
"github.com/go-playground/validator/v10"
"github.com/labstack/echo/v4"
"github.com/mikestefanello/pagoda/pkg/form"
"github.com/mikestefanello/pagoda/pkg/routenames"
"github.com/mikestefanello/pagoda/pkg/services"
"github.com/mikestefanello/pagoda/pkg/ui/forms"
"github.com/mikestefanello/pagoda/pkg/ui/pages"
)
type Contact struct {
mail *services.MailClient
}
func init() {
Register(new(Contact))
}
func (h *Contact) Init(c *services.Container) error {
h.mail = c.Mail
return nil
}
func (h *Contact) Routes(g *echo.Group) {
g.GET("/contact", h.Page).Name = routenames.Contact
g.POST("/contact", h.Submit).Name = routenames.ContactSubmit
}
func (h *Contact) Page(ctx echo.Context) error {
return pages.ContactUs(ctx, form.Get[forms.Contact](ctx))
}
func (h *Contact) Submit(ctx echo.Context) error {
var input forms.Contact
err := form.Submit(ctx, &input)
switch err.(type) {
case nil:
case validator.ValidationErrors:
return h.Page(ctx)
default:
return err
}
err = h.mail.
Compose().
To(input.Email).
Subject("Contact form submitted").
Body(fmt.Sprintf("The message is: %s", input.Message)).
Send(ctx)
if err != nil {
return fail(err, "unable to send email")
}
return h.Page(ctx)
}

43
pkg/handlers/error.go Normal file
View file

@ -0,0 +1,43 @@
package handlers
import (
"net/http"
"github.com/labstack/echo/v4"
"github.com/mikestefanello/pagoda/pkg/context"
"github.com/mikestefanello/pagoda/pkg/log"
"github.com/mikestefanello/pagoda/pkg/ui/pages"
)
type Error struct{}
func (e *Error) Page(err error, ctx echo.Context) {
if ctx.Response().Committed || context.IsCanceledError(err) {
return
}
// Determine the error status code.
code := http.StatusInternalServerError
if he, ok := err.(*echo.HTTPError); ok {
code = he.Code
}
// Log the error.
logger := log.Ctx(ctx)
switch {
case code >= 500:
logger.Error(err.Error())
case code >= 400:
logger.Warn(err.Error())
}
// Set the status code.
ctx.Response().WriteHeader(code)
// Render the error page.
if err = pages.Error(ctx, code); err != nil {
log.Ctx(ctx).Error("failed to render error page",
"error", err,
)
}
}

80
pkg/handlers/files.go Normal file
View file

@ -0,0 +1,80 @@
package handlers
import (
"fmt"
"io"
"time"
"github.com/labstack/echo/v4"
"github.com/mikestefanello/pagoda/pkg/msg"
"github.com/mikestefanello/pagoda/pkg/routenames"
"github.com/mikestefanello/pagoda/pkg/services"
"github.com/mikestefanello/pagoda/pkg/ui/models"
"github.com/mikestefanello/pagoda/pkg/ui/pages"
"github.com/spf13/afero"
)
type Files struct {
files afero.Fs
}
func init() {
Register(new(Files))
}
func (h *Files) Init(c *services.Container) error {
h.files = c.Files
return nil
}
func (h *Files) Routes(g *echo.Group) {
g.GET("/files", h.Page).Name = routenames.Files
g.POST("/files", h.Submit).Name = routenames.FilesSubmit
}
func (h *Files) Page(ctx echo.Context) error {
// Compile a list of all uploaded files to be rendered.
info, err := afero.ReadDir(h.files, "")
if err != nil {
return err
}
files := make([]*models.File, 0)
for _, file := range info {
files = append(files, &models.File{
Name: file.Name(),
Size: file.Size(),
Modified: file.ModTime().Format(time.DateTime),
})
}
return pages.UploadFile(ctx, files)
}
func (h *Files) Submit(ctx echo.Context) error {
file, err := ctx.FormFile("file")
if err != nil {
msg.Error(ctx, "A file is required.")
return h.Page(ctx)
}
src, err := file.Open()
if err != nil {
return err
}
defer src.Close()
dst, err := h.files.Create(file.Filename)
if err != nil {
return err
}
defer dst.Close()
if _, err = io.Copy(dst, src); err != nil {
return err
}
msg.Success(ctx, fmt.Sprintf("%s was uploaded successfully.", file.Filename))
return h.Page(ctx)
}

36
pkg/handlers/handlers.go Normal file
View file

@ -0,0 +1,36 @@
package handlers
import (
"fmt"
"net/http"
"github.com/labstack/echo/v4"
"github.com/mikestefanello/pagoda/pkg/services"
)
var handlers []Handler
// Handler handles one or more HTTP routes
type Handler interface {
// Routes allows for self-registration of HTTP routes on the router
Routes(g *echo.Group)
// Init provides the service container to initialize
Init(*services.Container) error
}
// Register registers a handler
func Register(h Handler) {
handlers = append(handlers, h)
}
// GetHandlers returns all handlers
func GetHandlers() []Handler {
return handlers
}
// fail is a helper to fail a request by returning a 500 error and logging the error
func fail(err error, log string) error {
// The error handler will handle logging
return echo.NewHTTPError(http.StatusInternalServerError, fmt.Sprintf("%s: %v", log, err))
}

View file

@ -0,0 +1,29 @@
package handlers
import (
"errors"
"net/http"
"testing"
"github.com/labstack/echo/v4"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestGetSetHandlers(t *testing.T) {
handlers = []Handler{}
assert.Empty(t, GetHandlers())
h := new(Pages)
Register(h)
got := GetHandlers()
require.Len(t, got, 1)
assert.Equal(t, h, got[0])
}
func TestFail(t *testing.T) {
err := fail(errors.New("err message"), "log message")
require.IsType(t, new(echo.HTTPError), err)
he := err.(*echo.HTTPError)
assert.Equal(t, http.StatusInternalServerError, he.Code)
assert.Equal(t, "log message: err message", he.Message)
}

55
pkg/handlers/pages.go Normal file
View file

@ -0,0 +1,55 @@
package handlers
import (
"fmt"
"github.com/labstack/echo/v4"
"github.com/mikestefanello/pagoda/pkg/pager"
"github.com/mikestefanello/pagoda/pkg/routenames"
"github.com/mikestefanello/pagoda/pkg/services"
"github.com/mikestefanello/pagoda/pkg/ui/models"
"github.com/mikestefanello/pagoda/pkg/ui/pages"
)
type Pages struct{}
func init() {
Register(new(Pages))
}
func (h *Pages) Init(c *services.Container) error {
return nil
}
func (h *Pages) Routes(g *echo.Group) {
g.GET("/", h.Home).Name = routenames.Home
g.GET("/about", h.About).Name = routenames.About
}
func (h *Pages) Home(ctx echo.Context) error {
pgr := pager.NewPager(ctx, 4)
return pages.Home(ctx, &models.Posts{
Posts: h.fetchPosts(&pgr),
Pager: pgr,
})
}
// fetchPosts is a mock example of fetching posts to illustrate how paging works.
func (h *Pages) fetchPosts(pager *pager.Pager) []models.Post {
pager.SetItems(20)
posts := make([]models.Post, 20)
for k := range posts {
posts[k] = models.Post{
ID: k + 1,
Title: fmt.Sprintf("Post example #%d", k+1),
Body: fmt.Sprintf("Lorem ipsum example #%d ddolor sit amet, consectetur adipiscing elit. Nam elementum vulputate tristique.", k+1),
}
}
return posts[pager.GetOffset() : pager.GetOffset()+pager.ItemsPerPage]
}
func (h *Pages) About(ctx echo.Context) error {
return pages.About(ctx)
}

View file

@ -1,23 +1,24 @@
package routes package handlers
import ( import (
"net/http" "net/http"
"testing" "testing"
"github.com/mikestefanello/pagoda/pkg/routenames"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
) )
// Simple example of how to test routes and their markup using the test HTTP server spun up within // Simple example of how to test routes and their markup using the test HTTP server spun up within
// this test package // this test package
func TestAbout_Get(t *testing.T) { func TestPages__About(t *testing.T) {
doc := request(t). doc := request(t).
setRoute("about"). setRoute(routenames.About).
get(). get().
assertStatusCode(http.StatusOK). assertStatusCode(http.StatusOK).
toDoc() toDoc()
// Goquery is an excellent package to use for testing HTML markup // Goquery is an excellent package to use for testing HTML markup
h1 := doc.Find("h1.title") h1 := doc.Find("h1")
assert.Len(t, h1.Nodes, 1) assert.Len(t, h1.Nodes, 1)
assert.Equal(t, "About", h1.Text()) assert.Equal(t, "About", h1.Text())
} }

93
pkg/handlers/router.go Normal file
View file

@ -0,0 +1,93 @@
package handlers
import (
"net/http"
"strings"
"github.com/gorilla/sessions"
"github.com/labstack/echo/v4"
echomw "github.com/labstack/echo/v4/middleware"
"github.com/mikestefanello/pagoda/pkg/context"
"github.com/mikestefanello/pagoda/pkg/middleware"
"github.com/mikestefanello/pagoda/pkg/services"
files "github.com/mikestefanello/pagoda/public"
)
// BuildRouter builds the router.
func BuildRouter(c *services.Container) error {
// Force HTTPS, if enabled.
if c.Config.HTTP.TLS.Enabled {
c.Web.Use(echomw.HTTPSRedirect())
}
// Serve public files with cache control.
c.Web.Group("", middleware.CacheControl(c.Config.Cache.Expiration.PublicFile)).
Static("files", "public/files")
// Serve static files.
// ui.StaticFile() should be used in ui components to append a cache key to the URL to break cache
// after each server reboot.
c.Web.Group(
"",
echomw.GzipWithConfig(echomw.GzipConfig{
Skipper: func(c echo.Context) bool {
for _, ext := range []string{
".js",
".css",
} {
if strings.HasSuffix(c.Request().URL.Path, ext) {
return false
}
}
return true
},
}),
middleware.CacheControl(c.Config.Cache.Expiration.PublicFile),
).StaticFS("static", echo.MustSubFS(files.Static, "static"))
// Non-static file route group.
g := c.Web.Group("")
// Create a cookie store for session data.
cookieStore := sessions.NewCookieStore([]byte(c.Config.App.EncryptionKey))
cookieStore.Options.HttpOnly = true
cookieStore.Options.SameSite = http.SameSiteStrictMode
g.Use(
echomw.RemoveTrailingSlashWithConfig(echomw.TrailingSlashConfig{
RedirectCode: http.StatusMovedPermanently,
}),
echomw.Recover(),
echomw.Secure(),
echomw.RequestID(),
middleware.SetLogger(),
middleware.LogRequest(),
echomw.Gzip(),
echomw.TimeoutWithConfig(echomw.TimeoutConfig{
Timeout: c.Config.App.Timeout,
}),
middleware.Config(c.Config),
middleware.Session(cookieStore),
middleware.LoadAuthenticatedUser(c.Auth),
echomw.CSRFWithConfig(echomw.CSRFConfig{
TokenLookup: "form:csrf",
CookieHTTPOnly: true,
CookieSameSite: http.SameSiteStrictMode,
ContextKey: context.CSRFKey,
}),
)
// Error handler.
c.Web.HTTPErrorHandler = new(Error).Page
// Initialize and register all handlers.
for _, h := range GetHandlers() {
if err := h.Init(c); err != nil {
return err
}
h.Routes(g)
}
return nil
}

View file

@ -1,14 +1,15 @@
package routes package handlers
import ( import (
"net/http" "net/http"
"net/http/cookiejar"
"net/http/httptest" "net/http/httptest"
"net/url" "net/url"
"os" "os"
"testing" "testing"
"goweb/config" "github.com/mikestefanello/pagoda/config"
"goweb/services" "github.com/mikestefanello/pagoda/pkg/services"
"github.com/PuerkitoBio/goquery" "github.com/PuerkitoBio/goquery"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
@ -27,19 +28,22 @@ func TestMain(m *testing.M) {
// Start a new container // Start a new container
c = services.NewContainer() c = services.NewContainer()
defer func() {
if err := c.Shutdown(); err != nil {
c.Web.Logger.Fatal(err)
}
}()
// Start a test HTTP server // Start a test HTTP server
BuildRouter(c) if err := BuildRouter(c); err != nil {
panic(err)
}
srv = httptest.NewServer(c.Web) srv = httptest.NewServer(c.Web)
// Run tests // Run tests
exitVal := m.Run() exitVal := m.Run()
// Shutdown the container and test server
if err := c.Shutdown(); err != nil {
panic(err)
}
srv.Close() srv.Close()
os.Exit(exitVal) os.Exit(exitVal)
} }
@ -51,8 +55,14 @@ type httpRequest struct {
} }
func request(t *testing.T) *httpRequest { func request(t *testing.T) *httpRequest {
jar, err := cookiejar.New(nil)
require.NoError(t, err)
r := httpRequest{ r := httpRequest{
t: t, t: t,
body: url.Values{},
client: http.Client{
Jar: jar,
},
} }
return &r return &r
} }
@ -62,7 +72,7 @@ func (h *httpRequest) setClient(client http.Client) *httpRequest {
return h return h
} }
func (h *httpRequest) setRoute(route string, params ...interface{}) *httpRequest { func (h *httpRequest) setRoute(route string, params ...any) *httpRequest {
h.route = srv.URL + c.Web.Reverse(route, params) h.route = srv.URL + c.Web.Reverse(route, params)
return h return h
} }
@ -83,6 +93,18 @@ func (h *httpRequest) get() *httpResponse {
} }
func (h *httpRequest) post() *httpResponse { func (h *httpRequest) post() *httpResponse {
// Make a get request to get the CSRF token
doc := h.get().
assertStatusCode(http.StatusOK).
toDoc()
// Extract the CSRF and include it in the POST request body
csrf := doc.Find(`input[name="csrf"]`).First()
token, exists := csrf.Attr("value")
assert.True(h.t, exists)
h.body["csrf"] = []string{token}
// Make the POST requests
resp, err := h.client.PostForm(h.route, h.body) resp, err := h.client.PostForm(h.route, h.body)
require.NoError(h.t, err) require.NoError(h.t, err)
r := httpResponse{ r := httpResponse{
@ -102,7 +124,7 @@ func (h *httpResponse) assertStatusCode(code int) *httpResponse {
return h return h
} }
func (h *httpResponse) assertRedirect(t *testing.T, route string, params ...interface{}) *httpResponse { func (h *httpResponse) assertRedirect(t *testing.T, route string, params ...any) *httpResponse {
assert.Equal(t, c.Web.Reverse(route, params), h.Header.Get("Location")) assert.Equal(t, c.Web.Reverse(route, params), h.Header.Get("Location"))
return h return h
} }

44
pkg/handlers/search.go Normal file
View file

@ -0,0 +1,44 @@
package handlers
import (
"fmt"
"math/rand"
"github.com/labstack/echo/v4"
"github.com/mikestefanello/pagoda/pkg/routenames"
"github.com/mikestefanello/pagoda/pkg/services"
"github.com/mikestefanello/pagoda/pkg/ui/models"
"github.com/mikestefanello/pagoda/pkg/ui/pages"
)
type Search struct{}
func init() {
Register(new(Search))
}
func (h *Search) Init(c *services.Container) error {
return nil
}
func (h *Search) Routes(g *echo.Group) {
g.GET("/search", h.Page).Name = routenames.Search
}
func (h *Search) Page(ctx echo.Context) error {
// Fake search results.
results := make([]*models.SearchResult, 0, 5)
if search := ctx.QueryParam("query"); search != "" {
for i := 0; i < 5; i++ {
title := "Lorem ipsum example ddolor sit amet"
index := rand.Intn(len(title))
title = title[:index] + search + title[index:]
results = append(results, &models.SearchResult{
Title: title,
URL: fmt.Sprintf("https://www.%s.com", search),
})
}
}
return pages.SearchResults(ctx, results)
}

71
pkg/handlers/task.go Normal file
View file

@ -0,0 +1,71 @@
package handlers
import (
"fmt"
"time"
"github.com/mikestefanello/backlite"
"github.com/mikestefanello/pagoda/pkg/msg"
"github.com/mikestefanello/pagoda/pkg/routenames"
"github.com/mikestefanello/pagoda/pkg/ui/forms"
"github.com/mikestefanello/pagoda/pkg/ui/pages"
"github.com/go-playground/validator/v10"
"github.com/labstack/echo/v4"
"github.com/mikestefanello/pagoda/pkg/form"
"github.com/mikestefanello/pagoda/pkg/services"
"github.com/mikestefanello/pagoda/pkg/tasks"
)
type Task struct {
tasks *backlite.Client
}
func init() {
Register(new(Task))
}
func (h *Task) Init(c *services.Container) error {
h.tasks = c.Tasks
return nil
}
func (h *Task) Routes(g *echo.Group) {
g.GET("/task", h.Page).Name = routenames.Task
g.POST("/task", h.Submit).Name = routenames.TaskSubmit
}
func (h *Task) Page(ctx echo.Context) error {
return pages.AddTask(ctx, form.Get[forms.Task](ctx))
}
func (h *Task) Submit(ctx echo.Context) error {
var input forms.Task
err := form.Submit(ctx, &input)
switch err.(type) {
case nil:
case validator.ValidationErrors:
return h.Page(ctx)
default:
return err
}
// Insert the task
_, err = h.tasks.
Add(tasks.ExampleTask{
Message: input.Message,
}).
Wait(time.Duration(input.Delay) * time.Second).
Save()
if err != nil {
return fail(err, "unable to create a task")
}
msg.Success(ctx, fmt.Sprintf("The task has been created. Check the logs in %d seconds.", input.Delay))
form.Clear(ctx)
return h.Page(ctx)
}

97
pkg/htmx/htmx.go Normal file
View file

@ -0,0 +1,97 @@
package htmx
import (
"net/http"
"github.com/labstack/echo/v4"
"github.com/mikestefanello/pagoda/pkg/context"
)
// Request headers: https://htmx.org/docs/#request-headers
const (
HeaderBoosted = "HX-Boosted"
HeaderHistoryRestoreRequest = "HX-History-Restore-Request"
HeaderPrompt = "HX-Prompt"
HeaderRequest = "HX-Request"
HeaderTarget = "HX-Target"
HeaderTrigger = "HX-Trigger"
HeaderTriggerName = "HX-Trigger-Name"
)
// Response headers: https://htmx.org/docs/#response-headers
const (
HeaderPushURL = "HX-Push-Url"
HeaderRedirect = "HX-Redirect"
HeaderReplaceURL = "HX-Replace-Url"
HeaderRefresh = "HX-Refresh"
HeaderTriggerAfterSettle = "HX-Trigger-After-Settle"
HeaderTriggerAfterSwap = "HX-Trigger-After-Swap"
)
type (
// Request contains data that HTMX provides during requests.
Request struct {
Enabled bool
Boosted bool
HistoryRestore bool
Trigger string
TriggerName string
Target string
Prompt string
}
// Response contain data that the server can communicate back to HTMX.
Response struct {
PushURL string
Redirect string
Refresh bool
ReplaceURL string
Trigger string
TriggerAfterSwap string
TriggerAfterSettle string
NoContent bool
}
)
// GetRequest extracts HTMX data from the request,
func GetRequest(ctx echo.Context) *Request {
return context.Cache(ctx, context.HTMXRequestKey, func(ctx echo.Context) *Request {
return &Request{
Enabled: ctx.Request().Header.Get(HeaderRequest) == "true",
Boosted: ctx.Request().Header.Get(HeaderBoosted) == "true",
Trigger: ctx.Request().Header.Get(HeaderTrigger),
TriggerName: ctx.Request().Header.Get(HeaderTriggerName),
Target: ctx.Request().Header.Get(HeaderTarget),
Prompt: ctx.Request().Header.Get(HeaderPrompt),
HistoryRestore: ctx.Request().Header.Get(HeaderHistoryRestoreRequest) == "true",
}
})
}
// Apply applies data from a Response to a server response.
func (r Response) Apply(ctx echo.Context) {
if r.PushURL != "" {
ctx.Response().Header().Set(HeaderPushURL, r.PushURL)
}
if r.Redirect != "" {
ctx.Response().Header().Set(HeaderRedirect, r.Redirect)
}
if r.Refresh {
ctx.Response().Header().Set(HeaderRefresh, "true")
}
if r.Trigger != "" {
ctx.Response().Header().Set(HeaderTrigger, r.Trigger)
}
if r.TriggerAfterSwap != "" {
ctx.Response().Header().Set(HeaderTriggerAfterSwap, r.TriggerAfterSwap)
}
if r.TriggerAfterSettle != "" {
ctx.Response().Header().Set(HeaderTriggerAfterSettle, r.TriggerAfterSettle)
}
if r.ReplaceURL != "" {
ctx.Response().Header().Set(HeaderReplaceURL, r.ReplaceURL)
}
if r.NoContent {
ctx.Response().Status = http.StatusNoContent
}
}

View file

@ -4,7 +4,8 @@ import (
"net/http" "net/http"
"testing" "testing"
"goweb/tests" "github.com/mikestefanello/pagoda/pkg/context"
"github.com/mikestefanello/pagoda/pkg/tests"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
@ -19,21 +20,29 @@ func TestSetRequest(t *testing.T) {
ctx.Request().Header.Set(HeaderTriggerName, "b") ctx.Request().Header.Set(HeaderTriggerName, "b")
ctx.Request().Header.Set(HeaderTarget, "c") ctx.Request().Header.Set(HeaderTarget, "c")
ctx.Request().Header.Set(HeaderPrompt, "d") ctx.Request().Header.Set(HeaderPrompt, "d")
ctx.Request().Header.Set(HeaderHistoryRestoreRequest, "true")
r := GetRequest(ctx) r := GetRequest(ctx)
assert.Equal(t, true, r.Enabled) assert.Equal(t, true, r.Enabled)
assert.Equal(t, true, r.Boosted) assert.Equal(t, true, r.Boosted)
assert.Equal(t, true, r.HistoryRestore)
assert.Equal(t, "a", r.Trigger) assert.Equal(t, "a", r.Trigger)
assert.Equal(t, "b", r.TriggerName) assert.Equal(t, "b", r.TriggerName)
assert.Equal(t, "c", r.Target) assert.Equal(t, "c", r.Target)
assert.Equal(t, "d", r.Prompt) assert.Equal(t, "d", r.Prompt)
cached := context.Cache(ctx, context.HTMXRequestKey, func(ctx echo.Context) *Request {
return nil
})
assert.Equal(t, r, cached)
} }
func TestResponse_Apply(t *testing.T) { func TestResponse_Apply(t *testing.T) {
ctx, _ := tests.NewContext(echo.New(), "/") ctx, _ := tests.NewContext(echo.New(), "/")
r := Response{ r := Response{
Push: "a", PushURL: "a",
Redirect: "b", Redirect: "b",
ReplaceURL: "f",
Refresh: true, Refresh: true,
Trigger: "c", Trigger: "c",
TriggerAfterSwap: "d", TriggerAfterSwap: "d",
@ -42,11 +51,12 @@ func TestResponse_Apply(t *testing.T) {
} }
r.Apply(ctx) r.Apply(ctx)
assert.Equal(t, "a", ctx.Response().Header().Get(HeaderPush)) assert.Equal(t, "a", ctx.Response().Header().Get(HeaderPushURL))
assert.Equal(t, "b", ctx.Response().Header().Get(HeaderRedirect)) assert.Equal(t, "b", ctx.Response().Header().Get(HeaderRedirect))
assert.Equal(t, "true", ctx.Response().Header().Get(HeaderRefresh)) assert.Equal(t, "true", ctx.Response().Header().Get(HeaderRefresh))
assert.Equal(t, "c", ctx.Response().Header().Get(HeaderTrigger)) assert.Equal(t, "c", ctx.Response().Header().Get(HeaderTrigger))
assert.Equal(t, "d", ctx.Response().Header().Get(HeaderTriggerAfterSwap)) assert.Equal(t, "d", ctx.Response().Header().Get(HeaderTriggerAfterSwap))
assert.Equal(t, "e", ctx.Response().Header().Get(HeaderTriggerAfterSettle)) assert.Equal(t, "e", ctx.Response().Header().Get(HeaderTriggerAfterSettle))
assert.Equal(t, "f", ctx.Response().Header().Get(HeaderReplaceURL))
assert.Equal(t, http.StatusNoContent, ctx.Response().Status) assert.Equal(t, http.StatusNoContent, ctx.Response().Status)
} }

27
pkg/log/log.go Normal file
View file

@ -0,0 +1,27 @@
package log
import (
"log/slog"
"github.com/labstack/echo/v4"
"github.com/mikestefanello/pagoda/pkg/context"
)
// Set sets a logger in the context.
func Set(ctx echo.Context, logger *slog.Logger) {
ctx.Set(context.LoggerKey, logger)
}
// Ctx returns the logger stored in context, or provides the default logger if one is not present.
func Ctx(ctx echo.Context) *slog.Logger {
if l, ok := ctx.Get(context.LoggerKey).(*slog.Logger); ok {
return l
}
return Default()
}
// Default returns the default logger.
func Default() *slog.Logger {
return slog.Default()
}

21
pkg/log/log_test.go Normal file
View file

@ -0,0 +1,21 @@
package log
import (
"testing"
"github.com/labstack/echo/v4"
"github.com/mikestefanello/pagoda/pkg/tests"
"github.com/stretchr/testify/assert"
)
func TestCtxSet(t *testing.T) {
ctx, _ := tests.NewContext(echo.New(), "/")
logger := Ctx(ctx)
assert.NotNil(t, logger)
logger = logger.With("a", "b")
Set(ctx, logger)
got := Ctx(ctx)
assert.Equal(t, got, logger)
}

120
pkg/middleware/auth.go Normal file
View file

@ -0,0 +1,120 @@
package middleware
import (
"fmt"
"net/http"
"strconv"
"github.com/mikestefanello/pagoda/ent"
"github.com/mikestefanello/pagoda/pkg/context"
"github.com/mikestefanello/pagoda/pkg/log"
"github.com/mikestefanello/pagoda/pkg/msg"
"github.com/mikestefanello/pagoda/pkg/routenames"
"github.com/mikestefanello/pagoda/pkg/services"
"github.com/labstack/echo/v4"
)
// LoadAuthenticatedUser loads the authenticated user, if one, and stores in context.
func LoadAuthenticatedUser(authClient *services.AuthClient) echo.MiddlewareFunc {
return func(next echo.HandlerFunc) echo.HandlerFunc {
return func(c echo.Context) error {
u, err := authClient.GetAuthenticatedUser(c)
switch err.(type) {
case *ent.NotFoundError:
log.Ctx(c).Warn("auth user not found")
case services.NotAuthenticatedError:
case nil:
c.Set(context.AuthenticatedUserKey, u)
default:
return echo.NewHTTPError(
http.StatusInternalServerError,
fmt.Sprintf("error querying for authenticated user: %v", err),
)
}
return next(c)
}
}
}
// LoadValidPasswordToken loads a valid password token entity that matches the user and token
// provided in path parameters
// If the token is invalid, the user will be redirected to the forgot password route
// This requires that the user owning the token is loaded in to context.
func LoadValidPasswordToken(authClient *services.AuthClient) echo.MiddlewareFunc {
return func(next echo.HandlerFunc) echo.HandlerFunc {
return func(c echo.Context) error {
// Extract the user parameter
if c.Get(context.UserKey) == nil {
return echo.NewHTTPError(http.StatusInternalServerError)
}
usr := c.Get(context.UserKey).(*ent.User)
// Extract the token ID.
tokenID, err := strconv.Atoi(c.Param("password_token"))
if err != nil {
return echo.NewHTTPError(http.StatusNotFound)
}
// Attempt to load a valid password token.
token, err := authClient.GetValidPasswordToken(
c,
usr.ID,
tokenID,
c.Param("token"),
)
switch err.(type) {
case nil:
c.Set(context.PasswordTokenKey, token)
return next(c)
case services.InvalidPasswordTokenError:
msg.Warning(c, "The link is either invalid or has expired. Please request a new one.")
return c.Redirect(http.StatusFound, c.Echo().Reverse(routenames.ForgotPassword))
default:
return echo.NewHTTPError(
http.StatusInternalServerError,
fmt.Sprintf("error loading password token: %v", err),
)
}
}
}
}
// RequireAuthentication requires that the user be authenticated in order to proceed.
func RequireAuthentication(next echo.HandlerFunc) echo.HandlerFunc {
return func(c echo.Context) error {
if u := c.Get(context.AuthenticatedUserKey); u == nil {
return echo.NewHTTPError(http.StatusUnauthorized)
}
return next(c)
}
}
// RequireNoAuthentication requires that the user not be authenticated in order to proceed.
func RequireNoAuthentication(next echo.HandlerFunc) echo.HandlerFunc {
return func(c echo.Context) error {
if u := c.Get(context.AuthenticatedUserKey); u != nil {
return echo.NewHTTPError(http.StatusForbidden)
}
return next(c)
}
}
// RequireAdmin requires that the authenticated user be an admin in order to proceed.
func RequireAdmin(next echo.HandlerFunc) echo.HandlerFunc {
return func(c echo.Context) error {
if u := c.Get(context.AuthenticatedUserKey); u != nil {
if user, ok := u.(*ent.User); ok {
if user.Admin {
return next(c)
}
}
}
return echo.NewHTTPError(http.StatusUnauthorized)
}
}

View file

@ -1,13 +1,14 @@
package middleware package middleware
import ( import (
goctx "context"
"fmt" "fmt"
"net/http" "net/http"
"testing" "testing"
"goweb/context" "github.com/mikestefanello/pagoda/ent"
"goweb/ent" "github.com/mikestefanello/pagoda/pkg/context"
"goweb/tests" "github.com/mikestefanello/pagoda/pkg/tests"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
@ -40,7 +41,7 @@ func TestRequireAuthentication(t *testing.T) {
tests.InitSession(ctx) tests.InitSession(ctx)
// Not logged in // Not logged in
err := tests.ExecuteMiddleware(ctx, RequireAuthentication()) err := tests.ExecuteMiddleware(ctx, RequireAuthentication)
tests.AssertHTTPErrorCode(t, err, http.StatusUnauthorized) tests.AssertHTTPErrorCode(t, err, http.StatusUnauthorized)
// Login // Login
@ -49,7 +50,7 @@ func TestRequireAuthentication(t *testing.T) {
_ = tests.ExecuteMiddleware(ctx, LoadAuthenticatedUser(c.Auth)) _ = tests.ExecuteMiddleware(ctx, LoadAuthenticatedUser(c.Auth))
// Logged in // Logged in
err = tests.ExecuteMiddleware(ctx, RequireAuthentication()) err = tests.ExecuteMiddleware(ctx, RequireAuthentication)
assert.Nil(t, err) assert.Nil(t, err)
} }
@ -58,7 +59,7 @@ func TestRequireNoAuthentication(t *testing.T) {
tests.InitSession(ctx) tests.InitSession(ctx)
// Not logged in // Not logged in
err := tests.ExecuteMiddleware(ctx, RequireNoAuthentication()) err := tests.ExecuteMiddleware(ctx, RequireNoAuthentication)
assert.Nil(t, err) assert.Nil(t, err)
// Login // Login
@ -67,7 +68,7 @@ func TestRequireNoAuthentication(t *testing.T) {
_ = tests.ExecuteMiddleware(ctx, LoadAuthenticatedUser(c.Auth)) _ = tests.ExecuteMiddleware(ctx, LoadAuthenticatedUser(c.Auth))
// Logged in // Logged in
err = tests.ExecuteMiddleware(ctx, RequireNoAuthentication()) err = tests.ExecuteMiddleware(ctx, RequireNoAuthentication)
tests.AssertHTTPErrorCode(t, err, http.StatusForbidden) tests.AssertHTTPErrorCode(t, err, http.StatusForbidden)
} }
@ -79,17 +80,17 @@ func TestLoadValidPasswordToken(t *testing.T) {
err := tests.ExecuteMiddleware(ctx, LoadValidPasswordToken(c.Auth)) err := tests.ExecuteMiddleware(ctx, LoadValidPasswordToken(c.Auth))
tests.AssertHTTPErrorCode(t, err, http.StatusInternalServerError) tests.AssertHTTPErrorCode(t, err, http.StatusInternalServerError)
// Add user context but no password token and expect a redirect // Add user and password token context but no token and expect a redirect
ctx.SetParamNames("user") ctx.SetParamNames("user", "password_token")
ctx.SetParamValues(fmt.Sprintf("%d", usr.ID)) ctx.SetParamValues(fmt.Sprintf("%d", usr.ID), "1")
_ = tests.ExecuteMiddleware(ctx, LoadUser(c.ORM)) _ = tests.ExecuteMiddleware(ctx, LoadUser(c.ORM))
err = tests.ExecuteMiddleware(ctx, LoadValidPasswordToken(c.Auth)) err = tests.ExecuteMiddleware(ctx, LoadValidPasswordToken(c.Auth))
assert.NoError(t, err) assert.NoError(t, err)
assert.Equal(t, http.StatusFound, ctx.Response().Status) assert.Equal(t, http.StatusFound, ctx.Response().Status)
// Add user context and invalid password token and expect a redirect // Add user context and invalid password token and expect a redirect
ctx.SetParamNames("user", "password_token") ctx.SetParamNames("user", "password_token", "token")
ctx.SetParamValues(fmt.Sprintf("%d", usr.ID), "faketoken") ctx.SetParamValues(fmt.Sprintf("%d", usr.ID), "1", "faketoken")
_ = tests.ExecuteMiddleware(ctx, LoadUser(c.ORM)) _ = tests.ExecuteMiddleware(ctx, LoadUser(c.ORM))
err = tests.ExecuteMiddleware(ctx, LoadValidPasswordToken(c.Auth)) err = tests.ExecuteMiddleware(ctx, LoadValidPasswordToken(c.Auth))
assert.NoError(t, err) assert.NoError(t, err)
@ -100,8 +101,8 @@ func TestLoadValidPasswordToken(t *testing.T) {
require.NoError(t, err) require.NoError(t, err)
// Add user and valid password token // Add user and valid password token
ctx.SetParamNames("user", "password_token") ctx.SetParamNames("user", "password_token", "token")
ctx.SetParamValues(fmt.Sprintf("%d", usr.ID), token) ctx.SetParamValues(fmt.Sprintf("%d", usr.ID), fmt.Sprintf("%d", pt.ID), token)
_ = tests.ExecuteMiddleware(ctx, LoadUser(c.ORM)) _ = tests.ExecuteMiddleware(ctx, LoadUser(c.ORM))
err = tests.ExecuteMiddleware(ctx, LoadValidPasswordToken(c.Auth)) err = tests.ExecuteMiddleware(ctx, LoadValidPasswordToken(c.Auth))
assert.Nil(t, err) assert.Nil(t, err)
@ -109,3 +110,36 @@ func TestLoadValidPasswordToken(t *testing.T) {
require.True(t, ok) require.True(t, ok)
assert.Equal(t, pt.ID, ctxPt.ID) assert.Equal(t, pt.ID, ctxPt.ID)
} }
func TestRequireAdmin(t *testing.T) {
ctx, _ := tests.NewContext(c.Web, "/")
tests.InitSession(ctx)
// Not logged in
err := tests.ExecuteMiddleware(ctx, RequireAdmin)
tests.AssertHTTPErrorCode(t, err, http.StatusUnauthorized)
// Login as a non-admin
err = c.Auth.Login(ctx, usr.ID)
require.NoError(t, err)
_ = tests.ExecuteMiddleware(ctx, LoadAuthenticatedUser(c.Auth))
// Logged in as a non-admin
err = tests.ExecuteMiddleware(ctx, RequireAdmin)
tests.AssertHTTPErrorCode(t, err, http.StatusUnauthorized)
// Create an admin and login
adm, err := tests.CreateUser(c.ORM)
require.NoError(t, err)
err = c.ORM.User.Update().
SetAdmin(true).
Exec(goctx.Background())
require.NoError(t, err)
err = c.Auth.Login(ctx, adm.ID)
require.NoError(t, err)
_ = tests.ExecuteMiddleware(ctx, LoadAuthenticatedUser(c.Auth))
// Logged in as an admin
err = tests.ExecuteMiddleware(ctx, RequireAdmin)
assert.Nil(t, err)
}

22
pkg/middleware/cache.go Normal file
View file

@ -0,0 +1,22 @@
package middleware
import (
"fmt"
"time"
"github.com/labstack/echo/v4"
)
// CacheControl sets a Cache-Control header with a given max age.
func CacheControl(maxAge time.Duration) echo.MiddlewareFunc {
return func(next echo.HandlerFunc) echo.HandlerFunc {
return func(ctx echo.Context) error {
v := "no-cache, no-store"
if maxAge > 0 {
v = fmt.Sprintf("public, max-age=%.0f", maxAge.Seconds())
}
ctx.Response().Header().Set("Cache-Control", v)
return next(ctx)
}
}
}

View file

@ -0,0 +1,18 @@
package middleware
import (
"testing"
"time"
"github.com/mikestefanello/pagoda/pkg/tests"
"github.com/stretchr/testify/assert"
)
func TestCacheControl(t *testing.T) {
ctx, _ := tests.NewContext(c.Web, "/")
_ = tests.ExecuteMiddleware(ctx, CacheControl(time.Second*5))
assert.Equal(t, "public, max-age=5", ctx.Response().Header().Get("Cache-Control"))
_ = tests.ExecuteMiddleware(ctx, CacheControl(0))
assert.Equal(t, "no-cache, no-store", ctx.Response().Header().Get("Cache-Control"))
}

17
pkg/middleware/config.go Normal file
View file

@ -0,0 +1,17 @@
package middleware
import (
"github.com/labstack/echo/v4"
"github.com/mikestefanello/pagoda/config"
"github.com/mikestefanello/pagoda/pkg/context"
)
// Config stores the configuration in the request so it can be accessed by the ui.
func Config(cfg *config.Config) echo.MiddlewareFunc {
return func(next echo.HandlerFunc) echo.HandlerFunc {
return func(ctx echo.Context) error {
ctx.Set(context.ConfigKey, cfg)
return next(ctx)
}
}
}

View file

@ -0,0 +1,22 @@
package middleware
import (
"testing"
"github.com/mikestefanello/pagoda/config"
"github.com/mikestefanello/pagoda/pkg/context"
"github.com/mikestefanello/pagoda/pkg/tests"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestConfig(t *testing.T) {
ctx, _ := tests.NewContext(c.Web, "/")
cfg := &config.Config{}
err := tests.ExecuteMiddleware(ctx, Config(cfg))
require.NoError(t, err)
got, ok := ctx.Get(context.ConfigKey).(*config.Config)
require.True(t, ok)
assert.Same(t, got, cfg)
}

View file

@ -1,17 +1,18 @@
package middleware package middleware
import ( import (
"fmt"
"net/http" "net/http"
"strconv" "strconv"
"goweb/context" "github.com/mikestefanello/pagoda/ent"
"goweb/ent" "github.com/mikestefanello/pagoda/ent/user"
"goweb/ent/user" "github.com/mikestefanello/pagoda/pkg/context"
"github.com/labstack/echo/v4" "github.com/labstack/echo/v4"
) )
// LoadUser loads the user based on the ID provided as a path parameter // LoadUser loads the user based on the ID provided as a path parameter.
func LoadUser(orm *ent.Client) echo.MiddlewareFunc { func LoadUser(orm *ent.Client) echo.MiddlewareFunc {
return func(next echo.HandlerFunc) echo.HandlerFunc { return func(next echo.HandlerFunc) echo.HandlerFunc {
return func(c echo.Context) error { return func(c echo.Context) error {
@ -32,8 +33,10 @@ func LoadUser(orm *ent.Client) echo.MiddlewareFunc {
case *ent.NotFoundError: case *ent.NotFoundError:
return echo.NewHTTPError(http.StatusNotFound) return echo.NewHTTPError(http.StatusNotFound)
default: default:
c.Logger().Error(err) return echo.NewHTTPError(
return echo.NewHTTPError(http.StatusInternalServerError) http.StatusInternalServerError,
fmt.Sprintf("error querying user: %v", err),
)
} }
} }
} }

View file

@ -4,9 +4,9 @@ import (
"fmt" "fmt"
"testing" "testing"
"goweb/context" "github.com/mikestefanello/pagoda/ent"
"goweb/ent" "github.com/mikestefanello/pagoda/pkg/context"
"goweb/tests" "github.com/mikestefanello/pagoda/pkg/tests"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"

73
pkg/middleware/log.go Normal file
View file

@ -0,0 +1,73 @@
package middleware
import (
"fmt"
"strconv"
"time"
"github.com/labstack/echo/v4"
"github.com/mikestefanello/pagoda/pkg/log"
)
// SetLogger initializes a logger for the current request and stores it in the context.
// It's recommended to have this executed after Echo's RequestID() middleware because it will add
// the request ID to the logger so that all log messages produced from this request have the
// request ID in it. You can modify this code to include any other fields that you want to always
// appear.
func SetLogger() echo.MiddlewareFunc {
return func(next echo.HandlerFunc) echo.HandlerFunc {
return func(ctx echo.Context) error {
// Include the request ID in the logger
rID := ctx.Response().Header().Get(echo.HeaderXRequestID)
logger := log.Ctx(ctx).With("request_id", rID)
// TODO include other fields you may want in all logs for this request
log.Set(ctx, logger)
return next(ctx)
}
}
}
// LogRequest logs the current request
// Echo provides middleware similar to this, but we want to use our own logger
func LogRequest() echo.MiddlewareFunc {
return func(next echo.HandlerFunc) echo.HandlerFunc {
return func(ctx echo.Context) (err error) {
req := ctx.Request()
res := ctx.Response()
// Track how long the request takes to complete
start := time.Now()
if err = next(ctx); err != nil {
ctx.Error(err)
}
stop := time.Now()
sub := log.Ctx(ctx).With(
"ip", ctx.RealIP(),
"host", req.Host,
"referer", req.Referer(),
"status", res.Status,
"bytes_in", func() string {
cl := req.Header.Get(echo.HeaderContentLength)
if cl == "" {
cl = "0"
}
return cl
}(),
"bytes_out", strconv.FormatInt(res.Size, 10),
"latency", stop.Sub(start).String(),
)
msg := fmt.Sprintf("%s %s", req.Method, req.URL.RequestURI())
if res.Status >= 500 {
sub.Error(msg)
} else {
sub.Info(msg)
}
return nil
}
}
}

Some files were not shown because too many files have changed in this diff Show more