Compare commits

...

69 Commits

Author SHA1 Message Date
martabal
e36b7240c7 fix: merge main 2024-05-28 00:42:52 +02:00
martabal
fec3a04123 merge main 2024-05-28 00:04:45 +02:00
Mert
4c7347d653 fix: re-add extends section for server in Compose files (#9806)
re-add extends section
2024-05-27 21:04:07 +00:00
Mert
dca420ef70 chore: refactor transcode config routing (#9800)
* chore: refactor transcode config

* rename parameter

* handle no /dev/dri

* prefer undefined
2024-05-27 15:20:07 -04:00
Mert
21bd20fd75 fix(server): nvenc not working when there are no filters (#9802)
don't add format=nv12
2024-05-27 15:18:01 -04:00
Mert
351dd647a9 feat(server): better video thumbnails (#9784) 2024-05-27 12:08:38 -04:00
Michel Heusschen
298370b7be fix(web): validation of number input fields (#9789) 2024-05-27 15:19:08 +07:00
aviv926
e3d39837d0 docs: Add Google OAuth example (#9778)
* Add Google OAuth example

* npm run format:fix

* fix

* PR feedback

* Fix
2024-05-27 03:39:59 -04:00
Michel Heusschen
38f4a02a14 fix(web): require button type (#9786) 2024-05-27 14:06:15 +07:00
Michel Heusschen
fc5615eff6 fix(web): memories year missing (#9787) 2024-05-27 14:01:33 +07:00
Alex
6879bcb7a4 chore(server): duplication default settings (#9781) 2024-05-26 20:51:41 -04:00
Conner Hnatiuk
11152f9b3d fix(mobile): appBar on album viewer screen animates out and doesnt alter asset grid position (#9741)
* Animated out top bar added, currenlty need to move up current app bar as it is too far down

* album viewer appBar animates out and does not cause screen shift

* Formatting

---------

Co-authored-by: Alex <alex.tran1502@gmail.com>
2024-05-27 00:13:32 +00:00
Jason Rasmussen
75830a4878 refactor(server): user endpoints (#9730)
* refactor(server): user endpoints

* fix repos

* fix unit tests

---------

Co-authored-by: Daniel Dietzler <mail@ddietzler.dev>
Co-authored-by: Alex <alex.tran1502@gmail.com>
2024-05-27 05:15:52 +07:00
Mert
e7c8501930 fix(server): search duplicates of the same asset type (#9747)
* search by type

* make sql

---------

Co-authored-by: Alex <alex.tran1502@gmail.com>
2024-05-26 22:04:23 +00:00
Alexandre Bouijoux
50f9b2d44e docs: update README fr (#9764)
Update README_fr_FR.md
2024-05-27 04:45:05 +07:00
Ben
9628ea2d24 fix(web): keyboard event propagation in modals (#9713)
* fix: key events propagating from modal, visible close button focus

* feat: set initial focus on the text field for album creation

* chore: step back duplicated changes
2024-05-27 04:43:30 +07:00
martabal
d919d2c975 chore: regenarate api 2024-05-26 22:15:28 +02:00
martabal
7ac9c1bb78 merge main 2024-05-26 22:14:36 +02:00
safehome-jdev
4d4bb8b6a7 fix(server): Properly build ML predict URL (#9751)
New URL via URL constructor and not string concatenation
2024-05-26 08:21:10 -04:00
Michel Heusschen
99f0aa868a fix(web): detail panel asset description (#9765) 2024-05-26 08:10:01 -04:00
Michel Heusschen
459fee9ee4 fix(web): add location modal invisible on safari (#9756) 2024-05-25 15:36:36 -04:00
Matthew Momjian
871f3ea468 fix(docs): docker version -> name in ML (#9755)
fix docker
2024-05-25 15:14:22 +00:00
Michel Heusschen
98c4c683ae fix(web): profile picture url (#9754) 2024-05-25 11:13:03 -04:00
Michel Heusschen
8a7b0f66a4 fix(server): partner can view archived assets (#9750)
* fix(server): partner can view archived assets

* update sql queries
2024-05-25 06:53:57 -04:00
Jason Rasmussen
9e71256191 chore(server): remove unused code (#9746) 2024-05-25 12:15:07 +02:00
Min Idzelis
d5cf8e4bfe refactor(server): move checkExistingAssets(), checkBulkUpdate() remove getAllAssets() (#9715)
* Refactor controller methods, non-breaking change

* Remove getAllAssets

* used imports

* sync:sql

* missing mock

* Removing remaining references

* chore: remove unused code

---------

Co-authored-by: Jason Rasmussen <jrasm91@gmail.com>
2024-05-24 21:02:22 -04:00
Jason Rasmussen
95012dc19b fix: config error logging (#9738) 2024-05-24 16:44:50 -04:00
Lukas
f197f5d530 fix(server): use correct file extension for motion photo videos (#8659)
* fix(server): use mp4 file extension for motion photo videos in archive download

* always use mp4 for videos

* get file extension from originalPath

* remove console log

* store motion assets with mp4 extension

* add migration

* set originalFileName for live photo asset stubs

* leave down migration empty

* only set originalFileName for livePhotoStillAsset

* use separate stub

* shorter stub name
2024-05-24 16:38:18 -04:00
Jason Rasmussen
7168707395 refactor(server): remove unused code (#9737) 2024-05-24 16:37:29 -04:00
Snowknight26
847cb90038 fix(web): fix asset grid keyboard navigation (#9448)
* fix(web): fix asset grid keyboard navigation

* Ignore eslint rule

* Pass page up/down keys after focusing on grid

* Remove unneeded event listener, use existing class
2024-05-24 22:11:55 +02:00
bo0tzz
602f0a3499 fix(docs): Duplicate user key in example config.json (#9735)
related: #9734
2024-05-24 16:06:08 -04:00
Michel Heusschen
fdaa0e5413 fix(web): shared link isOwner check (#9729)
* fix(web): shared link isOwner check

* add e2e tests + update playwright

* fix formatting
2024-05-24 17:59:19 +00:00
Zack Pollard
39d2c4f37b chore: remove all deprecated endpoints/properties from server and mobile app (#9724)
* chore: remove deprecated title property from MemoryLaneResponseDto

* chore: remove deprecated webpPath and resizePath from MetadataSearchDto

* chore: remove deprecated sharedUserIds property from Album AddUsersDto

* chore: remove deprecated sharedUsers property from AlbumResponseDto

* chore: remove deprecated sharedWithUserIds property from CreateAlbumDto

* chore: remove deprecated isExternal and isReadOnly properties from AssetResponseDto

* chore: remove deprecated /server-info endpoint

* chore: bloody linters
2024-05-24 15:37:01 +01:00
Kedas
1f5d82e9d9 fix(mobile): respect SSL override during background sync (#9587) 2024-05-24 10:16:14 +01:00
Julian Collins
e98744f222 chore(docs): Russian readme update (#9691)
* Fix many typos, update features, add Activity and Star history sections

* Add clarity

* Add clarity
2024-05-24 10:14:07 +01:00
François-Xavier Payet
56ea07bcba fix(mobile): use correct Focus Node for latitude and longitude (#9699)
FIx focus node for longitude

Co-authored-by: François-Xavier Payet <fxpayet@salesapps.io>
2024-05-24 09:59:05 +01:00
Lukas
b3b258f32f fix(web): allow copying text in photo viewer (#9705)
* fix(web): allow copying text in photo viewer

* use default browser copy

* revert changes

* fix lint
2024-05-24 09:56:36 +01:00
Mert
69b5eb005f fix(server): use qsv format for hwmap (#9722)
use qsv format for hwmap
2024-05-24 09:50:28 +01:00
renovate[bot]
3f44a33eac chore(deps): update docker.io/redis:6.2-alpine docker digest to e31ca60 (#9717)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-05-24 09:35:59 +01:00
renovate[bot]
b2a0422efb chore(deps): update redis:6.2-alpine docker digest to e31ca60 (#9718)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-05-24 09:34:49 +01:00
Min Idzelis
562c43b6f5 test: reorder tests in asset.e2e-spec.ts (#9714)
* Reorder tests; make tests independent of ordering

* use it.each
2024-05-23 22:10:38 -04:00
Min Idzelis
4f21f6a2e1 feat: API operation replaceAsset, POST /api/asset/:id/file (#9684)
* impl and unit tests for replaceAsset

* Remove it.only

* Typo in generated spec +regen

* Remove unused dtos

* Dto removal fallout/bugfix

* fix - missed a line

* sql:generate

* Review comments

* Unused imports

* chore: clean up

---------

Co-authored-by: Jason Rasmussen <jrasm91@gmail.com>
2024-05-23 20:26:22 -04:00
Lukas
76fdcc9863 fix(web): show api key copy button in Firefox (#9704) 2024-05-23 17:16:38 -04:00
Alex
57d94bce68 feat(web): deduplication UI (#9540) 2024-05-23 12:57:25 -05:00
martin
832d728940 refactor(web): svelte actions (#9701) 2024-05-23 12:56:48 -05:00
Michel Heusschen
8bfa6769a5 fix(web): hide detail panel for shared links with hidden metadata (#9700) 2024-05-23 12:39:06 -04:00
Jason Rasmussen
e7aa50425c test: sync open api spec (#9687)
test: sync spec file
2024-05-23 07:40:57 -04:00
Mert
a5e8b451b2 feat(server): qsv hardware decoding and tone-mapping (#9689)
* qsv hw decoding and tone-mapping

* fix vaapi

* add tests

* formatting

* handle device name without path
2024-05-23 03:58:29 +00:00
Jason Rasmussen
13cbdf6851 refactor(server): cli service (#9672) 2024-05-22 22:23:47 +02:00
Jason Rasmussen
967d195a05 chore(server): remove unused code (#9670) 2024-05-22 15:53:57 -04:00
Jason Rasmussen
8f37784eae refactor(server): /user profile endpoint (#9669)
* refactor(server): user profile endpoint

* chore: open api
2024-05-22 14:31:12 -04:00
Jason Rasmussen
ecd018a826 refactor(server): user info endpoint (#9668)
* refactor(server): user info endpoint

* chore: open api
2024-05-22 14:15:33 -04:00
Jason Rasmussen
202745f14b refactor(server): plural endpoints (#9667) 2024-05-22 13:24:57 -04:00
CodaBool
6a4c2e97c0 feat: add docker healthchecks to server and ml (#9583)
* add healthcheck

* format, import, IMMICH_PORT, and eslint change

* chore: clean up nodejs healthcheck

* fix ruff formating

* add healthcheck

* format, import, IMMICH_PORT, and eslint change

* chore: clean up nodejs healthcheck

* fix ruff formating

* add healthcheck to dockerfile

* poetry run ruff check --fix

* removed 2 of 3 console calls

---------

Co-authored-by: Jason Rasmussen <jrasm91@gmail.com>
2024-05-22 16:54:29 +00:00
Jason Rasmussen
f6f82a5662 feat(web): s (#9663) 2024-05-22 09:33:37 -04:00
Michel Heusschen
ae21781442 fix(web): albums dark mode contrast + a11y issue (#9662) 2024-05-22 08:14:53 -04:00
Jason Rasmussen
06ce8247cc feat(server): user metadata (#9650)
* feat(server): user metadata

* add missing method to user mock

* update migration to include cascades

* update sql files

* test: fix e2e

* chore: clean up

---------

Co-authored-by: Daniel Dietzler <mail@ddietzler.dev>
2024-05-22 08:13:36 -04:00
dependabot[bot]
a4887bfa7e chore(deps): bump ytanikin/PRConventionalCommits from 1.1.0 to 1.2.0 (#9661)
---
updated-dependencies:
- dependency-name: ytanikin/PRConventionalCommits
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-05-22 11:43:46 +01:00
renovate[bot]
27a02c75dc chore(deps): update dependency fastlane to v2.220.0 (#9653)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-05-22 09:46:53 +00:00
Matthew Momjian
f8ee977b9e feat(server): healthchecks for PG and redis (#9590)
* HCs -> docker compose

---------

Co-authored-by: Zack Pollard <zackpollard@ymail.com>
2024-05-22 09:28:12 +00:00
Zack Pollard
a3e7e8cc31 refactor: deprecate /server-info and replace with /server-info/storage (#9645) 2024-05-22 10:25:55 +01:00
Snowknight26
a341ab0050 refactor(web): refactor album selection modal and album summary component (#9658) 2024-05-22 00:15:28 -05:00
Lukas
61b850f0ce fix(web): emit updated date when pressing enter (#9640) 2024-05-21 16:58:57 +00:00
martabal
0f6e665d99 pr feedback 2024-05-12 17:25:49 +02:00
martin
7ced61e67d Update web/src/lib/components/faces-page/unassigned-faces-side-panel.svelte
Co-authored-by: Daniel Dietzler <36593685+danieldietzler@users.noreply.github.com>
2024-05-11 22:44:48 +02:00
martin
ac26d2f45f Update web/src/lib/components/faces-page/unassigned-faces-side-panel.svelte
Co-authored-by: Daniel Dietzler <36593685+danieldietzler@users.noreply.github.com>
2024-05-11 22:44:39 +02:00
martin
782d02a5e2 Update web/src/lib/components/faces-page/person-side-panel.svelte
Co-authored-by: Daniel Dietzler <36593685+danieldietzler@users.noreply.github.com>
2024-05-11 22:44:31 +02:00
martabal
22c42dd8ff pr feedback 2024-05-11 22:43:56 +02:00
martabal
7e9dcaacff feat: unassign faces 2024-05-11 19:52:23 +02:00
356 changed files with 8604 additions and 5409 deletions

View File

@@ -9,7 +9,7 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: PR Conventional Commit Validation - name: PR Conventional Commit Validation
uses: ytanikin/PRConventionalCommits@1.1.0 uses: ytanikin/PRConventionalCommits@1.2.0
with: with:
task_types: '["feat","fix","docs","test","ci","refactor","perf","chore","revert"]' task_types: '["feat","fix","docs","test","ci","refactor","perf","chore","revert"]'
add_label: 'false' add_label: 'false'

View File

@@ -260,9 +260,18 @@ jobs:
name: OpenAPI Clients name: OpenAPI Clients
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v4 - name: Checkout code
uses: actions/checkout@v4
- name: Install server dependencies
run: npm --prefix=server ci
- name: Build the app
run: npm --prefix=server run build
- name: Run API generation - name: Run API generation
run: make open-api run: make open-api
- name: Find file changes - name: Find file changes
uses: tj-actions/verify-changed-files@v20 uses: tj-actions/verify-changed-files@v20
id: verify-changed-files id: verify-changed-files
@@ -270,6 +279,8 @@ jobs:
files: | files: |
mobile/openapi mobile/openapi
open-api/typescript-sdk open-api/typescript-sdk
open-api/immich-openapi-specs.json
- name: Verify files have not changed - name: Verify files have not changed
if: steps.verify-changed-files.outputs.files_changed == 'true' if: steps.verify-changed-files.outputs.files_changed == 'true'
run: | run: |
@@ -332,7 +343,7 @@ jobs:
exit 1 exit 1
- name: Run SQL generation - name: Run SQL generation
run: npm run sql:generate run: npm run sync:sql
env: env:
DB_URL: postgres://postgres:postgres@localhost:5432/immich DB_URL: postgres://postgres:postgres@localhost:5432/immich

View File

@@ -37,7 +37,7 @@ open-api-typescript:
cd ./open-api && bash ./bin/generate-open-api.sh typescript cd ./open-api && bash ./bin/generate-open-api.sh typescript
sql: sql:
npm --prefix server run sql:generate npm --prefix server run sync:sql
attach-server: attach-server:
docker exec -it docker_immich-server_1 sh docker exec -it docker_immich-server_1 sh

View File

@@ -1,4 +1,4 @@
import { getMyUserInfo } from '@immich/sdk'; import { getMyUser } from '@immich/sdk';
import { existsSync } from 'node:fs'; import { existsSync } from 'node:fs';
import { mkdir, unlink } from 'node:fs/promises'; import { mkdir, unlink } from 'node:fs/promises';
import { BaseOptions, connect, getAuthFilePath, logError, withError, writeAuthFile } from 'src/utils'; import { BaseOptions, connect, getAuthFilePath, logError, withError, writeAuthFile } from 'src/utils';
@@ -10,13 +10,13 @@ export const login = async (url: string, key: string, options: BaseOptions) => {
await connect(url, key); await connect(url, key);
const [error, userInfo] = await withError(getMyUserInfo()); const [error, user] = await withError(getMyUser());
if (error) { if (error) {
logError(error, 'Failed to load user info'); logError(error, 'Failed to load user info');
process.exit(1); process.exit(1);
} }
console.log(`Logged in as ${userInfo.email}`); console.log(`Logged in as ${user.email}`);
if (!existsSync(configDir)) { if (!existsSync(configDir)) {
// Create config folder if it doesn't exist // Create config folder if it doesn't exist

View File

@@ -1,4 +1,4 @@
import { getAssetStatistics, getMyUserInfo, getServerVersion, getSupportedMediaTypes } from '@immich/sdk'; import { getAssetStatistics, getMyUser, getServerVersion, getSupportedMediaTypes } from '@immich/sdk';
import { BaseOptions, authenticate } from 'src/utils'; import { BaseOptions, authenticate } from 'src/utils';
export const serverInfo = async (options: BaseOptions) => { export const serverInfo = async (options: BaseOptions) => {
@@ -8,7 +8,7 @@ export const serverInfo = async (options: BaseOptions) => {
getServerVersion(), getServerVersion(),
getSupportedMediaTypes(), getSupportedMediaTypes(),
getAssetStatistics({}), getAssetStatistics({}),
getMyUserInfo(), getMyUser(),
]); ]);
console.log(`Server Info (via ${userInfo.email})`); console.log(`Server Info (via ${userInfo.email})`);

View File

@@ -1,4 +1,4 @@
import { getMyUserInfo, init, isHttpError } from '@immich/sdk'; import { getMyUser, init, isHttpError } from '@immich/sdk';
import { glob } from 'fast-glob'; import { glob } from 'fast-glob';
import { createHash } from 'node:crypto'; import { createHash } from 'node:crypto';
import { createReadStream } from 'node:fs'; import { createReadStream } from 'node:fs';
@@ -48,7 +48,7 @@ export const connect = async (url: string, key: string) => {
init({ baseUrl: url, apiKey: key }); init({ baseUrl: url, apiKey: key });
const [error] = await withError(getMyUserInfo()); const [error] = await withError(getMyUser());
if (isHttpError(error)) { if (isHttpError(error)) {
logError(error, 'Failed to connect to server'); logError(error, 'Failed to connect to server');
process.exit(1); process.exit(1);

View File

@@ -9,6 +9,9 @@ services:
container_name: immich_server container_name: immich_server
command: ['/usr/src/app/bin/immich-dev'] command: ['/usr/src/app/bin/immich-dev']
image: immich-server-dev:latest image: immich-server-dev:latest
# extends:
# file: hwaccel.transcoding.yml
# service: cpu # set to one of [nvenc, quicksync, rkmpp, vaapi, vaapi-wsl] for accelerated transcoding
build: build:
context: ../ context: ../
dockerfile: server/Dockerfile dockerfile: server/Dockerfile
@@ -81,7 +84,9 @@ services:
redis: redis:
container_name: immich_redis container_name: immich_redis
image: redis:6.2-alpine@sha256:c0634a08e74a4bb576d02d1ee993dc05dba10e8b7b9492dfa28a7af100d46c01 image: redis:6.2-alpine@sha256:e31ca60b18f7e9b78b573d156702471d4eda038803c0b8e6f01559f350031e93
healthcheck:
test: redis-cli ping || exit 1
database: database:
container_name: immich_postgres container_name: immich_postgres
@@ -97,6 +102,11 @@ services:
- ${UPLOAD_LOCATION}/postgres:/var/lib/postgresql/data - ${UPLOAD_LOCATION}/postgres:/var/lib/postgresql/data
ports: ports:
- 5432:5432 - 5432:5432
healthcheck:
test: pg_isready --dbname='${DB_DATABASE_NAME}' || exit 1; Chksum="$$(psql --dbname='${DB_DATABASE_NAME}' --username='${DB_USERNAME}' --tuples-only --no-align --command='SELECT SUM(checksum_failures) FROM pg_stat_database')"; echo "checksum failure count is $$Chksum"; [ "$$Chksum" = '0' ] || exit 1
interval: 5m
start_interval: 30s
start_period: 5m
command: ["postgres", "-c" ,"shared_preload_libraries=vectors.so", "-c", 'search_path="$$user", public, vectors', "-c", "logging_collector=on", "-c", "max_wal_size=2GB", "-c", "shared_buffers=512MB", "-c", "wal_compression=on"] command: ["postgres", "-c" ,"shared_preload_libraries=vectors.so", "-c", 'search_path="$$user", public, vectors', "-c", "logging_collector=on", "-c", "max_wal_size=2GB", "-c", "shared_buffers=512MB", "-c", "wal_compression=on"]
# set IMMICH_METRICS=true in .env to enable metrics # set IMMICH_METRICS=true in .env to enable metrics

View File

@@ -4,6 +4,9 @@ services:
immich-server: immich-server:
container_name: immich_server container_name: immich_server
image: immich-server:latest image: immich-server:latest
# extends:
# file: hwaccel.transcoding.yml
# service: cpu # set to one of [nvenc, quicksync, rkmpp, vaapi, vaapi-wsl] for accelerated transcoding
build: build:
context: ../ context: ../
dockerfile: server/Dockerfile dockerfile: server/Dockerfile
@@ -12,12 +15,12 @@ services:
- /etc/localtime:/etc/localtime:ro - /etc/localtime:/etc/localtime:ro
env_file: env_file:
- .env - .env
restart: always
ports: ports:
- 2283:3001 - 2283:3001
depends_on: depends_on:
- redis - redis
- database - database
restart: always
immich-machine-learning: immich-machine-learning:
container_name: immich_machine_learning container_name: immich_machine_learning
@@ -38,7 +41,9 @@ services:
redis: redis:
container_name: immich_redis container_name: immich_redis
image: redis:6.2-alpine@sha256:c0634a08e74a4bb576d02d1ee993dc05dba10e8b7b9492dfa28a7af100d46c01 image: redis:6.2-alpine@sha256:e31ca60b18f7e9b78b573d156702471d4eda038803c0b8e6f01559f350031e93
healthcheck:
test: redis-cli ping || exit 1
restart: always restart: always
database: database:
@@ -55,7 +60,13 @@ services:
- ${UPLOAD_LOCATION}/postgres:/var/lib/postgresql/data - ${UPLOAD_LOCATION}/postgres:/var/lib/postgresql/data
ports: ports:
- 5432:5432 - 5432:5432
healthcheck:
test: pg_isready --dbname='${DB_DATABASE_NAME}' || exit 1; Chksum="$$(psql --dbname='${DB_DATABASE_NAME}' --username='${DB_USERNAME}' --tuples-only --no-align --command='SELECT SUM(checksum_failures) FROM pg_stat_database')"; echo "checksum failure count is $$Chksum"; [ "$$Chksum" = '0' ] || exit 1
interval: 5m
start_interval: 30s
start_period: 5m
command: ["postgres", "-c" ,"shared_preload_libraries=vectors.so", "-c", 'search_path="$$user", public, vectors', "-c", "logging_collector=on", "-c", "max_wal_size=2GB", "-c", "shared_buffers=512MB", "-c", "wal_compression=on"] command: ["postgres", "-c" ,"shared_preload_libraries=vectors.so", "-c", 'search_path="$$user", public, vectors', "-c", "logging_collector=on", "-c", "max_wal_size=2GB", "-c", "shared_buffers=512MB", "-c", "wal_compression=on"]
restart: always
# set IMMICH_METRICS=true in .env to enable metrics # set IMMICH_METRICS=true in .env to enable metrics
immich-prometheus: immich-prometheus:

View File

@@ -12,6 +12,9 @@ services:
immich-server: immich-server:
container_name: immich_server container_name: immich_server
image: ghcr.io/immich-app/immich-server:${IMMICH_VERSION:-release} image: ghcr.io/immich-app/immich-server:${IMMICH_VERSION:-release}
# extends:
# file: hwaccel.transcoding.yml
# service: cpu # set to one of [nvenc, quicksync, rkmpp, vaapi, vaapi-wsl] for accelerated transcoding
volumes: volumes:
- ${UPLOAD_LOCATION}:/usr/src/app/upload - ${UPLOAD_LOCATION}:/usr/src/app/upload
- /etc/localtime:/etc/localtime:ro - /etc/localtime:/etc/localtime:ro
@@ -40,7 +43,9 @@ services:
redis: redis:
container_name: immich_redis container_name: immich_redis
image: docker.io/redis:6.2-alpine@sha256:c0634a08e74a4bb576d02d1ee993dc05dba10e8b7b9492dfa28a7af100d46c01 image: docker.io/redis:6.2-alpine@sha256:e31ca60b18f7e9b78b573d156702471d4eda038803c0b8e6f01559f350031e93
healthcheck:
test: redis-cli ping || exit 1
restart: always restart: always
database: database:
@@ -53,8 +58,13 @@ services:
POSTGRES_INITDB_ARGS: '--data-checksums' POSTGRES_INITDB_ARGS: '--data-checksums'
volumes: volumes:
- ${DB_DATA_LOCATION}:/var/lib/postgresql/data - ${DB_DATA_LOCATION}:/var/lib/postgresql/data
restart: always healthcheck:
test: pg_isready --dbname='${DB_DATABASE_NAME}' || exit 1; Chksum="$$(psql --dbname='${DB_DATABASE_NAME}' --username='${DB_USERNAME}' --tuples-only --no-align --command='SELECT SUM(checksum_failures) FROM pg_stat_database')"; echo "checksum failure count is $$Chksum"; [ "$$Chksum" = '0' ] || exit 1
interval: 5m
start_interval: 30s
start_period: 5m
command: ["postgres", "-c" ,"shared_preload_libraries=vectors.so", "-c", 'search_path="$$user", public, vectors', "-c", "logging_collector=on", "-c", "max_wal_size=2GB", "-c", "shared_buffers=512MB", "-c", "wal_compression=on"] command: ["postgres", "-c" ,"shared_preload_libraries=vectors.so", "-c", 'search_path="$$user", public, vectors', "-c", "logging_collector=on", "-c", "max_wal_size=2GB", "-c", "shared_buffers=512MB", "-c", "wal_compression=on"]
restart: always
volumes: volumes:
model-cache: model-cache:

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 113 KiB

View File

@@ -110,8 +110,44 @@ Immich has a route (`/api/oauth/mobile-redirect`) that is already configured to
## Example Configuration ## Example Configuration
<details>
<summary>Authentik Example</summary>
### Authentik Example
Here's an example of OAuth configured for Authentik: Here's an example of OAuth configured for Authentik:
![OAuth Settings](./img/oauth-settings.png) <img src={require('./img/oauth-settings.png').default} title="OAuth settings" />
</details>
<details>
<summary>Google Example</summary>
### Google Example
Configuration of Authorised redirect URIs (Google Console)
<img src={require('./img/google-example.webp').default} width='50%' title="Authorised redirect URIs" />
Configuration of OAuth in System Settings
| Setting | Value |
| ---------------------------- | ------------------------------------------------------------------------------------------------------ |
| Issuer URL | [https://accounts.google.com](https://accounts.google.com) |
| Client ID | 7\***\*\*\*\*\*\*\***\*\*\***\*\*\*\*\*\*\***vuls.apps.googleusercontent.com |
| Client Secret | G\***\*\*\*\*\*\*\***\*\*\***\*\*\*\*\*\*\***OO |
| Scope | openid email profile |
| Signing Algorithm | RS256 |
| Storage Label Claim | preferred_username |
| Storage Quota Claim | immich_quota |
| Default Storage Quota (GiB) | 0 (0 for unlimited quota) |
| Button Text | Sign in with Google (optional) |
| Auto Register | Enabled (optional) |
| Auto Launch | Enabled |
| Mobile Redirect URI Override | Enabled (required) |
| Mobile Redirect URI | [https://demo.immich.app/api/oauth/mobile-redirect](https://demo.immich.app/api/oauth/mobile-redirect) |
</details>
[oidc]: https://openid.net/connect/ [oidc]: https://openid.net/connect/

View File

@@ -15,7 +15,7 @@ The [hwaccel.ml.yml](https://github.com/immich-app/immich/releases/latest/downlo
::: :::
```yaml ```yaml
version: '3.8' name: immich_remote_ml
services: services:
immich-machine-learning: immich-machine-learning:

View File

@@ -157,9 +157,6 @@ The default configuration looks like this:
"server": { "server": {
"externalDomain": "", "externalDomain": "",
"loginPageMessage": "" "loginPageMessage": ""
},
"user": {
"deleteDelay": 7
} }
} }
``` ```

View File

@@ -27,7 +27,7 @@ services:
- 2283:3001 - 2283:3001
redis: redis:
image: redis:6.2-alpine@sha256:c0634a08e74a4bb576d02d1ee993dc05dba10e8b7b9492dfa28a7af100d46c01 image: redis:6.2-alpine@sha256:e31ca60b18f7e9b78b573d156702471d4eda038803c0b8e6f01559f350031e93
database: database:
image: tensorchord/pgvecto-rs:pg14-v0.2.0@sha256:90724186f0a3517cf6914295b5ab410db9ce23190a2d9d0b9dd6463e3fa298f0 image: tensorchord/pgvecto-rs:pg14-v0.2.0@sha256:90724186f0a3517cf6914295b5ab410db9ce23190a2d9d0b9dd6463e3fa298f0

26
e2e/package-lock.json generated
View File

@@ -11,7 +11,7 @@
"devDependencies": { "devDependencies": {
"@immich/cli": "file:../cli", "@immich/cli": "file:../cli",
"@immich/sdk": "file:../open-api/typescript-sdk", "@immich/sdk": "file:../open-api/typescript-sdk",
"@playwright/test": "^1.41.2", "@playwright/test": "^1.44.1",
"@types/luxon": "^3.4.2", "@types/luxon": "^3.4.2",
"@types/node": "^20.11.17", "@types/node": "^20.11.17",
"@types/pg": "^8.11.0", "@types/pg": "^8.11.0",
@@ -88,7 +88,7 @@
"@oazapfts/runtime": "^1.0.2" "@oazapfts/runtime": "^1.0.2"
}, },
"devDependencies": { "devDependencies": {
"@types/node": "^20.12.12", "@types/node": "^20.11.0",
"typescript": "^5.3.3" "typescript": "^5.3.3"
} }
}, },
@@ -971,12 +971,12 @@
} }
}, },
"node_modules/@playwright/test": { "node_modules/@playwright/test": {
"version": "1.44.0", "version": "1.44.1",
"resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.44.0.tgz", "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.44.1.tgz",
"integrity": "sha512-rNX5lbNidamSUorBhB4XZ9SQTjAqfe5M+p37Z8ic0jPFBMo5iCtQz1kRWkEMg+rYOKSlVycpQmpqjSFq7LXOfg==", "integrity": "sha512-1hZ4TNvD5z9VuhNJ/walIjvMVvYkZKf71axoF/uiAqpntQJXpG64dlXhoDXE3OczPuTuvjf/M5KWFg5VAVUS3Q==",
"dev": true, "dev": true,
"dependencies": { "dependencies": {
"playwright": "1.44.0" "playwright": "1.44.1"
}, },
"bin": { "bin": {
"playwright": "cli.js" "playwright": "cli.js"
@@ -4252,12 +4252,12 @@
} }
}, },
"node_modules/playwright": { "node_modules/playwright": {
"version": "1.44.0", "version": "1.44.1",
"resolved": "https://registry.npmjs.org/playwright/-/playwright-1.44.0.tgz", "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.44.1.tgz",
"integrity": "sha512-F9b3GUCLQ3Nffrfb6dunPOkE5Mh68tR7zN32L4jCk4FjQamgesGay7/dAAe1WaMEGV04DkdJfcJzjoCKygUaRQ==", "integrity": "sha512-qr/0UJ5CFAtloI3avF95Y0L1xQo6r3LQArLIg/z/PoGJ6xa+EwzrwO5lpNr/09STxdHuUoP2mvuELJS+hLdtgg==",
"dev": true, "dev": true,
"dependencies": { "dependencies": {
"playwright-core": "1.44.0" "playwright-core": "1.44.1"
}, },
"bin": { "bin": {
"playwright": "cli.js" "playwright": "cli.js"
@@ -4270,9 +4270,9 @@
} }
}, },
"node_modules/playwright-core": { "node_modules/playwright-core": {
"version": "1.44.0", "version": "1.44.1",
"resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.44.0.tgz", "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.44.1.tgz",
"integrity": "sha512-ZTbkNpFfYcGWohvTTl+xewITm7EOuqIqex0c7dNZ+aXsbrLj0qI8XlGKfPpipjm0Wny/4Lt4CJsWJk1stVS5qQ==", "integrity": "sha512-wh0JWtYTrhv1+OSsLPgFzGzt67Y7BE/ZS3jEqgGBlp2ppp1ZDj8c+9IARNW4dwf1poq5MgHreEM2KV/GuR4cFA==",
"dev": true, "dev": true,
"bin": { "bin": {
"playwright-core": "cli.js" "playwright-core": "cli.js"

View File

@@ -21,7 +21,7 @@
"devDependencies": { "devDependencies": {
"@immich/cli": "file:../cli", "@immich/cli": "file:../cli",
"@immich/sdk": "file:../open-api/typescript-sdk", "@immich/sdk": "file:../open-api/typescript-sdk",
"@playwright/test": "^1.41.2", "@playwright/test": "^1.44.1",
"@types/luxon": "^3.4.2", "@types/luxon": "^3.4.2",
"@types/node": "^20.11.17", "@types/node": "^20.11.17",
"@types/pg": "^8.11.0", "@types/pg": "^8.11.0",

View File

@@ -14,7 +14,7 @@ import { app, asBearerAuth, utils } from 'src/utils';
import request from 'supertest'; import request from 'supertest';
import { beforeAll, beforeEach, describe, expect, it } from 'vitest'; import { beforeAll, beforeEach, describe, expect, it } from 'vitest';
describe('/activity', () => { describe('/activities', () => {
let admin: LoginResponseDto; let admin: LoginResponseDto;
let nonOwner: LoginResponseDto; let nonOwner: LoginResponseDto;
let asset: AssetFileUploadResponseDto; let asset: AssetFileUploadResponseDto;
@@ -45,22 +45,24 @@ describe('/activity', () => {
await utils.resetDatabase(['activity']); await utils.resetDatabase(['activity']);
}); });
describe('GET /activity', () => { describe('GET /activities', () => {
it('should require authentication', async () => { it('should require authentication', async () => {
const { status, body } = await request(app).get('/activity'); const { status, body } = await request(app).get('/activities');
expect(status).toBe(401); expect(status).toBe(401);
expect(body).toEqual(errorDto.unauthorized); expect(body).toEqual(errorDto.unauthorized);
}); });
it('should require an albumId', async () => { it('should require an albumId', async () => {
const { status, body } = await request(app).get('/activity').set('Authorization', `Bearer ${admin.accessToken}`); const { status, body } = await request(app)
.get('/activities')
.set('Authorization', `Bearer ${admin.accessToken}`);
expect(status).toEqual(400); expect(status).toEqual(400);
expect(body).toEqual(errorDto.badRequest(expect.arrayContaining(['albumId must be a UUID']))); expect(body).toEqual(errorDto.badRequest(expect.arrayContaining(['albumId must be a UUID'])));
}); });
it('should reject an invalid albumId', async () => { it('should reject an invalid albumId', async () => {
const { status, body } = await request(app) const { status, body } = await request(app)
.get('/activity') .get('/activities')
.query({ albumId: uuidDto.invalid }) .query({ albumId: uuidDto.invalid })
.set('Authorization', `Bearer ${admin.accessToken}`); .set('Authorization', `Bearer ${admin.accessToken}`);
expect(status).toEqual(400); expect(status).toEqual(400);
@@ -69,7 +71,7 @@ describe('/activity', () => {
it('should reject an invalid assetId', async () => { it('should reject an invalid assetId', async () => {
const { status, body } = await request(app) const { status, body } = await request(app)
.get('/activity') .get('/activities')
.query({ albumId: uuidDto.notFound, assetId: uuidDto.invalid }) .query({ albumId: uuidDto.notFound, assetId: uuidDto.invalid })
.set('Authorization', `Bearer ${admin.accessToken}`); .set('Authorization', `Bearer ${admin.accessToken}`);
expect(status).toEqual(400); expect(status).toEqual(400);
@@ -78,7 +80,7 @@ describe('/activity', () => {
it('should start off empty', async () => { it('should start off empty', async () => {
const { status, body } = await request(app) const { status, body } = await request(app)
.get('/activity') .get('/activities')
.query({ albumId: album.id }) .query({ albumId: album.id })
.set('Authorization', `Bearer ${admin.accessToken}`); .set('Authorization', `Bearer ${admin.accessToken}`);
expect(body).toEqual([]); expect(body).toEqual([]);
@@ -102,7 +104,7 @@ describe('/activity', () => {
]); ]);
const { status, body } = await request(app) const { status, body } = await request(app)
.get('/activity') .get('/activities')
.query({ albumId: album.id }) .query({ albumId: album.id })
.set('Authorization', `Bearer ${admin.accessToken}`); .set('Authorization', `Bearer ${admin.accessToken}`);
expect(status).toEqual(200); expect(status).toEqual(200);
@@ -121,7 +123,7 @@ describe('/activity', () => {
]); ]);
const { status, body } = await request(app) const { status, body } = await request(app)
.get('/activity') .get('/activities')
.query({ albumId: album.id, type: 'comment' }) .query({ albumId: album.id, type: 'comment' })
.set('Authorization', `Bearer ${admin.accessToken}`); .set('Authorization', `Bearer ${admin.accessToken}`);
expect(status).toEqual(200); expect(status).toEqual(200);
@@ -140,7 +142,7 @@ describe('/activity', () => {
]); ]);
const { status, body } = await request(app) const { status, body } = await request(app)
.get('/activity') .get('/activities')
.query({ albumId: album.id, type: 'like' }) .query({ albumId: album.id, type: 'like' })
.set('Authorization', `Bearer ${admin.accessToken}`); .set('Authorization', `Bearer ${admin.accessToken}`);
expect(status).toEqual(200); expect(status).toEqual(200);
@@ -152,7 +154,7 @@ describe('/activity', () => {
const reaction = await createActivity({ albumId: album.id, type: ReactionType.Like }); const reaction = await createActivity({ albumId: album.id, type: ReactionType.Like });
const response1 = await request(app) const response1 = await request(app)
.get('/activity') .get('/activities')
.query({ albumId: album.id, userId: uuidDto.notFound }) .query({ albumId: album.id, userId: uuidDto.notFound })
.set('Authorization', `Bearer ${admin.accessToken}`); .set('Authorization', `Bearer ${admin.accessToken}`);
@@ -160,7 +162,7 @@ describe('/activity', () => {
expect(response1.body.length).toBe(0); expect(response1.body.length).toBe(0);
const response2 = await request(app) const response2 = await request(app)
.get('/activity') .get('/activities')
.query({ albumId: album.id, userId: admin.userId }) .query({ albumId: album.id, userId: admin.userId })
.set('Authorization', `Bearer ${admin.accessToken}`); .set('Authorization', `Bearer ${admin.accessToken}`);
@@ -180,7 +182,7 @@ describe('/activity', () => {
]); ]);
const { status, body } = await request(app) const { status, body } = await request(app)
.get('/activity') .get('/activities')
.query({ albumId: album.id, assetId: asset.id }) .query({ albumId: album.id, assetId: asset.id })
.set('Authorization', `Bearer ${admin.accessToken}`); .set('Authorization', `Bearer ${admin.accessToken}`);
expect(status).toEqual(200); expect(status).toEqual(200);
@@ -189,16 +191,16 @@ describe('/activity', () => {
}); });
}); });
describe('POST /activity', () => { describe('POST /activities', () => {
it('should require authentication', async () => { it('should require authentication', async () => {
const { status, body } = await request(app).post('/activity'); const { status, body } = await request(app).post('/activities');
expect(status).toBe(401); expect(status).toBe(401);
expect(body).toEqual(errorDto.unauthorized); expect(body).toEqual(errorDto.unauthorized);
}); });
it('should require an albumId', async () => { it('should require an albumId', async () => {
const { status, body } = await request(app) const { status, body } = await request(app)
.post('/activity') .post('/activities')
.set('Authorization', `Bearer ${admin.accessToken}`) .set('Authorization', `Bearer ${admin.accessToken}`)
.send({ albumId: uuidDto.invalid }); .send({ albumId: uuidDto.invalid });
expect(status).toEqual(400); expect(status).toEqual(400);
@@ -207,7 +209,7 @@ describe('/activity', () => {
it('should require a comment when type is comment', async () => { it('should require a comment when type is comment', async () => {
const { status, body } = await request(app) const { status, body } = await request(app)
.post('/activity') .post('/activities')
.set('Authorization', `Bearer ${admin.accessToken}`) .set('Authorization', `Bearer ${admin.accessToken}`)
.send({ albumId: uuidDto.notFound, type: 'comment', comment: null }); .send({ albumId: uuidDto.notFound, type: 'comment', comment: null });
expect(status).toEqual(400); expect(status).toEqual(400);
@@ -216,7 +218,7 @@ describe('/activity', () => {
it('should add a comment to an album', async () => { it('should add a comment to an album', async () => {
const { status, body } = await request(app) const { status, body } = await request(app)
.post('/activity') .post('/activities')
.set('Authorization', `Bearer ${admin.accessToken}`) .set('Authorization', `Bearer ${admin.accessToken}`)
.send({ .send({
albumId: album.id, albumId: album.id,
@@ -236,7 +238,7 @@ describe('/activity', () => {
it('should add a like to an album', async () => { it('should add a like to an album', async () => {
const { status, body } = await request(app) const { status, body } = await request(app)
.post('/activity') .post('/activities')
.set('Authorization', `Bearer ${admin.accessToken}`) .set('Authorization', `Bearer ${admin.accessToken}`)
.send({ albumId: album.id, type: 'like' }); .send({ albumId: album.id, type: 'like' });
expect(status).toEqual(201); expect(status).toEqual(201);
@@ -253,7 +255,7 @@ describe('/activity', () => {
it('should return a 200 for a duplicate like on the album', async () => { it('should return a 200 for a duplicate like on the album', async () => {
const reaction = await createActivity({ albumId: album.id, type: ReactionType.Like }); const reaction = await createActivity({ albumId: album.id, type: ReactionType.Like });
const { status, body } = await request(app) const { status, body } = await request(app)
.post('/activity') .post('/activities')
.set('Authorization', `Bearer ${admin.accessToken}`) .set('Authorization', `Bearer ${admin.accessToken}`)
.send({ albumId: album.id, type: 'like' }); .send({ albumId: album.id, type: 'like' });
expect(status).toEqual(200); expect(status).toEqual(200);
@@ -267,7 +269,7 @@ describe('/activity', () => {
type: ReactionType.Like, type: ReactionType.Like,
}); });
const { status, body } = await request(app) const { status, body } = await request(app)
.post('/activity') .post('/activities')
.set('Authorization', `Bearer ${admin.accessToken}`) .set('Authorization', `Bearer ${admin.accessToken}`)
.send({ albumId: album.id, type: 'like' }); .send({ albumId: album.id, type: 'like' });
expect(status).toEqual(201); expect(status).toEqual(201);
@@ -276,7 +278,7 @@ describe('/activity', () => {
it('should add a comment to an asset', async () => { it('should add a comment to an asset', async () => {
const { status, body } = await request(app) const { status, body } = await request(app)
.post('/activity') .post('/activities')
.set('Authorization', `Bearer ${admin.accessToken}`) .set('Authorization', `Bearer ${admin.accessToken}`)
.send({ .send({
albumId: album.id, albumId: album.id,
@@ -297,7 +299,7 @@ describe('/activity', () => {
it('should add a like to an asset', async () => { it('should add a like to an asset', async () => {
const { status, body } = await request(app) const { status, body } = await request(app)
.post('/activity') .post('/activities')
.set('Authorization', `Bearer ${admin.accessToken}`) .set('Authorization', `Bearer ${admin.accessToken}`)
.send({ albumId: album.id, assetId: asset.id, type: 'like' }); .send({ albumId: album.id, assetId: asset.id, type: 'like' });
expect(status).toEqual(201); expect(status).toEqual(201);
@@ -319,7 +321,7 @@ describe('/activity', () => {
}); });
const { status, body } = await request(app) const { status, body } = await request(app)
.post('/activity') .post('/activities')
.set('Authorization', `Bearer ${admin.accessToken}`) .set('Authorization', `Bearer ${admin.accessToken}`)
.send({ albumId: album.id, assetId: asset.id, type: 'like' }); .send({ albumId: album.id, assetId: asset.id, type: 'like' });
expect(status).toEqual(200); expect(status).toEqual(200);
@@ -327,16 +329,16 @@ describe('/activity', () => {
}); });
}); });
describe('DELETE /activity/:id', () => { describe('DELETE /activities/:id', () => {
it('should require authentication', async () => { it('should require authentication', async () => {
const { status, body } = await request(app).delete(`/activity/${uuidDto.notFound}`); const { status, body } = await request(app).delete(`/activities/${uuidDto.notFound}`);
expect(status).toBe(401); expect(status).toBe(401);
expect(body).toEqual(errorDto.unauthorized); expect(body).toEqual(errorDto.unauthorized);
}); });
it('should require a valid uuid', async () => { it('should require a valid uuid', async () => {
const { status, body } = await request(app) const { status, body } = await request(app)
.delete(`/activity/${uuidDto.invalid}`) .delete(`/activities/${uuidDto.invalid}`)
.set('Authorization', `Bearer ${admin.accessToken}`); .set('Authorization', `Bearer ${admin.accessToken}`);
expect(status).toBe(400); expect(status).toBe(400);
expect(body).toEqual(errorDto.badRequest(['id must be a UUID'])); expect(body).toEqual(errorDto.badRequest(['id must be a UUID']));
@@ -349,7 +351,7 @@ describe('/activity', () => {
comment: 'This is a test comment', comment: 'This is a test comment',
}); });
const { status } = await request(app) const { status } = await request(app)
.delete(`/activity/${reaction.id}`) .delete(`/activities/${reaction.id}`)
.set('Authorization', `Bearer ${admin.accessToken}`); .set('Authorization', `Bearer ${admin.accessToken}`);
expect(status).toEqual(204); expect(status).toEqual(204);
}); });
@@ -360,7 +362,7 @@ describe('/activity', () => {
type: ReactionType.Like, type: ReactionType.Like,
}); });
const { status } = await request(app) const { status } = await request(app)
.delete(`/activity/${reaction.id}`) .delete(`/activities/${reaction.id}`)
.set('Authorization', `Bearer ${admin.accessToken}`); .set('Authorization', `Bearer ${admin.accessToken}`);
expect(status).toEqual(204); expect(status).toEqual(204);
}); });
@@ -373,7 +375,7 @@ describe('/activity', () => {
}); });
const { status } = await request(app) const { status } = await request(app)
.delete(`/activity/${reaction.id}`) .delete(`/activities/${reaction.id}`)
.set('Authorization', `Bearer ${admin.accessToken}`); .set('Authorization', `Bearer ${admin.accessToken}`);
expect(status).toEqual(204); expect(status).toEqual(204);
@@ -387,7 +389,7 @@ describe('/activity', () => {
}); });
const { status, body } = await request(app) const { status, body } = await request(app)
.delete(`/activity/${reaction.id}`) .delete(`/activities/${reaction.id}`)
.set('Authorization', `Bearer ${nonOwner.accessToken}`); .set('Authorization', `Bearer ${nonOwner.accessToken}`);
expect(status).toBe(400); expect(status).toBe(400);
@@ -405,7 +407,7 @@ describe('/activity', () => {
); );
const { status } = await request(app) const { status } = await request(app)
.delete(`/activity/${reaction.id}`) .delete(`/activities/${reaction.id}`)
.set('Authorization', `Bearer ${nonOwner.accessToken}`); .set('Authorization', `Bearer ${nonOwner.accessToken}`);
expect(status).toBe(204); expect(status).toBe(204);

View File

@@ -4,7 +4,7 @@ import {
AlbumUserRole, AlbumUserRole,
AssetFileUploadResponseDto, AssetFileUploadResponseDto,
AssetOrder, AssetOrder,
deleteUser, deleteUserAdmin,
getAlbumInfo, getAlbumInfo,
LoginResponseDto, LoginResponseDto,
SharedLinkType, SharedLinkType,
@@ -23,7 +23,7 @@ const user2SharedUser = 'user2SharedUser';
const user2SharedLink = 'user2SharedLink'; const user2SharedLink = 'user2SharedLink';
const user2NotShared = 'user2NotShared'; const user2NotShared = 'user2NotShared';
describe('/album', () => { describe('/albums', () => {
let admin: LoginResponseDto; let admin: LoginResponseDto;
let user1: LoginResponseDto; let user1: LoginResponseDto;
let user1Asset1: AssetFileUploadResponseDto; let user1Asset1: AssetFileUploadResponseDto;
@@ -107,19 +107,19 @@ describe('/album', () => {
}), }),
]); ]);
await deleteUser({ id: user3.userId, deleteUserDto: {} }, { headers: asBearerAuth(admin.accessToken) }); await deleteUserAdmin({ id: user3.userId, userAdminDeleteDto: {} }, { headers: asBearerAuth(admin.accessToken) });
}); });
describe('GET /album', () => { describe('GET /albums', () => {
it('should require authentication', async () => { it('should require authentication', async () => {
const { status, body } = await request(app).get('/album'); const { status, body } = await request(app).get('/albums');
expect(status).toBe(401); expect(status).toBe(401);
expect(body).toEqual(errorDto.unauthorized); expect(body).toEqual(errorDto.unauthorized);
}); });
it('should reject an invalid shared param', async () => { it('should reject an invalid shared param', async () => {
const { status, body } = await request(app) const { status, body } = await request(app)
.get('/album?shared=invalid') .get('/albums?shared=invalid')
.set('Authorization', `Bearer ${user1.accessToken}`); .set('Authorization', `Bearer ${user1.accessToken}`);
expect(status).toEqual(400); expect(status).toEqual(400);
expect(body).toEqual(errorDto.badRequest(['shared must be a boolean value'])); expect(body).toEqual(errorDto.badRequest(['shared must be a boolean value']));
@@ -127,7 +127,7 @@ describe('/album', () => {
it('should reject an invalid assetId param', async () => { it('should reject an invalid assetId param', async () => {
const { status, body } = await request(app) const { status, body } = await request(app)
.get('/album?assetId=invalid') .get('/albums?assetId=invalid')
.set('Authorization', `Bearer ${user1.accessToken}`); .set('Authorization', `Bearer ${user1.accessToken}`);
expect(status).toEqual(400); expect(status).toEqual(400);
expect(body).toEqual(errorDto.badRequest(['assetId must be a UUID'])); expect(body).toEqual(errorDto.badRequest(['assetId must be a UUID']));
@@ -135,7 +135,7 @@ describe('/album', () => {
it("should not show other users' favorites", async () => { it("should not show other users' favorites", async () => {
const { status, body } = await request(app) const { status, body } = await request(app)
.get(`/album/${user1Albums[0].id}?withoutAssets=false`) .get(`/albums/${user1Albums[0].id}?withoutAssets=false`)
.set('Authorization', `Bearer ${user2.accessToken}`); .set('Authorization', `Bearer ${user2.accessToken}`);
expect(status).toEqual(200); expect(status).toEqual(200);
expect(body).toEqual({ expect(body).toEqual({
@@ -146,7 +146,7 @@ describe('/album', () => {
it('should not return shared albums with a deleted owner', async () => { it('should not return shared albums with a deleted owner', async () => {
const { status, body } = await request(app) const { status, body } = await request(app)
.get('/album?shared=true') .get('/albums?shared=true')
.set('Authorization', `Bearer ${user1.accessToken}`); .set('Authorization', `Bearer ${user1.accessToken}`);
expect(status).toBe(200); expect(status).toBe(200);
@@ -178,7 +178,7 @@ describe('/album', () => {
}); });
it('should return the album collection including owned and shared', async () => { it('should return the album collection including owned and shared', async () => {
const { status, body } = await request(app).get('/album').set('Authorization', `Bearer ${user1.accessToken}`); const { status, body } = await request(app).get('/albums').set('Authorization', `Bearer ${user1.accessToken}`);
expect(status).toBe(200); expect(status).toBe(200);
expect(body).toHaveLength(4); expect(body).toHaveLength(4);
expect(body).toEqual( expect(body).toEqual(
@@ -209,7 +209,7 @@ describe('/album', () => {
it('should return the album collection filtered by shared', async () => { it('should return the album collection filtered by shared', async () => {
const { status, body } = await request(app) const { status, body } = await request(app)
.get('/album?shared=true') .get('/albums?shared=true')
.set('Authorization', `Bearer ${user1.accessToken}`); .set('Authorization', `Bearer ${user1.accessToken}`);
expect(status).toBe(200); expect(status).toBe(200);
expect(body).toHaveLength(4); expect(body).toHaveLength(4);
@@ -241,7 +241,7 @@ describe('/album', () => {
it('should return the album collection filtered by NOT shared', async () => { it('should return the album collection filtered by NOT shared', async () => {
const { status, body } = await request(app) const { status, body } = await request(app)
.get('/album?shared=false') .get('/albums?shared=false')
.set('Authorization', `Bearer ${user1.accessToken}`); .set('Authorization', `Bearer ${user1.accessToken}`);
expect(status).toBe(200); expect(status).toBe(200);
expect(body).toHaveLength(1); expect(body).toHaveLength(1);
@@ -258,7 +258,7 @@ describe('/album', () => {
it('should return the album collection filtered by assetId', async () => { it('should return the album collection filtered by assetId', async () => {
const { status, body } = await request(app) const { status, body } = await request(app)
.get(`/album?assetId=${user1Asset2.id}`) .get(`/albums?assetId=${user1Asset2.id}`)
.set('Authorization', `Bearer ${user1.accessToken}`); .set('Authorization', `Bearer ${user1.accessToken}`);
expect(status).toBe(200); expect(status).toBe(200);
expect(body).toHaveLength(1); expect(body).toHaveLength(1);
@@ -266,7 +266,7 @@ describe('/album', () => {
it('should return the album collection filtered by assetId and ignores shared=true', async () => { it('should return the album collection filtered by assetId and ignores shared=true', async () => {
const { status, body } = await request(app) const { status, body } = await request(app)
.get(`/album?shared=true&assetId=${user1Asset1.id}`) .get(`/albums?shared=true&assetId=${user1Asset1.id}`)
.set('Authorization', `Bearer ${user1.accessToken}`); .set('Authorization', `Bearer ${user1.accessToken}`);
expect(status).toBe(200); expect(status).toBe(200);
expect(body).toHaveLength(5); expect(body).toHaveLength(5);
@@ -274,23 +274,23 @@ describe('/album', () => {
it('should return the album collection filtered by assetId and ignores shared=false', async () => { it('should return the album collection filtered by assetId and ignores shared=false', async () => {
const { status, body } = await request(app) const { status, body } = await request(app)
.get(`/album?shared=false&assetId=${user1Asset1.id}`) .get(`/albums?shared=false&assetId=${user1Asset1.id}`)
.set('Authorization', `Bearer ${user1.accessToken}`); .set('Authorization', `Bearer ${user1.accessToken}`);
expect(status).toBe(200); expect(status).toBe(200);
expect(body).toHaveLength(5); expect(body).toHaveLength(5);
}); });
}); });
describe('GET /album/:id', () => { describe('GET /albums/:id', () => {
it('should require authentication', async () => { it('should require authentication', async () => {
const { status, body } = await request(app).get(`/album/${user1Albums[0].id}`); const { status, body } = await request(app).get(`/albums/${user1Albums[0].id}`);
expect(status).toBe(401); expect(status).toBe(401);
expect(body).toEqual(errorDto.unauthorized); expect(body).toEqual(errorDto.unauthorized);
}); });
it('should return album info for own album', async () => { it('should return album info for own album', async () => {
const { status, body } = await request(app) const { status, body } = await request(app)
.get(`/album/${user1Albums[0].id}?withoutAssets=false`) .get(`/albums/${user1Albums[0].id}?withoutAssets=false`)
.set('Authorization', `Bearer ${user1.accessToken}`); .set('Authorization', `Bearer ${user1.accessToken}`);
expect(status).toBe(200); expect(status).toBe(200);
@@ -302,7 +302,7 @@ describe('/album', () => {
it('should return album info for shared album (editor)', async () => { it('should return album info for shared album (editor)', async () => {
const { status, body } = await request(app) const { status, body } = await request(app)
.get(`/album/${user2Albums[0].id}?withoutAssets=false`) .get(`/albums/${user2Albums[0].id}?withoutAssets=false`)
.set('Authorization', `Bearer ${user1.accessToken}`); .set('Authorization', `Bearer ${user1.accessToken}`);
expect(status).toBe(200); expect(status).toBe(200);
@@ -311,7 +311,7 @@ describe('/album', () => {
it('should return album info for shared album (viewer)', async () => { it('should return album info for shared album (viewer)', async () => {
const { status, body } = await request(app) const { status, body } = await request(app)
.get(`/album/${user1Albums[3].id}?withoutAssets=false`) .get(`/albums/${user1Albums[3].id}?withoutAssets=false`)
.set('Authorization', `Bearer ${user2.accessToken}`); .set('Authorization', `Bearer ${user2.accessToken}`);
expect(status).toBe(200); expect(status).toBe(200);
@@ -320,7 +320,7 @@ describe('/album', () => {
it('should return album info with assets when withoutAssets is undefined', async () => { it('should return album info with assets when withoutAssets is undefined', async () => {
const { status, body } = await request(app) const { status, body } = await request(app)
.get(`/album/${user1Albums[0].id}`) .get(`/albums/${user1Albums[0].id}`)
.set('Authorization', `Bearer ${user1.accessToken}`); .set('Authorization', `Bearer ${user1.accessToken}`);
expect(status).toBe(200); expect(status).toBe(200);
@@ -332,7 +332,7 @@ describe('/album', () => {
it('should return album info without assets when withoutAssets is true', async () => { it('should return album info without assets when withoutAssets is true', async () => {
const { status, body } = await request(app) const { status, body } = await request(app)
.get(`/album/${user1Albums[0].id}?withoutAssets=true`) .get(`/albums/${user1Albums[0].id}?withoutAssets=true`)
.set('Authorization', `Bearer ${user1.accessToken}`); .set('Authorization', `Bearer ${user1.accessToken}`);
expect(status).toBe(200); expect(status).toBe(200);
@@ -344,16 +344,16 @@ describe('/album', () => {
}); });
}); });
describe('GET /album/count', () => { describe('GET /albums/count', () => {
it('should require authentication', async () => { it('should require authentication', async () => {
const { status, body } = await request(app).get('/album/count'); const { status, body } = await request(app).get('/albums/count');
expect(status).toBe(401); expect(status).toBe(401);
expect(body).toEqual(errorDto.unauthorized); expect(body).toEqual(errorDto.unauthorized);
}); });
it('should return total count of albums the user has access to', async () => { it('should return total count of albums the user has access to', async () => {
const { status, body } = await request(app) const { status, body } = await request(app)
.get('/album/count') .get('/albums/count')
.set('Authorization', `Bearer ${user1.accessToken}`); .set('Authorization', `Bearer ${user1.accessToken}`);
expect(status).toBe(200); expect(status).toBe(200);
@@ -361,16 +361,16 @@ describe('/album', () => {
}); });
}); });
describe('POST /album', () => { describe('POST /albums', () => {
it('should require authentication', async () => { it('should require authentication', async () => {
const { status, body } = await request(app).post('/album').send({ albumName: 'New album' }); const { status, body } = await request(app).post('/albums').send({ albumName: 'New album' });
expect(status).toBe(401); expect(status).toBe(401);
expect(body).toEqual(errorDto.unauthorized); expect(body).toEqual(errorDto.unauthorized);
}); });
it('should create an album', async () => { it('should create an album', async () => {
const { status, body } = await request(app) const { status, body } = await request(app)
.post('/album') .post('/albums')
.send({ albumName: 'New album' }) .send({ albumName: 'New album' })
.set('Authorization', `Bearer ${user1.accessToken}`); .set('Authorization', `Bearer ${user1.accessToken}`);
expect(status).toBe(201); expect(status).toBe(201);
@@ -383,7 +383,6 @@ describe('/album', () => {
description: '', description: '',
albumThumbnailAssetId: null, albumThumbnailAssetId: null,
shared: false, shared: false,
sharedUsers: [],
albumUsers: [], albumUsers: [],
hasSharedLink: false, hasSharedLink: false,
assets: [], assets: [],
@@ -395,9 +394,9 @@ describe('/album', () => {
}); });
}); });
describe('PUT /album/:id/assets', () => { describe('PUT /albums/:id/assets', () => {
it('should require authentication', async () => { it('should require authentication', async () => {
const { status, body } = await request(app).put(`/album/${user1Albums[0].id}/assets`); const { status, body } = await request(app).put(`/albums/${user1Albums[0].id}/assets`);
expect(status).toBe(401); expect(status).toBe(401);
expect(body).toEqual(errorDto.unauthorized); expect(body).toEqual(errorDto.unauthorized);
}); });
@@ -405,7 +404,7 @@ describe('/album', () => {
it('should be able to add own asset to own album', async () => { it('should be able to add own asset to own album', async () => {
const asset = await utils.createAsset(user1.accessToken); const asset = await utils.createAsset(user1.accessToken);
const { status, body } = await request(app) const { status, body } = await request(app)
.put(`/album/${user1Albums[0].id}/assets`) .put(`/albums/${user1Albums[0].id}/assets`)
.set('Authorization', `Bearer ${user1.accessToken}`) .set('Authorization', `Bearer ${user1.accessToken}`)
.send({ ids: [asset.id] }); .send({ ids: [asset.id] });
@@ -416,7 +415,7 @@ describe('/album', () => {
it('should be able to add own asset to shared album', async () => { it('should be able to add own asset to shared album', async () => {
const asset = await utils.createAsset(user1.accessToken); const asset = await utils.createAsset(user1.accessToken);
const { status, body } = await request(app) const { status, body } = await request(app)
.put(`/album/${user2Albums[0].id}/assets`) .put(`/albums/${user2Albums[0].id}/assets`)
.set('Authorization', `Bearer ${user1.accessToken}`) .set('Authorization', `Bearer ${user1.accessToken}`)
.send({ ids: [asset.id] }); .send({ ids: [asset.id] });
@@ -427,7 +426,7 @@ describe('/album', () => {
it('should not be able to add assets to album as a viewer', async () => { it('should not be able to add assets to album as a viewer', async () => {
const asset = await utils.createAsset(user2.accessToken); const asset = await utils.createAsset(user2.accessToken);
const { status, body } = await request(app) const { status, body } = await request(app)
.put(`/album/${user1Albums[3].id}/assets`) .put(`/albums/${user1Albums[3].id}/assets`)
.set('Authorization', `Bearer ${user2.accessToken}`) .set('Authorization', `Bearer ${user2.accessToken}`)
.send({ ids: [asset.id] }); .send({ ids: [asset.id] });
@@ -438,7 +437,7 @@ describe('/album', () => {
it('should add duplicate assets only once', async () => { it('should add duplicate assets only once', async () => {
const asset = await utils.createAsset(user1.accessToken); const asset = await utils.createAsset(user1.accessToken);
const { status, body } = await request(app) const { status, body } = await request(app)
.put(`/album/${user1Albums[0].id}/assets`) .put(`/albums/${user1Albums[0].id}/assets`)
.set('Authorization', `Bearer ${user1.accessToken}`) .set('Authorization', `Bearer ${user1.accessToken}`)
.send({ ids: [asset.id, asset.id] }); .send({ ids: [asset.id, asset.id] });
@@ -450,10 +449,10 @@ describe('/album', () => {
}); });
}); });
describe('PATCH /album/:id', () => { describe('PATCH /albums/:id', () => {
it('should require authentication', async () => { it('should require authentication', async () => {
const { status, body } = await request(app) const { status, body } = await request(app)
.patch(`/album/${uuidDto.notFound}`) .patch(`/albums/${uuidDto.notFound}`)
.send({ albumName: 'New album name' }); .send({ albumName: 'New album name' });
expect(status).toBe(401); expect(status).toBe(401);
expect(body).toEqual(errorDto.unauthorized); expect(body).toEqual(errorDto.unauthorized);
@@ -464,7 +463,7 @@ describe('/album', () => {
albumName: 'New album', albumName: 'New album',
}); });
const { status, body } = await request(app) const { status, body } = await request(app)
.patch(`/album/${album.id}`) .patch(`/albums/${album.id}`)
.set('Authorization', `Bearer ${user1.accessToken}`) .set('Authorization', `Bearer ${user1.accessToken}`)
.send({ .send({
albumName: 'New album name', albumName: 'New album name',
@@ -481,7 +480,7 @@ describe('/album', () => {
it('should not be able to update as a viewer', async () => { it('should not be able to update as a viewer', async () => {
const { status, body } = await request(app) const { status, body } = await request(app)
.patch(`/album/${user1Albums[3].id}`) .patch(`/albums/${user1Albums[3].id}`)
.set('Authorization', `Bearer ${user2.accessToken}`) .set('Authorization', `Bearer ${user2.accessToken}`)
.send({ albumName: 'New album name' }); .send({ albumName: 'New album name' });
@@ -491,7 +490,7 @@ describe('/album', () => {
it('should not be able to update as an editor', async () => { it('should not be able to update as an editor', async () => {
const { status, body } = await request(app) const { status, body } = await request(app)
.patch(`/album/${user1Albums[0].id}`) .patch(`/albums/${user1Albums[0].id}`)
.set('Authorization', `Bearer ${user2.accessToken}`) .set('Authorization', `Bearer ${user2.accessToken}`)
.send({ albumName: 'New album name' }); .send({ albumName: 'New album name' });
@@ -500,10 +499,10 @@ describe('/album', () => {
}); });
}); });
describe('DELETE /album/:id/assets', () => { describe('DELETE /albums/:id/assets', () => {
it('should require authentication', async () => { it('should require authentication', async () => {
const { status, body } = await request(app) const { status, body } = await request(app)
.delete(`/album/${user1Albums[0].id}/assets`) .delete(`/albums/${user1Albums[0].id}/assets`)
.send({ ids: [user1Asset1.id] }); .send({ ids: [user1Asset1.id] });
expect(status).toBe(401); expect(status).toBe(401);
@@ -512,7 +511,7 @@ describe('/album', () => {
it('should not be able to remove foreign asset from own album', async () => { it('should not be able to remove foreign asset from own album', async () => {
const { status, body } = await request(app) const { status, body } = await request(app)
.delete(`/album/${user2Albums[0].id}/assets`) .delete(`/albums/${user2Albums[0].id}/assets`)
.set('Authorization', `Bearer ${user2.accessToken}`) .set('Authorization', `Bearer ${user2.accessToken}`)
.send({ ids: [user1Asset1.id] }); .send({ ids: [user1Asset1.id] });
@@ -528,7 +527,7 @@ describe('/album', () => {
it('should not be able to remove foreign asset from foreign album', async () => { it('should not be able to remove foreign asset from foreign album', async () => {
const { status, body } = await request(app) const { status, body } = await request(app)
.delete(`/album/${user1Albums[0].id}/assets`) .delete(`/albums/${user1Albums[0].id}/assets`)
.set('Authorization', `Bearer ${user2.accessToken}`) .set('Authorization', `Bearer ${user2.accessToken}`)
.send({ ids: [user1Asset1.id] }); .send({ ids: [user1Asset1.id] });
@@ -544,7 +543,7 @@ describe('/album', () => {
it('should be able to remove own asset from own album', async () => { it('should be able to remove own asset from own album', async () => {
const { status, body } = await request(app) const { status, body } = await request(app)
.delete(`/album/${user1Albums[0].id}/assets`) .delete(`/albums/${user1Albums[0].id}/assets`)
.set('Authorization', `Bearer ${user1.accessToken}`) .set('Authorization', `Bearer ${user1.accessToken}`)
.send({ ids: [user1Asset1.id] }); .send({ ids: [user1Asset1.id] });
@@ -554,7 +553,7 @@ describe('/album', () => {
it('should be able to remove own asset from shared album', async () => { it('should be able to remove own asset from shared album', async () => {
const { status, body } = await request(app) const { status, body } = await request(app)
.delete(`/album/${user2Albums[0].id}/assets`) .delete(`/albums/${user2Albums[0].id}/assets`)
.set('Authorization', `Bearer ${user1.accessToken}`) .set('Authorization', `Bearer ${user1.accessToken}`)
.send({ ids: [user1Asset1.id] }); .send({ ids: [user1Asset1.id] });
@@ -564,7 +563,7 @@ describe('/album', () => {
it('should not be able to remove assets from album as a viewer', async () => { it('should not be able to remove assets from album as a viewer', async () => {
const { status, body } = await request(app) const { status, body } = await request(app)
.delete(`/album/${user1Albums[3].id}/assets`) .delete(`/albums/${user1Albums[3].id}/assets`)
.set('Authorization', `Bearer ${user2.accessToken}`) .set('Authorization', `Bearer ${user2.accessToken}`)
.send({ ids: [user1Asset1.id] }); .send({ ids: [user1Asset1.id] });
@@ -574,7 +573,7 @@ describe('/album', () => {
it('should remove duplicate assets only once', async () => { it('should remove duplicate assets only once', async () => {
const { status, body } = await request(app) const { status, body } = await request(app)
.delete(`/album/${user1Albums[1].id}/assets`) .delete(`/albums/${user1Albums[1].id}/assets`)
.set('Authorization', `Bearer ${user1.accessToken}`) .set('Authorization', `Bearer ${user1.accessToken}`)
.send({ ids: [user1Asset1.id, user1Asset1.id] }); .send({ ids: [user1Asset1.id, user1Asset1.id] });
@@ -596,7 +595,7 @@ describe('/album', () => {
}); });
it('should require authentication', async () => { it('should require authentication', async () => {
const { status, body } = await request(app).put(`/album/${user1Albums[0].id}/users`).send({ sharedUserIds: [] }); const { status, body } = await request(app).put(`/albums/${user1Albums[0].id}/users`).send({ sharedUserIds: [] });
expect(status).toBe(401); expect(status).toBe(401);
expect(body).toEqual(errorDto.unauthorized); expect(body).toEqual(errorDto.unauthorized);
@@ -604,21 +603,25 @@ describe('/album', () => {
it('should be able to add user to own album', async () => { it('should be able to add user to own album', async () => {
const { status, body } = await request(app) const { status, body } = await request(app)
.put(`/album/${album.id}/users`) .put(`/albums/${album.id}/users`)
.set('Authorization', `Bearer ${user1.accessToken}`) .set('Authorization', `Bearer ${user1.accessToken}`)
.send({ albumUsers: [{ userId: user2.userId, role: AlbumUserRole.Editor }] }); .send({ albumUsers: [{ userId: user2.userId, role: AlbumUserRole.Editor }] });
expect(status).toBe(200); expect(status).toBe(200);
expect(body).toEqual( expect(body).toEqual(
expect.objectContaining({ expect.objectContaining({
sharedUsers: [expect.objectContaining({ id: user2.userId })], albumUsers: [
expect.objectContaining({
user: expect.objectContaining({ id: user2.userId }),
}),
],
}), }),
); );
}); });
it('should not be able to share album with owner', async () => { it('should not be able to share album with owner', async () => {
const { status, body } = await request(app) const { status, body } = await request(app)
.put(`/album/${album.id}/users`) .put(`/albums/${album.id}/users`)
.set('Authorization', `Bearer ${user1.accessToken}`) .set('Authorization', `Bearer ${user1.accessToken}`)
.send({ albumUsers: [{ userId: user1.userId, role: AlbumUserRole.Editor }] }); .send({ albumUsers: [{ userId: user1.userId, role: AlbumUserRole.Editor }] });
@@ -628,12 +631,12 @@ describe('/album', () => {
it('should not be able to add existing user to shared album', async () => { it('should not be able to add existing user to shared album', async () => {
await request(app) await request(app)
.put(`/album/${album.id}/users`) .put(`/albums/${album.id}/users`)
.set('Authorization', `Bearer ${user1.accessToken}`) .set('Authorization', `Bearer ${user1.accessToken}`)
.send({ albumUsers: [{ userId: user2.userId, role: AlbumUserRole.Editor }] }); .send({ albumUsers: [{ userId: user2.userId, role: AlbumUserRole.Editor }] });
const { status, body } = await request(app) const { status, body } = await request(app)
.put(`/album/${album.id}/users`) .put(`/albums/${album.id}/users`)
.set('Authorization', `Bearer ${user1.accessToken}`) .set('Authorization', `Bearer ${user1.accessToken}`)
.send({ albumUsers: [{ userId: user2.userId, role: AlbumUserRole.Editor }] }); .send({ albumUsers: [{ userId: user2.userId, role: AlbumUserRole.Editor }] });
@@ -652,14 +655,16 @@ describe('/album', () => {
expect(album.albumUsers[0].role).toEqual(AlbumUserRole.Viewer); expect(album.albumUsers[0].role).toEqual(AlbumUserRole.Viewer);
const { status } = await request(app) const { status } = await request(app)
.put(`/album/${album.id}/user/${user2.userId}`) .put(`/albums/${album.id}/user/${user2.userId}`)
.set('Authorization', `Bearer ${user1.accessToken}`) .set('Authorization', `Bearer ${user1.accessToken}`)
.send({ role: AlbumUserRole.Editor }); .send({ role: AlbumUserRole.Editor });
expect(status).toBe(200); expect(status).toBe(200);
// Get album to verify the role change // Get album to verify the role change
const { body } = await request(app).get(`/album/${album.id}`).set('Authorization', `Bearer ${user1.accessToken}`); const { body } = await request(app)
.get(`/albums/${album.id}`)
.set('Authorization', `Bearer ${user1.accessToken}`);
expect(body).toEqual( expect(body).toEqual(
expect.objectContaining({ expect.objectContaining({
albumUsers: [expect.objectContaining({ role: AlbumUserRole.Editor })], albumUsers: [expect.objectContaining({ role: AlbumUserRole.Editor })],
@@ -676,7 +681,7 @@ describe('/album', () => {
expect(album.albumUsers[0].role).toEqual(AlbumUserRole.Viewer); expect(album.albumUsers[0].role).toEqual(AlbumUserRole.Viewer);
const { status, body } = await request(app) const { status, body } = await request(app)
.put(`/album/${album.id}/user/${user2.userId}`) .put(`/albums/${album.id}/user/${user2.userId}`)
.set('Authorization', `Bearer ${user2.accessToken}`) .set('Authorization', `Bearer ${user2.accessToken}`)
.send({ role: AlbumUserRole.Editor }); .send({ role: AlbumUserRole.Editor });

View File

@@ -5,6 +5,7 @@ import {
LoginResponseDto, LoginResponseDto,
SharedLinkType, SharedLinkType,
getAssetInfo, getAssetInfo,
getMyUser,
updateAssets, updateAssets,
} from '@immich/sdk'; } from '@immich/sdk';
import { exiftool } from 'exiftool-vendored'; import { exiftool } from 'exiftool-vendored';
@@ -71,7 +72,7 @@ describe('/asset', () => {
let stackAssets: AssetFileUploadResponseDto[]; let stackAssets: AssetFileUploadResponseDto[];
let locationAsset: AssetFileUploadResponseDto; let locationAsset: AssetFileUploadResponseDto;
beforeAll(async () => { const setupTests = async () => {
await utils.resetDatabase(); await utils.resetDatabase();
admin = await utils.adminSetup({ onboarding: false }); admin = await utils.adminSetup({ onboarding: false });
@@ -85,6 +86,8 @@ describe('/asset', () => {
utils.userSetup(admin.accessToken, createUserDto.create('stack')), utils.userSetup(admin.accessToken, createUserDto.create('stack')),
]); ]);
await utils.createPartner(user1.accessToken, user2.userId);
// asset location // asset location
locationAsset = await utils.createAsset(admin.accessToken, { locationAsset = await utils.createAsset(admin.accessToken, {
assetData: { assetData: {
@@ -154,7 +157,8 @@ describe('/asset', () => {
assetId: user1Assets[0].id, assetId: user1Assets[0].id,
personId: person1.id, personId: person1.id,
}); });
}, 30_000); };
beforeAll(setupTests, 30_000);
afterAll(() => { afterAll(() => {
utils.disconnectWebsocket(websocket); utils.disconnectWebsocket(websocket);
@@ -211,15 +215,18 @@ describe('/asset', () => {
expect(body).toMatchObject({ expect(body).toMatchObject({
id: user1Assets[0].id, id: user1Assets[0].id,
isFavorite: false, isFavorite: false,
people: [ people: {
{ visiblePeople: [
birthDate: null, {
id: expect.any(String), birthDate: null,
isHidden: false, id: expect.any(String),
name: 'Test Person', isHidden: false,
thumbnailPath: '/my/awesome/thumbnail.jpg', name: 'Test Person',
}, thumbnailPath: '/my/awesome/thumbnail.jpg',
], },
],
numberOfFaces: 1,
},
}); });
const sharedLink = await utils.createSharedLink(user1.accessToken, { const sharedLink = await utils.createSharedLink(user1.accessToken, {
@@ -229,7 +236,36 @@ describe('/asset', () => {
const data = await request(app).get(`/asset/${user1Assets[0].id}?key=${sharedLink.key}`); const data = await request(app).get(`/asset/${user1Assets[0].id}?key=${sharedLink.key}`);
expect(data.status).toBe(200); expect(data.status).toBe(200);
expect(data.body).toMatchObject({ people: [] }); expect(data.body).not.toHaveProperty('people');
});
describe('partner assets', () => {
it('should get the asset info', async () => {
const { status, body } = await request(app)
.get(`/asset/${user1Assets[0].id}`)
.set('Authorization', `Bearer ${user2.accessToken}`);
expect(status).toBe(200);
expect(body).toMatchObject({ id: user1Assets[0].id });
});
it('disallows viewing archived assets', async () => {
const asset = await utils.createAsset(user1.accessToken, { isArchived: true });
const { status } = await request(app)
.get(`/asset/${asset.id}`)
.set('Authorization', `Bearer ${user2.accessToken}`);
expect(status).toBe(400);
});
it('disallows viewing trashed assets', async () => {
const asset = await utils.createAsset(user1.accessToken);
await utils.deleteAssets(user1.accessToken, [asset.id]);
const { status } = await request(app)
.get(`/asset/${asset.id}`)
.set('Authorization', `Bearer ${user2.accessToken}`);
expect(status).toBe(400);
});
}); });
}); });
@@ -478,15 +514,18 @@ describe('/asset', () => {
expect(body).toMatchObject({ expect(body).toMatchObject({
id: user1Assets[0].id, id: user1Assets[0].id,
isFavorite: true, isFavorite: true,
people: [ people: {
{ visiblePeople: [
birthDate: null, {
id: expect.any(String), birthDate: null,
isHidden: false, id: expect.any(String),
name: 'Test Person', isHidden: false,
thumbnailPath: '/my/awesome/thumbnail.jpg', name: 'Test Person',
}, thumbnailPath: '/my/awesome/thumbnail.jpg',
], },
],
numberOfFaces: 1,
},
}); });
}); });
}); });
@@ -538,14 +577,321 @@ describe('/asset', () => {
}); });
}); });
describe('GET /asset/thumbnail/:id', () => {
it('should require authentication', async () => {
const { status, body } = await request(app).get(`/asset/thumbnail/${locationAsset.id}`);
expect(status).toBe(401);
expect(body).toEqual(errorDto.unauthorized);
});
it('should not include gps data for webp thumbnails', async () => {
await utils.waitForWebsocketEvent({
event: 'assetUpload',
id: locationAsset.id,
});
const { status, body, type } = await request(app)
.get(`/asset/thumbnail/${locationAsset.id}?format=WEBP`)
.set('Authorization', `Bearer ${admin.accessToken}`);
expect(status).toBe(200);
expect(body).toBeDefined();
expect(type).toBe('image/webp');
const exifData = await readTags(body, 'thumbnail.webp');
expect(exifData).not.toHaveProperty('GPSLongitude');
expect(exifData).not.toHaveProperty('GPSLatitude');
});
it('should not include gps data for jpeg thumbnails', async () => {
const { status, body, type } = await request(app)
.get(`/asset/thumbnail/${locationAsset.id}?format=JPEG`)
.set('Authorization', `Bearer ${admin.accessToken}`);
expect(status).toBe(200);
expect(body).toBeDefined();
expect(type).toBe('image/jpeg');
const exifData = await readTags(body, 'thumbnail.jpg');
expect(exifData).not.toHaveProperty('GPSLongitude');
expect(exifData).not.toHaveProperty('GPSLatitude');
});
});
describe('GET /asset/file/:id', () => {
it('should require authentication', async () => {
const { status, body } = await request(app).get(`/asset/thumbnail/${locationAsset.id}`);
expect(status).toBe(401);
expect(body).toEqual(errorDto.unauthorized);
});
it('should download the original', async () => {
const { status, body, type } = await request(app)
.get(`/asset/file/${locationAsset.id}`)
.set('Authorization', `Bearer ${admin.accessToken}`);
expect(status).toBe(200);
expect(body).toBeDefined();
expect(type).toBe('image/jpeg');
const asset = await utils.getAssetInfo(admin.accessToken, locationAsset.id);
const original = await readFile(locationAssetFilepath);
const originalChecksum = utils.sha1(original);
const downloadChecksum = utils.sha1(body);
expect(originalChecksum).toBe(downloadChecksum);
expect(downloadChecksum).toBe(asset.checksum);
});
});
describe('GET /asset/map-marker', () => {
beforeAll(async () => {
const files = [
'formats/avif/8bit-sRGB.avif',
'formats/jpg/el_torcal_rocks.jpg',
'formats/jxl/8bit-sRGB.jxl',
'formats/heic/IMG_2682.heic',
'formats/png/density_plot.png',
'formats/raw/Nikon/D80/glarus.nef',
'formats/raw/Nikon/D700/philadelphia.nef',
'formats/raw/Panasonic/DMC-GH4/4_3.rw2',
'formats/raw/Sony/ILCE-6300/12bit-compressed-(3_2).arw',
'formats/raw/Sony/ILCE-7M2/14bit-uncompressed-(3_2).arw',
];
utils.resetEvents();
const uploadFile = async (input: string) => {
const filepath = join(testAssetDir, input);
const { id } = await utils.createAsset(admin.accessToken, {
assetData: { bytes: await readFile(filepath), filename: basename(filepath) },
});
await utils.waitForWebsocketEvent({ event: 'assetUpload', id });
};
const uploads = files.map((f) => uploadFile(f));
await Promise.all(uploads);
}, 30_000);
it('should require authentication', async () => {
const { status, body } = await request(app).get('/asset/map-marker');
expect(status).toBe(401);
expect(body).toEqual(errorDto.unauthorized);
});
// TODO archive one of these assets
it('should get map markers for all non-archived assets', async () => {
const { status, body } = await request(app)
.get('/asset/map-marker')
.query({ isArchived: false })
.set('Authorization', `Bearer ${admin.accessToken}`);
expect(status).toBe(200);
expect(body).toHaveLength(2);
expect(body).toEqual([
{
city: 'Palisade',
country: 'United States of America',
id: expect.any(String),
lat: expect.closeTo(39.115),
lon: expect.closeTo(-108.400_968),
state: 'Colorado',
},
{
city: 'Ralston',
country: 'United States of America',
id: expect.any(String),
lat: expect.closeTo(41.2203),
lon: expect.closeTo(-96.071_625),
state: 'Nebraska',
},
]);
});
// TODO archive one of these assets
it('should get all map markers', async () => {
const { status, body } = await request(app)
.get('/asset/map-marker')
.set('Authorization', `Bearer ${admin.accessToken}`);
expect(status).toBe(200);
expect(body).toEqual([
{
city: 'Palisade',
country: 'United States of America',
id: expect.any(String),
lat: expect.closeTo(39.115),
lon: expect.closeTo(-108.400_968),
state: 'Colorado',
},
{
city: 'Ralston',
country: 'United States of America',
id: expect.any(String),
lat: expect.closeTo(41.2203),
lon: expect.closeTo(-96.071_625),
state: 'Nebraska',
},
]);
});
});
describe('PUT /asset', () => {
it('should require authentication', async () => {
const { status, body } = await request(app).put('/asset');
expect(status).toBe(401);
expect(body).toEqual(errorDto.unauthorized);
});
it('should require a valid parent id', async () => {
const { status, body } = await request(app)
.put('/asset')
.set('Authorization', `Bearer ${user1.accessToken}`)
.send({ stackParentId: uuidDto.invalid, ids: [stackAssets[0].id] });
expect(status).toBe(400);
expect(body).toEqual(errorDto.badRequest(['stackParentId must be a UUID']));
});
it('should require access to the parent', async () => {
const { status, body } = await request(app)
.put('/asset')
.set('Authorization', `Bearer ${user1.accessToken}`)
.send({ stackParentId: stackAssets[3].id, ids: [user1Assets[0].id] });
expect(status).toBe(400);
expect(body).toEqual(errorDto.noPermission);
});
it('should add stack children', async () => {
const { status } = await request(app)
.put('/asset')
.set('Authorization', `Bearer ${stackUser.accessToken}`)
.send({ stackParentId: stackAssets[0].id, ids: [stackAssets[3].id] });
expect(status).toBe(204);
const asset = await getAssetInfo({ id: stackAssets[0].id }, { headers: asBearerAuth(stackUser.accessToken) });
expect(asset.stack).not.toBeUndefined();
expect(asset.stack).toEqual(expect.arrayContaining([expect.objectContaining({ id: stackAssets[3].id })]));
});
it('should remove stack children', async () => {
const { status } = await request(app)
.put('/asset')
.set('Authorization', `Bearer ${stackUser.accessToken}`)
.send({ removeParent: true, ids: [stackAssets[1].id] });
expect(status).toBe(204);
const asset = await getAssetInfo({ id: stackAssets[0].id }, { headers: asBearerAuth(stackUser.accessToken) });
expect(asset.stack).not.toBeUndefined();
expect(asset.stack).toEqual(
expect.arrayContaining([
expect.objectContaining({ id: stackAssets[2].id }),
expect.objectContaining({ id: stackAssets[3].id }),
]),
);
});
it('should remove all stack children', async () => {
const { status } = await request(app)
.put('/asset')
.set('Authorization', `Bearer ${stackUser.accessToken}`)
.send({ removeParent: true, ids: [stackAssets[2].id, stackAssets[3].id] });
expect(status).toBe(204);
const asset = await getAssetInfo({ id: stackAssets[0].id }, { headers: asBearerAuth(stackUser.accessToken) });
expect(asset.stack).toBeUndefined();
});
it('should merge stack children', async () => {
// create stack after previous test removed stack children
await updateAssets(
{ assetBulkUpdateDto: { stackParentId: stackAssets[0].id, ids: [stackAssets[1].id, stackAssets[2].id] } },
{ headers: asBearerAuth(stackUser.accessToken) },
);
const { status } = await request(app)
.put('/asset')
.set('Authorization', `Bearer ${stackUser.accessToken}`)
.send({ stackParentId: stackAssets[3].id, ids: [stackAssets[0].id] });
expect(status).toBe(204);
const asset = await getAssetInfo({ id: stackAssets[3].id }, { headers: asBearerAuth(stackUser.accessToken) });
expect(asset.stack).not.toBeUndefined();
expect(asset.stack).toEqual(
expect.arrayContaining([
expect.objectContaining({ id: stackAssets[0].id }),
expect.objectContaining({ id: stackAssets[1].id }),
expect.objectContaining({ id: stackAssets[2].id }),
]),
);
});
});
describe('PUT /asset/stack/parent', () => {
it('should require authentication', async () => {
const { status, body } = await request(app).put('/asset/stack/parent');
expect(status).toBe(401);
expect(body).toEqual(errorDto.unauthorized);
});
it('should require a valid id', async () => {
const { status, body } = await request(app)
.put('/asset/stack/parent')
.set('Authorization', `Bearer ${user1.accessToken}`)
.send({ oldParentId: uuidDto.invalid, newParentId: uuidDto.invalid });
expect(status).toBe(400);
expect(body).toEqual(errorDto.badRequest());
});
it('should require access', async () => {
const { status, body } = await request(app)
.put('/asset/stack/parent')
.set('Authorization', `Bearer ${user1.accessToken}`)
.send({ oldParentId: stackAssets[3].id, newParentId: stackAssets[0].id });
expect(status).toBe(400);
expect(body).toEqual(errorDto.noPermission);
});
it('should make old parent child of new parent', async () => {
const { status } = await request(app)
.put('/asset/stack/parent')
.set('Authorization', `Bearer ${stackUser.accessToken}`)
.send({ oldParentId: stackAssets[3].id, newParentId: stackAssets[0].id });
expect(status).toBe(200);
const asset = await getAssetInfo({ id: stackAssets[0].id }, { headers: asBearerAuth(stackUser.accessToken) });
// new parent
expect(asset.stack).not.toBeUndefined();
expect(asset.stack).toEqual(
expect.arrayContaining([
expect.objectContaining({ id: stackAssets[1].id }),
expect.objectContaining({ id: stackAssets[2].id }),
expect.objectContaining({ id: stackAssets[3].id }),
]),
);
});
});
describe('POST /asset/upload', () => { describe('POST /asset/upload', () => {
beforeAll(setupTests, 30_000);
it('should require authentication', async () => { it('should require authentication', async () => {
const { status, body } = await request(app).post(`/asset/upload`); const { status, body } = await request(app).post(`/asset/upload`);
expect(body).toEqual(errorDto.unauthorized); expect(body).toEqual(errorDto.unauthorized);
expect(status).toBe(401); expect(status).toBe(401);
}); });
const invalid = [ it.each([
{ should: 'require `deviceAssetId`', dto: { ...makeUploadDto({ omit: 'deviceAssetId' }) } }, { should: 'require `deviceAssetId`', dto: { ...makeUploadDto({ omit: 'deviceAssetId' }) } },
{ should: 'require `deviceId`', dto: { ...makeUploadDto({ omit: 'deviceId' }) } }, { should: 'require `deviceId`', dto: { ...makeUploadDto({ omit: 'deviceId' }) } },
{ should: 'require `fileCreatedAt`', dto: { ...makeUploadDto({ omit: 'fileCreatedAt' }) } }, { should: 'require `fileCreatedAt`', dto: { ...makeUploadDto({ omit: 'fileCreatedAt' }) } },
@@ -554,21 +900,17 @@ describe('/asset', () => {
{ should: 'throw if `isFavorite` is not a boolean', dto: { ...makeUploadDto(), isFavorite: 'not-a-boolean' } }, { should: 'throw if `isFavorite` is not a boolean', dto: { ...makeUploadDto(), isFavorite: 'not-a-boolean' } },
{ should: 'throw if `isVisible` is not a boolean', dto: { ...makeUploadDto(), isVisible: 'not-a-boolean' } }, { should: 'throw if `isVisible` is not a boolean', dto: { ...makeUploadDto(), isVisible: 'not-a-boolean' } },
{ should: 'throw if `isArchived` is not a boolean', dto: { ...makeUploadDto(), isArchived: 'not-a-boolean' } }, { should: 'throw if `isArchived` is not a boolean', dto: { ...makeUploadDto(), isArchived: 'not-a-boolean' } },
]; ])('should $should', async ({ dto }) => {
const { status, body } = await request(app)
.post('/asset/upload')
.set('Authorization', `Bearer ${user1.accessToken}`)
.attach('assetData', makeRandomImage(), 'example.png')
.field(dto);
expect(status).toBe(400);
expect(body).toEqual(errorDto.badRequest());
});
for (const { should, dto } of invalid) { it.each([
it(`should ${should}`, async () => {
const { status, body } = await request(app)
.post('/asset/upload')
.set('Authorization', `Bearer ${user1.accessToken}`)
.attach('assetData', makeRandomImage(), 'example.png')
.field(dto);
expect(status).toBe(400);
expect(body).toEqual(errorDto.badRequest());
});
}
const tests = [
{ {
input: 'formats/avif/8bit-sRGB.avif', input: 'formats/avif/8bit-sRGB.avif',
expected: { expected: {
@@ -784,26 +1126,22 @@ describe('/asset', () => {
}, },
}, },
}, },
]; ])(`should upload and generate a thumbnail for $input`, async ({ input, expected }) => {
const filepath = join(testAssetDir, input);
for (const { input, expected } of tests) { const { id, duplicate } = await utils.createAsset(admin.accessToken, {
it(`should upload and generate a thumbnail for ${input}`, async () => { assetData: { bytes: await readFile(filepath), filename: basename(filepath) },
const filepath = join(testAssetDir, input);
const { id, duplicate } = await utils.createAsset(admin.accessToken, {
assetData: { bytes: await readFile(filepath), filename: basename(filepath) },
});
expect(duplicate).toBe(false);
await utils.waitForWebsocketEvent({ event: 'assetUpload', id: id });
const asset = await utils.getAssetInfo(admin.accessToken, id);
expect(asset.exifInfo).toBeDefined();
expect(asset.exifInfo).toMatchObject(expected.exifInfo);
expect(asset).toMatchObject(expected);
}); });
}
expect(duplicate).toBe(false);
await utils.waitForWebsocketEvent({ event: 'assetUpload', id: id });
const asset = await utils.getAssetInfo(admin.accessToken, id);
expect(asset.exifInfo).toBeDefined();
expect(asset.exifInfo).toMatchObject(expected.exifInfo);
expect(asset).toMatchObject(expected);
});
it('should handle a duplicate', async () => { it('should handle a duplicate', async () => {
const filepath = 'formats/jpeg/el_torcal_rocks.jpeg'; const filepath = 'formats/jpeg/el_torcal_rocks.jpeg';
@@ -830,7 +1168,7 @@ describe('/asset', () => {
expect(body).toEqual({ id: expect.any(String), duplicate: false }); expect(body).toEqual({ id: expect.any(String), duplicate: false });
expect(status).toBe(201); expect(status).toBe(201);
const { body: user } = await request(app).get('/user/me').set('Authorization', `Bearer ${quotaUser.accessToken}`); const user = await getMyUser({ headers: asBearerAuth(quotaUser.accessToken) });
expect(user).toEqual(expect.objectContaining({ quotaUsageInBytes: 70 })); expect(user).toEqual(expect.objectContaining({ quotaUsageInBytes: 70 }));
}); });
@@ -854,7 +1192,7 @@ describe('/asset', () => {
// This ensures that immich+exiftool are extracting the videos the same way Samsung does. // This ensures that immich+exiftool are extracting the videos the same way Samsung does.
// DO NOT assume immich+exiftool are doing things correctly and just copy whatever hash it gives // DO NOT assume immich+exiftool are doing things correctly and just copy whatever hash it gives
// into the test here. // into the test here.
const motionTests = [ it.each([
{ {
filepath: 'formats/motionphoto/Samsung One UI 5.jpg', filepath: 'formats/motionphoto/Samsung One UI 5.jpg',
checksum: 'fr14niqCq6N20HB8rJYEvpsUVtI=', checksum: 'fr14niqCq6N20HB8rJYEvpsUVtI=',
@@ -867,329 +1205,23 @@ describe('/asset', () => {
filepath: 'formats/motionphoto/Samsung One UI 6.heic', filepath: 'formats/motionphoto/Samsung One UI 6.heic',
checksum: '/ejgzywvgvzvVhUYVfvkLzFBAF0=', checksum: '/ejgzywvgvzvVhUYVfvkLzFBAF0=',
}, },
]; ])(`should extract motionphoto video from $filepath`, async ({ filepath, checksum }) => {
const response = await utils.createAsset(admin.accessToken, {
for (const { filepath, checksum } of motionTests) { assetData: {
it(`should extract motionphoto video from ${filepath}`, async () => { bytes: await readFile(join(testAssetDir, filepath)),
const response = await utils.createAsset(admin.accessToken, { filename: basename(filepath),
assetData: { },
bytes: await readFile(join(testAssetDir, filepath)),
filename: basename(filepath),
},
});
await utils.waitForWebsocketEvent({ event: 'assetUpload', id: response.id });
expect(response.duplicate).toBe(false);
const asset = await utils.getAssetInfo(admin.accessToken, response.id);
expect(asset.livePhotoVideoId).toBeDefined();
const video = await utils.getAssetInfo(admin.accessToken, asset.livePhotoVideoId as string);
expect(video.checksum).toStrictEqual(checksum);
});
}
});
describe('GET /asset/thumbnail/:id', () => {
it('should require authentication', async () => {
const { status, body } = await request(app).get(`/asset/thumbnail/${locationAsset.id}`);
expect(status).toBe(401);
expect(body).toEqual(errorDto.unauthorized);
});
it('should not include gps data for webp thumbnails', async () => {
await utils.waitForWebsocketEvent({
event: 'assetUpload',
id: locationAsset.id,
}); });
const { status, body, type } = await request(app) await utils.waitForWebsocketEvent({ event: 'assetUpload', id: response.id });
.get(`/asset/thumbnail/${locationAsset.id}?format=WEBP`)
.set('Authorization', `Bearer ${admin.accessToken}`);
expect(status).toBe(200); expect(response.duplicate).toBe(false);
expect(body).toBeDefined();
expect(type).toBe('image/webp');
const exifData = await readTags(body, 'thumbnail.webp'); const asset = await utils.getAssetInfo(admin.accessToken, response.id);
expect(exifData).not.toHaveProperty('GPSLongitude'); expect(asset.livePhotoVideoId).toBeDefined();
expect(exifData).not.toHaveProperty('GPSLatitude');
});
it('should not include gps data for jpeg thumbnails', async () => { const video = await utils.getAssetInfo(admin.accessToken, asset.livePhotoVideoId as string);
const { status, body, type } = await request(app) expect(video.checksum).toStrictEqual(checksum);
.get(`/asset/thumbnail/${locationAsset.id}?format=JPEG`)
.set('Authorization', `Bearer ${admin.accessToken}`);
expect(status).toBe(200);
expect(body).toBeDefined();
expect(type).toBe('image/jpeg');
const exifData = await readTags(body, 'thumbnail.jpg');
expect(exifData).not.toHaveProperty('GPSLongitude');
expect(exifData).not.toHaveProperty('GPSLatitude');
});
});
describe('GET /asset/file/:id', () => {
it('should require authentication', async () => {
const { status, body } = await request(app).get(`/asset/thumbnail/${locationAsset.id}`);
expect(status).toBe(401);
expect(body).toEqual(errorDto.unauthorized);
});
it('should download the original', async () => {
const { status, body, type } = await request(app)
.get(`/asset/file/${locationAsset.id}`)
.set('Authorization', `Bearer ${admin.accessToken}`);
expect(status).toBe(200);
expect(body).toBeDefined();
expect(type).toBe('image/jpeg');
const asset = await utils.getAssetInfo(admin.accessToken, locationAsset.id);
const original = await readFile(locationAssetFilepath);
const originalChecksum = utils.sha1(original);
const downloadChecksum = utils.sha1(body);
expect(originalChecksum).toBe(downloadChecksum);
expect(downloadChecksum).toBe(asset.checksum);
});
});
describe('GET /asset/map-marker', () => {
it('should require authentication', async () => {
const { status, body } = await request(app).get('/asset/map-marker');
expect(status).toBe(401);
expect(body).toEqual(errorDto.unauthorized);
});
// TODO archive one of these assets
it('should get map markers for all non-archived assets', async () => {
const { status, body } = await request(app)
.get('/asset/map-marker')
.query({ isArchived: false })
.set('Authorization', `Bearer ${admin.accessToken}`);
expect(status).toBe(200);
expect(body).toHaveLength(2);
expect(body).toEqual([
{
city: 'Palisade',
country: 'United States of America',
id: expect.any(String),
lat: expect.closeTo(39.115),
lon: expect.closeTo(-108.400_968),
state: 'Colorado',
},
{
city: 'Ralston',
country: 'United States of America',
id: expect.any(String),
lat: expect.closeTo(41.2203),
lon: expect.closeTo(-96.071_625),
state: 'Nebraska',
},
]);
});
// TODO archive one of these assets
it('should get all map markers', async () => {
const { status, body } = await request(app)
.get('/asset/map-marker')
.set('Authorization', `Bearer ${admin.accessToken}`);
expect(status).toBe(200);
expect(body).toEqual([
{
city: 'Palisade',
country: 'United States of America',
id: expect.any(String),
lat: expect.closeTo(39.115),
lon: expect.closeTo(-108.400_968),
state: 'Colorado',
},
{
city: 'Ralston',
country: 'United States of America',
id: expect.any(String),
lat: expect.closeTo(41.2203),
lon: expect.closeTo(-96.071_625),
state: 'Nebraska',
},
]);
});
});
describe('GET /asset', () => {
it('should return stack data', async () => {
const { status, body } = await request(app).get('/asset').set('Authorization', `Bearer ${stackUser.accessToken}`);
const stack = body.find((asset: AssetResponseDto) => asset.id === stackAssets[0].id);
expect(status).toBe(200);
expect(stack).toEqual(
expect.objectContaining({
stackCount: 3,
stack:
// Response includes children at the root level
expect.arrayContaining([
expect.objectContaining({ id: stackAssets[1].id }),
expect.objectContaining({ id: stackAssets[2].id }),
]),
}),
);
});
});
describe('PUT /asset', () => {
it('should require authentication', async () => {
const { status, body } = await request(app).put('/asset');
expect(status).toBe(401);
expect(body).toEqual(errorDto.unauthorized);
});
it('should require a valid parent id', async () => {
const { status, body } = await request(app)
.put('/asset')
.set('Authorization', `Bearer ${user1.accessToken}`)
.send({ stackParentId: uuidDto.invalid, ids: [stackAssets[0].id] });
expect(status).toBe(400);
expect(body).toEqual(errorDto.badRequest(['stackParentId must be a UUID']));
});
it('should require access to the parent', async () => {
const { status, body } = await request(app)
.put('/asset')
.set('Authorization', `Bearer ${user1.accessToken}`)
.send({ stackParentId: stackAssets[3].id, ids: [user1Assets[0].id] });
expect(status).toBe(400);
expect(body).toEqual(errorDto.noPermission);
});
it('should add stack children', async () => {
const { status } = await request(app)
.put('/asset')
.set('Authorization', `Bearer ${stackUser.accessToken}`)
.send({ stackParentId: stackAssets[0].id, ids: [stackAssets[3].id] });
expect(status).toBe(204);
const asset = await getAssetInfo({ id: stackAssets[0].id }, { headers: asBearerAuth(stackUser.accessToken) });
expect(asset.stack).not.toBeUndefined();
expect(asset.stack).toEqual(expect.arrayContaining([expect.objectContaining({ id: stackAssets[3].id })]));
});
it('should remove stack children', async () => {
const { status } = await request(app)
.put('/asset')
.set('Authorization', `Bearer ${stackUser.accessToken}`)
.send({ removeParent: true, ids: [stackAssets[1].id] });
expect(status).toBe(204);
const asset = await getAssetInfo({ id: stackAssets[0].id }, { headers: asBearerAuth(stackUser.accessToken) });
expect(asset.stack).not.toBeUndefined();
expect(asset.stack).toEqual(
expect.arrayContaining([
expect.objectContaining({ id: stackAssets[2].id }),
expect.objectContaining({ id: stackAssets[3].id }),
]),
);
});
it('should remove all stack children', async () => {
const { status } = await request(app)
.put('/asset')
.set('Authorization', `Bearer ${stackUser.accessToken}`)
.send({ removeParent: true, ids: [stackAssets[2].id, stackAssets[3].id] });
expect(status).toBe(204);
const asset = await getAssetInfo({ id: stackAssets[0].id }, { headers: asBearerAuth(stackUser.accessToken) });
expect(asset.stack).toBeUndefined();
});
it('should merge stack children', async () => {
// create stack after previous test removed stack children
await updateAssets(
{ assetBulkUpdateDto: { stackParentId: stackAssets[0].id, ids: [stackAssets[1].id, stackAssets[2].id] } },
{ headers: asBearerAuth(stackUser.accessToken) },
);
const { status } = await request(app)
.put('/asset')
.set('Authorization', `Bearer ${stackUser.accessToken}`)
.send({ stackParentId: stackAssets[3].id, ids: [stackAssets[0].id] });
expect(status).toBe(204);
const asset = await getAssetInfo({ id: stackAssets[3].id }, { headers: asBearerAuth(stackUser.accessToken) });
expect(asset.stack).not.toBeUndefined();
expect(asset.stack).toEqual(
expect.arrayContaining([
expect.objectContaining({ id: stackAssets[0].id }),
expect.objectContaining({ id: stackAssets[1].id }),
expect.objectContaining({ id: stackAssets[2].id }),
]),
);
});
});
describe('PUT /asset/stack/parent', () => {
it('should require authentication', async () => {
const { status, body } = await request(app).put('/asset/stack/parent');
expect(status).toBe(401);
expect(body).toEqual(errorDto.unauthorized);
});
it('should require a valid id', async () => {
const { status, body } = await request(app)
.put('/asset/stack/parent')
.set('Authorization', `Bearer ${user1.accessToken}`)
.send({ oldParentId: uuidDto.invalid, newParentId: uuidDto.invalid });
expect(status).toBe(400);
expect(body).toEqual(errorDto.badRequest());
});
it('should require access', async () => {
const { status, body } = await request(app)
.put('/asset/stack/parent')
.set('Authorization', `Bearer ${user1.accessToken}`)
.send({ oldParentId: stackAssets[3].id, newParentId: stackAssets[0].id });
expect(status).toBe(400);
expect(body).toEqual(errorDto.noPermission);
});
it('should make old parent child of new parent', async () => {
const { status } = await request(app)
.put('/asset/stack/parent')
.set('Authorization', `Bearer ${stackUser.accessToken}`)
.send({ oldParentId: stackAssets[3].id, newParentId: stackAssets[0].id });
expect(status).toBe(200);
const asset = await getAssetInfo({ id: stackAssets[0].id }, { headers: asBearerAuth(stackUser.accessToken) });
// new parent
expect(asset.stack).not.toBeUndefined();
expect(asset.stack).toEqual(
expect.arrayContaining([
expect.objectContaining({ id: stackAssets[1].id }),
expect.objectContaining({ id: stackAssets[2].id }),
expect.objectContaining({ id: stackAssets[3].id }),
]),
);
}); });
}); });
}); });

View File

@@ -2,7 +2,7 @@ import { deleteAssets, getAuditFiles, updateAsset, type LoginResponseDto } from
import { asBearerAuth, utils } from 'src/utils'; import { asBearerAuth, utils } from 'src/utils';
import { beforeAll, describe, expect, it } from 'vitest'; import { beforeAll, describe, expect, it } from 'vitest';
describe('/audit', () => { describe('/audits', () => {
let admin: LoginResponseDto; let admin: LoginResponseDto;
beforeAll(async () => { beforeAll(async () => {

View File

@@ -11,7 +11,7 @@ import { afterAll, beforeAll, beforeEach, describe, expect, it } from 'vitest';
const scan = async (accessToken: string, id: string, dto: ScanLibraryDto = {}) => const scan = async (accessToken: string, id: string, dto: ScanLibraryDto = {}) =>
scanLibrary({ id, scanLibraryDto: dto }, { headers: asBearerAuth(accessToken) }); scanLibrary({ id, scanLibraryDto: dto }, { headers: asBearerAuth(accessToken) });
describe('/library', () => { describe('/libraries', () => {
let admin: LoginResponseDto; let admin: LoginResponseDto;
let user: LoginResponseDto; let user: LoginResponseDto;
let library: LibraryResponseDto; let library: LibraryResponseDto;
@@ -37,24 +37,24 @@ describe('/library', () => {
utils.resetEvents(); utils.resetEvents();
}); });
describe('GET /library', () => { describe('GET /libraries', () => {
it('should require authentication', async () => { it('should require authentication', async () => {
const { status, body } = await request(app).get('/library'); const { status, body } = await request(app).get('/libraries');
expect(status).toBe(401); expect(status).toBe(401);
expect(body).toEqual(errorDto.unauthorized); expect(body).toEqual(errorDto.unauthorized);
}); });
}); });
describe('POST /library', () => { describe('POST /libraries', () => {
it('should require authentication', async () => { it('should require authentication', async () => {
const { status, body } = await request(app).post('/library').send({}); const { status, body } = await request(app).post('/libraries').send({});
expect(status).toBe(401); expect(status).toBe(401);
expect(body).toEqual(errorDto.unauthorized); expect(body).toEqual(errorDto.unauthorized);
}); });
it('should require admin authentication', async () => { it('should require admin authentication', async () => {
const { status, body } = await request(app) const { status, body } = await request(app)
.post('/library') .post('/libraries')
.set('Authorization', `Bearer ${user.accessToken}`) .set('Authorization', `Bearer ${user.accessToken}`)
.send({ ownerId: admin.userId }); .send({ ownerId: admin.userId });
@@ -64,7 +64,7 @@ describe('/library', () => {
it('should create an external library with defaults', async () => { it('should create an external library with defaults', async () => {
const { status, body } = await request(app) const { status, body } = await request(app)
.post('/library') .post('/libraries')
.set('Authorization', `Bearer ${admin.accessToken}`) .set('Authorization', `Bearer ${admin.accessToken}`)
.send({ ownerId: admin.userId }); .send({ ownerId: admin.userId });
@@ -83,7 +83,7 @@ describe('/library', () => {
it('should create an external library with options', async () => { it('should create an external library with options', async () => {
const { status, body } = await request(app) const { status, body } = await request(app)
.post('/library') .post('/libraries')
.set('Authorization', `Bearer ${admin.accessToken}`) .set('Authorization', `Bearer ${admin.accessToken}`)
.send({ .send({
ownerId: admin.userId, ownerId: admin.userId,
@@ -103,7 +103,7 @@ describe('/library', () => {
it('should not create an external library with duplicate import paths', async () => { it('should not create an external library with duplicate import paths', async () => {
const { status, body } = await request(app) const { status, body } = await request(app)
.post('/library') .post('/libraries')
.set('Authorization', `Bearer ${admin.accessToken}`) .set('Authorization', `Bearer ${admin.accessToken}`)
.send({ .send({
ownerId: admin.userId, ownerId: admin.userId,
@@ -118,7 +118,7 @@ describe('/library', () => {
it('should not create an external library with duplicate exclusion patterns', async () => { it('should not create an external library with duplicate exclusion patterns', async () => {
const { status, body } = await request(app) const { status, body } = await request(app)
.post('/library') .post('/libraries')
.set('Authorization', `Bearer ${admin.accessToken}`) .set('Authorization', `Bearer ${admin.accessToken}`)
.send({ .send({
ownerId: admin.userId, ownerId: admin.userId,
@@ -132,16 +132,16 @@ describe('/library', () => {
}); });
}); });
describe('PUT /library/:id', () => { describe('PUT /libraries/:id', () => {
it('should require authentication', async () => { it('should require authentication', async () => {
const { status, body } = await request(app).put(`/library/${uuidDto.notFound}`).send({}); const { status, body } = await request(app).put(`/libraries/${uuidDto.notFound}`).send({});
expect(status).toBe(401); expect(status).toBe(401);
expect(body).toEqual(errorDto.unauthorized); expect(body).toEqual(errorDto.unauthorized);
}); });
it('should change the library name', async () => { it('should change the library name', async () => {
const { status, body } = await request(app) const { status, body } = await request(app)
.put(`/library/${library.id}`) .put(`/libraries/${library.id}`)
.set('Authorization', `Bearer ${admin.accessToken}`) .set('Authorization', `Bearer ${admin.accessToken}`)
.send({ name: 'New Library Name' }); .send({ name: 'New Library Name' });
@@ -155,7 +155,7 @@ describe('/library', () => {
it('should not set an empty name', async () => { it('should not set an empty name', async () => {
const { status, body } = await request(app) const { status, body } = await request(app)
.put(`/library/${library.id}`) .put(`/libraries/${library.id}`)
.set('Authorization', `Bearer ${admin.accessToken}`) .set('Authorization', `Bearer ${admin.accessToken}`)
.send({ name: '' }); .send({ name: '' });
@@ -165,7 +165,7 @@ describe('/library', () => {
it('should change the import paths', async () => { it('should change the import paths', async () => {
const { status, body } = await request(app) const { status, body } = await request(app)
.put(`/library/${library.id}`) .put(`/libraries/${library.id}`)
.set('Authorization', `Bearer ${admin.accessToken}`) .set('Authorization', `Bearer ${admin.accessToken}`)
.send({ importPaths: [testAssetDirInternal] }); .send({ importPaths: [testAssetDirInternal] });
@@ -179,7 +179,7 @@ describe('/library', () => {
it('should reject an empty import path', async () => { it('should reject an empty import path', async () => {
const { status, body } = await request(app) const { status, body } = await request(app)
.put(`/library/${library.id}`) .put(`/libraries/${library.id}`)
.set('Authorization', `Bearer ${admin.accessToken}`) .set('Authorization', `Bearer ${admin.accessToken}`)
.send({ importPaths: [''] }); .send({ importPaths: [''] });
@@ -189,7 +189,7 @@ describe('/library', () => {
it('should reject duplicate import paths', async () => { it('should reject duplicate import paths', async () => {
const { status, body } = await request(app) const { status, body } = await request(app)
.put(`/library/${library.id}`) .put(`/libraries/${library.id}`)
.set('Authorization', `Bearer ${admin.accessToken}`) .set('Authorization', `Bearer ${admin.accessToken}`)
.send({ importPaths: ['/path', '/path'] }); .send({ importPaths: ['/path', '/path'] });
@@ -199,7 +199,7 @@ describe('/library', () => {
it('should change the exclusion pattern', async () => { it('should change the exclusion pattern', async () => {
const { status, body } = await request(app) const { status, body } = await request(app)
.put(`/library/${library.id}`) .put(`/libraries/${library.id}`)
.set('Authorization', `Bearer ${admin.accessToken}`) .set('Authorization', `Bearer ${admin.accessToken}`)
.send({ exclusionPatterns: ['**/Raw/**'] }); .send({ exclusionPatterns: ['**/Raw/**'] });
@@ -213,7 +213,7 @@ describe('/library', () => {
it('should reject duplicate exclusion patterns', async () => { it('should reject duplicate exclusion patterns', async () => {
const { status, body } = await request(app) const { status, body } = await request(app)
.put(`/library/${library.id}`) .put(`/libraries/${library.id}`)
.set('Authorization', `Bearer ${admin.accessToken}`) .set('Authorization', `Bearer ${admin.accessToken}`)
.send({ exclusionPatterns: ['**/*.jpg', '**/*.jpg'] }); .send({ exclusionPatterns: ['**/*.jpg', '**/*.jpg'] });
@@ -223,7 +223,7 @@ describe('/library', () => {
it('should reject an empty exclusion pattern', async () => { it('should reject an empty exclusion pattern', async () => {
const { status, body } = await request(app) const { status, body } = await request(app)
.put(`/library/${library.id}`) .put(`/libraries/${library.id}`)
.set('Authorization', `Bearer ${admin.accessToken}`) .set('Authorization', `Bearer ${admin.accessToken}`)
.send({ exclusionPatterns: [''] }); .send({ exclusionPatterns: [''] });
@@ -232,9 +232,9 @@ describe('/library', () => {
}); });
}); });
describe('GET /library/:id', () => { describe('GET /libraries/:id', () => {
it('should require authentication', async () => { it('should require authentication', async () => {
const { status, body } = await request(app).get(`/library/${uuidDto.notFound}`); const { status, body } = await request(app).get(`/libraries/${uuidDto.notFound}`);
expect(status).toBe(401); expect(status).toBe(401);
expect(body).toEqual(errorDto.unauthorized); expect(body).toEqual(errorDto.unauthorized);
@@ -242,7 +242,7 @@ describe('/library', () => {
it('should require admin access', async () => { it('should require admin access', async () => {
const { status, body } = await request(app) const { status, body } = await request(app)
.get(`/library/${uuidDto.notFound}`) .get(`/libraries/${uuidDto.notFound}`)
.set('Authorization', `Bearer ${user.accessToken}`); .set('Authorization', `Bearer ${user.accessToken}`);
expect(status).toBe(403); expect(status).toBe(403);
expect(body).toEqual(errorDto.forbidden); expect(body).toEqual(errorDto.forbidden);
@@ -252,7 +252,7 @@ describe('/library', () => {
const library = await utils.createLibrary(admin.accessToken, { ownerId: admin.userId }); const library = await utils.createLibrary(admin.accessToken, { ownerId: admin.userId });
const { status, body } = await request(app) const { status, body } = await request(app)
.get(`/library/${library.id}`) .get(`/libraries/${library.id}`)
.set('Authorization', `Bearer ${admin.accessToken}`); .set('Authorization', `Bearer ${admin.accessToken}`);
expect(status).toBe(200); expect(status).toBe(200);
@@ -269,18 +269,18 @@ describe('/library', () => {
}); });
}); });
describe('GET /library/:id/statistics', () => { describe('GET /libraries/:id/statistics', () => {
it('should require authentication', async () => { it('should require authentication', async () => {
const { status, body } = await request(app).get(`/library/${uuidDto.notFound}/statistics`); const { status, body } = await request(app).get(`/libraries/${uuidDto.notFound}/statistics`);
expect(status).toBe(401); expect(status).toBe(401);
expect(body).toEqual(errorDto.unauthorized); expect(body).toEqual(errorDto.unauthorized);
}); });
}); });
describe('POST /library/:id/scan', () => { describe('POST /libraries/:id/scan', () => {
it('should require authentication', async () => { it('should require authentication', async () => {
const { status, body } = await request(app).post(`/library/${uuidDto.notFound}/scan`).send({}); const { status, body } = await request(app).post(`/libraries/${uuidDto.notFound}/scan`).send({});
expect(status).toBe(401); expect(status).toBe(401);
expect(body).toEqual(errorDto.unauthorized); expect(body).toEqual(errorDto.unauthorized);
@@ -496,9 +496,9 @@ describe('/library', () => {
}); });
}); });
describe('POST /library/:id/removeOffline', () => { describe('POST /libraries/:id/removeOffline', () => {
it('should require authentication', async () => { it('should require authentication', async () => {
const { status, body } = await request(app).post(`/library/${uuidDto.notFound}/removeOffline`).send({}); const { status, body } = await request(app).post(`/libraries/${uuidDto.notFound}/removeOffline`).send({});
expect(status).toBe(401); expect(status).toBe(401);
expect(body).toEqual(errorDto.unauthorized); expect(body).toEqual(errorDto.unauthorized);
@@ -532,7 +532,7 @@ describe('/library', () => {
expect(offlineAssets.count).toBe(1); expect(offlineAssets.count).toBe(1);
const { status } = await request(app) const { status } = await request(app)
.post(`/library/${library.id}/removeOffline`) .post(`/libraries/${library.id}/removeOffline`)
.set('Authorization', `Bearer ${admin.accessToken}`) .set('Authorization', `Bearer ${admin.accessToken}`)
.send(); .send();
expect(status).toBe(204); expect(status).toBe(204);
@@ -557,7 +557,7 @@ describe('/library', () => {
expect(assetsBefore.count).toBeGreaterThan(1); expect(assetsBefore.count).toBeGreaterThan(1);
const { status } = await request(app) const { status } = await request(app)
.post(`/library/${library.id}/removeOffline`) .post(`/libraries/${library.id}/removeOffline`)
.set('Authorization', `Bearer ${admin.accessToken}`) .set('Authorization', `Bearer ${admin.accessToken}`)
.send(); .send();
expect(status).toBe(204); expect(status).toBe(204);
@@ -569,9 +569,9 @@ describe('/library', () => {
}); });
}); });
describe('POST /library/:id/validate', () => { describe('POST /libraries/:id/validate', () => {
it('should require authentication', async () => { it('should require authentication', async () => {
const { status, body } = await request(app).post(`/library/${uuidDto.notFound}/validate`).send({}); const { status, body } = await request(app).post(`/libraries/${uuidDto.notFound}/validate`).send({});
expect(status).toBe(401); expect(status).toBe(401);
expect(body).toEqual(errorDto.unauthorized); expect(body).toEqual(errorDto.unauthorized);
@@ -617,9 +617,9 @@ describe('/library', () => {
}); });
}); });
describe('DELETE /library/:id', () => { describe('DELETE /libraries/:id', () => {
it('should require authentication', async () => { it('should require authentication', async () => {
const { status, body } = await request(app).delete(`/library/${uuidDto.notFound}`); const { status, body } = await request(app).delete(`/libraries/${uuidDto.notFound}`);
expect(status).toBe(401); expect(status).toBe(401);
expect(body).toEqual(errorDto.unauthorized); expect(body).toEqual(errorDto.unauthorized);
@@ -629,7 +629,7 @@ describe('/library', () => {
const library = await utils.createLibrary(admin.accessToken, { ownerId: admin.userId }); const library = await utils.createLibrary(admin.accessToken, { ownerId: admin.userId });
const { status, body } = await request(app) const { status, body } = await request(app)
.delete(`/library/${library.id}`) .delete(`/libraries/${library.id}`)
.set('Authorization', `Bearer ${admin.accessToken}`); .set('Authorization', `Bearer ${admin.accessToken}`);
expect(status).toBe(204); expect(status).toBe(204);
@@ -655,7 +655,7 @@ describe('/library', () => {
await utils.waitForWebsocketEvent({ event: 'assetUpload', total: 2 }); await utils.waitForWebsocketEvent({ event: 'assetUpload', total: 2 });
const { status, body } = await request(app) const { status, body } = await request(app)
.delete(`/library/${library.id}`) .delete(`/libraries/${library.id}`)
.set('Authorization', `Bearer ${admin.accessToken}`); .set('Authorization', `Bearer ${admin.accessToken}`);
expect(status).toBe(204); expect(status).toBe(204);

View File

@@ -5,7 +5,7 @@ import { app, asBearerAuth, utils } from 'src/utils';
import request from 'supertest'; import request from 'supertest';
import { beforeAll, describe, expect, it } from 'vitest'; import { beforeAll, describe, expect, it } from 'vitest';
describe('/partner', () => { describe('/partners', () => {
let admin: LoginResponseDto; let admin: LoginResponseDto;
let user1: LoginResponseDto; let user1: LoginResponseDto;
let user2: LoginResponseDto; let user2: LoginResponseDto;
@@ -28,9 +28,9 @@ describe('/partner', () => {
]); ]);
}); });
describe('GET /partner', () => { describe('GET /partners', () => {
it('should require authentication', async () => { it('should require authentication', async () => {
const { status, body } = await request(app).get('/partner'); const { status, body } = await request(app).get('/partners');
expect(status).toBe(401); expect(status).toBe(401);
expect(body).toEqual(errorDto.unauthorized); expect(body).toEqual(errorDto.unauthorized);
@@ -38,7 +38,7 @@ describe('/partner', () => {
it('should get all partners shared by user', async () => { it('should get all partners shared by user', async () => {
const { status, body } = await request(app) const { status, body } = await request(app)
.get('/partner') .get('/partners')
.set('Authorization', `Bearer ${user1.accessToken}`) .set('Authorization', `Bearer ${user1.accessToken}`)
.query({ direction: 'shared-by' }); .query({ direction: 'shared-by' });
@@ -48,7 +48,7 @@ describe('/partner', () => {
it('should get all partners that share with user', async () => { it('should get all partners that share with user', async () => {
const { status, body } = await request(app) const { status, body } = await request(app)
.get('/partner') .get('/partners')
.set('Authorization', `Bearer ${user1.accessToken}`) .set('Authorization', `Bearer ${user1.accessToken}`)
.query({ direction: 'shared-with' }); .query({ direction: 'shared-with' });
@@ -57,9 +57,9 @@ describe('/partner', () => {
}); });
}); });
describe('POST /partner/:id', () => { describe('POST /partners/:id', () => {
it('should require authentication', async () => { it('should require authentication', async () => {
const { status, body } = await request(app).post(`/partner/${user3.userId}`); const { status, body } = await request(app).post(`/partners/${user3.userId}`);
expect(status).toBe(401); expect(status).toBe(401);
expect(body).toEqual(errorDto.unauthorized); expect(body).toEqual(errorDto.unauthorized);
@@ -67,7 +67,7 @@ describe('/partner', () => {
it('should share with new partner', async () => { it('should share with new partner', async () => {
const { status, body } = await request(app) const { status, body } = await request(app)
.post(`/partner/${user3.userId}`) .post(`/partners/${user3.userId}`)
.set('Authorization', `Bearer ${user1.accessToken}`); .set('Authorization', `Bearer ${user1.accessToken}`);
expect(status).toBe(201); expect(status).toBe(201);
@@ -76,7 +76,7 @@ describe('/partner', () => {
it('should not share with new partner if already sharing with this partner', async () => { it('should not share with new partner if already sharing with this partner', async () => {
const { status, body } = await request(app) const { status, body } = await request(app)
.post(`/partner/${user2.userId}`) .post(`/partners/${user2.userId}`)
.set('Authorization', `Bearer ${user1.accessToken}`); .set('Authorization', `Bearer ${user1.accessToken}`);
expect(status).toBe(400); expect(status).toBe(400);
@@ -84,9 +84,9 @@ describe('/partner', () => {
}); });
}); });
describe('PUT /partner/:id', () => { describe('PUT /partners/:id', () => {
it('should require authentication', async () => { it('should require authentication', async () => {
const { status, body } = await request(app).put(`/partner/${user2.userId}`); const { status, body } = await request(app).put(`/partners/${user2.userId}`);
expect(status).toBe(401); expect(status).toBe(401);
expect(body).toEqual(errorDto.unauthorized); expect(body).toEqual(errorDto.unauthorized);
@@ -94,7 +94,7 @@ describe('/partner', () => {
it('should update partner', async () => { it('should update partner', async () => {
const { status, body } = await request(app) const { status, body } = await request(app)
.put(`/partner/${user2.userId}`) .put(`/partners/${user2.userId}`)
.set('Authorization', `Bearer ${user1.accessToken}`) .set('Authorization', `Bearer ${user1.accessToken}`)
.send({ inTimeline: false }); .send({ inTimeline: false });
@@ -103,9 +103,9 @@ describe('/partner', () => {
}); });
}); });
describe('DELETE /partner/:id', () => { describe('DELETE /partners/:id', () => {
it('should require authentication', async () => { it('should require authentication', async () => {
const { status, body } = await request(app).delete(`/partner/${user3.userId}`); const { status, body } = await request(app).delete(`/partners/${user3.userId}`);
expect(status).toBe(401); expect(status).toBe(401);
expect(body).toEqual(errorDto.unauthorized); expect(body).toEqual(errorDto.unauthorized);
@@ -113,7 +113,7 @@ describe('/partner', () => {
it('should delete partner', async () => { it('should delete partner', async () => {
const { status } = await request(app) const { status } = await request(app)
.delete(`/partner/${user3.userId}`) .delete(`/partners/${user3.userId}`)
.set('Authorization', `Bearer ${user1.accessToken}`); .set('Authorization', `Bearer ${user1.accessToken}`);
expect(status).toBe(200); expect(status).toBe(200);
@@ -121,7 +121,7 @@ describe('/partner', () => {
it('should throw a bad request if partner not found', async () => { it('should throw a bad request if partner not found', async () => {
const { status, body } = await request(app) const { status, body } = await request(app)
.delete(`/partner/${user3.userId}`) .delete(`/partners/${user3.userId}`)
.set('Authorization', `Bearer ${user1.accessToken}`); .set('Authorization', `Bearer ${user1.accessToken}`);
expect(status).toBe(400); expect(status).toBe(400);

View File

@@ -12,7 +12,7 @@ const invalidBirthday = [
{ birthDate: new Date(9999, 0, 0).toISOString(), response: ['Birth date cannot be in the future'] }, { birthDate: new Date(9999, 0, 0).toISOString(), response: ['Birth date cannot be in the future'] },
]; ];
describe('/person', () => { describe('/people', () => {
let admin: LoginResponseDto; let admin: LoginResponseDto;
let visiblePerson: PersonResponseDto; let visiblePerson: PersonResponseDto;
let hiddenPerson: PersonResponseDto; let hiddenPerson: PersonResponseDto;
@@ -47,11 +47,11 @@ describe('/person', () => {
]); ]);
}); });
describe('GET /person', () => { describe('GET /people', () => {
beforeEach(async () => {}); beforeEach(async () => {});
it('should require authentication', async () => { it('should require authentication', async () => {
const { status, body } = await request(app).get('/person'); const { status, body } = await request(app).get('/people');
expect(status).toBe(401); expect(status).toBe(401);
expect(body).toEqual(errorDto.unauthorized); expect(body).toEqual(errorDto.unauthorized);
@@ -59,7 +59,7 @@ describe('/person', () => {
it('should return all people (including hidden)', async () => { it('should return all people (including hidden)', async () => {
const { status, body } = await request(app) const { status, body } = await request(app)
.get('/person') .get('/people')
.set('Authorization', `Bearer ${admin.accessToken}`) .set('Authorization', `Bearer ${admin.accessToken}`)
.query({ withHidden: true }); .query({ withHidden: true });
@@ -76,7 +76,7 @@ describe('/person', () => {
}); });
it('should return only visible people', async () => { it('should return only visible people', async () => {
const { status, body } = await request(app).get('/person').set('Authorization', `Bearer ${admin.accessToken}`); const { status, body } = await request(app).get('/people').set('Authorization', `Bearer ${admin.accessToken}`);
expect(status).toBe(200); expect(status).toBe(200);
expect(body).toEqual({ expect(body).toEqual({
@@ -90,9 +90,9 @@ describe('/person', () => {
}); });
}); });
describe('GET /person/:id', () => { describe('GET /people/:id', () => {
it('should require authentication', async () => { it('should require authentication', async () => {
const { status, body } = await request(app).get(`/person/${uuidDto.notFound}`); const { status, body } = await request(app).get(`/people/${uuidDto.notFound}`);
expect(status).toBe(401); expect(status).toBe(401);
expect(body).toEqual(errorDto.unauthorized); expect(body).toEqual(errorDto.unauthorized);
@@ -100,7 +100,7 @@ describe('/person', () => {
it('should throw error if person with id does not exist', async () => { it('should throw error if person with id does not exist', async () => {
const { status, body } = await request(app) const { status, body } = await request(app)
.get(`/person/${uuidDto.notFound}`) .get(`/people/${uuidDto.notFound}`)
.set('Authorization', `Bearer ${admin.accessToken}`); .set('Authorization', `Bearer ${admin.accessToken}`);
expect(status).toBe(400); expect(status).toBe(400);
@@ -109,7 +109,7 @@ describe('/person', () => {
it('should return person information', async () => { it('should return person information', async () => {
const { status, body } = await request(app) const { status, body } = await request(app)
.get(`/person/${visiblePerson.id}`) .get(`/people/${visiblePerson.id}`)
.set('Authorization', `Bearer ${admin.accessToken}`); .set('Authorization', `Bearer ${admin.accessToken}`);
expect(status).toBe(200); expect(status).toBe(200);
@@ -117,9 +117,9 @@ describe('/person', () => {
}); });
}); });
describe('GET /person/:id/statistics', () => { describe('GET /people/:id/statistics', () => {
it('should require authentication', async () => { it('should require authentication', async () => {
const { status, body } = await request(app).get(`/person/${multipleAssetsPerson.id}/statistics`); const { status, body } = await request(app).get(`/people/${multipleAssetsPerson.id}/statistics`);
expect(status).toBe(401); expect(status).toBe(401);
expect(body).toEqual(errorDto.unauthorized); expect(body).toEqual(errorDto.unauthorized);
@@ -127,7 +127,7 @@ describe('/person', () => {
it('should throw error if person with id does not exist', async () => { it('should throw error if person with id does not exist', async () => {
const { status, body } = await request(app) const { status, body } = await request(app)
.get(`/person/${uuidDto.notFound}/statistics`) .get(`/people/${uuidDto.notFound}/statistics`)
.set('Authorization', `Bearer ${admin.accessToken}`); .set('Authorization', `Bearer ${admin.accessToken}`);
expect(status).toBe(400); expect(status).toBe(400);
@@ -136,7 +136,7 @@ describe('/person', () => {
it('should return the correct number of assets', async () => { it('should return the correct number of assets', async () => {
const { status, body } = await request(app) const { status, body } = await request(app)
.get(`/person/${multipleAssetsPerson.id}/statistics`) .get(`/people/${multipleAssetsPerson.id}/statistics`)
.set('Authorization', `Bearer ${admin.accessToken}`); .set('Authorization', `Bearer ${admin.accessToken}`);
expect(status).toBe(200); expect(status).toBe(200);
@@ -144,9 +144,9 @@ describe('/person', () => {
}); });
}); });
describe('POST /person', () => { describe('POST /people', () => {
it('should require authentication', async () => { it('should require authentication', async () => {
const { status, body } = await request(app).post(`/person`); const { status, body } = await request(app).post(`/people`);
expect(status).toBe(401); expect(status).toBe(401);
expect(body).toEqual(errorDto.unauthorized); expect(body).toEqual(errorDto.unauthorized);
}); });
@@ -154,7 +154,7 @@ describe('/person', () => {
for (const { birthDate, response } of invalidBirthday) { for (const { birthDate, response } of invalidBirthday) {
it(`should not accept an invalid birth date [${birthDate}]`, async () => { it(`should not accept an invalid birth date [${birthDate}]`, async () => {
const { status, body } = await request(app) const { status, body } = await request(app)
.post(`/person`) .post(`/people`)
.set('Authorization', `Bearer ${admin.accessToken}`) .set('Authorization', `Bearer ${admin.accessToken}`)
.send({ birthDate }); .send({ birthDate });
expect(status).toBe(400); expect(status).toBe(400);
@@ -164,7 +164,7 @@ describe('/person', () => {
it('should create a person', async () => { it('should create a person', async () => {
const { status, body } = await request(app) const { status, body } = await request(app)
.post(`/person`) .post(`/people`)
.set('Authorization', `Bearer ${admin.accessToken}`) .set('Authorization', `Bearer ${admin.accessToken}`)
.send({ .send({
name: 'New Person', name: 'New Person',
@@ -179,9 +179,9 @@ describe('/person', () => {
}); });
}); });
describe('PUT /person/:id', () => { describe('PUT /people/:id', () => {
it('should require authentication', async () => { it('should require authentication', async () => {
const { status, body } = await request(app).put(`/person/${uuidDto.notFound}`); const { status, body } = await request(app).put(`/people/${uuidDto.notFound}`);
expect(status).toBe(401); expect(status).toBe(401);
expect(body).toEqual(errorDto.unauthorized); expect(body).toEqual(errorDto.unauthorized);
}); });
@@ -193,7 +193,7 @@ describe('/person', () => {
]) { ]) {
it(`should not allow null ${key}`, async () => { it(`should not allow null ${key}`, async () => {
const { status, body } = await request(app) const { status, body } = await request(app)
.put(`/person/${visiblePerson.id}`) .put(`/people/${visiblePerson.id}`)
.set('Authorization', `Bearer ${admin.accessToken}`) .set('Authorization', `Bearer ${admin.accessToken}`)
.send({ [key]: null }); .send({ [key]: null });
expect(status).toBe(400); expect(status).toBe(400);
@@ -204,7 +204,7 @@ describe('/person', () => {
for (const { birthDate, response } of invalidBirthday) { for (const { birthDate, response } of invalidBirthday) {
it(`should not accept an invalid birth date [${birthDate}]`, async () => { it(`should not accept an invalid birth date [${birthDate}]`, async () => {
const { status, body } = await request(app) const { status, body } = await request(app)
.put(`/person/${visiblePerson.id}`) .put(`/people/${visiblePerson.id}`)
.set('Authorization', `Bearer ${admin.accessToken}`) .set('Authorization', `Bearer ${admin.accessToken}`)
.send({ birthDate }); .send({ birthDate });
expect(status).toBe(400); expect(status).toBe(400);
@@ -214,7 +214,7 @@ describe('/person', () => {
it('should update a date of birth', async () => { it('should update a date of birth', async () => {
const { status, body } = await request(app) const { status, body } = await request(app)
.put(`/person/${visiblePerson.id}`) .put(`/people/${visiblePerson.id}`)
.set('Authorization', `Bearer ${admin.accessToken}`) .set('Authorization', `Bearer ${admin.accessToken}`)
.send({ birthDate: '1990-01-01T05:00:00.000Z' }); .send({ birthDate: '1990-01-01T05:00:00.000Z' });
expect(status).toBe(200); expect(status).toBe(200);
@@ -223,7 +223,7 @@ describe('/person', () => {
it('should clear a date of birth', async () => { it('should clear a date of birth', async () => {
const { status, body } = await request(app) const { status, body } = await request(app)
.put(`/person/${visiblePerson.id}`) .put(`/people/${visiblePerson.id}`)
.set('Authorization', `Bearer ${admin.accessToken}`) .set('Authorization', `Bearer ${admin.accessToken}`)
.send({ birthDate: null }); .send({ birthDate: null });
expect(status).toBe(200); expect(status).toBe(200);

View File

@@ -15,16 +15,16 @@ describe('/server-info', () => {
nonAdmin = await utils.userSetup(admin.accessToken, createUserDto.user1); nonAdmin = await utils.userSetup(admin.accessToken, createUserDto.user1);
}); });
describe('GET /server-info', () => { describe('GET /server-info/storage', () => {
it('should require authentication', async () => { it('should require authentication', async () => {
const { status, body } = await request(app).get('/server-info'); const { status, body } = await request(app).get('/server-info/storage');
expect(status).toBe(401); expect(status).toBe(401);
expect(body).toEqual(errorDto.unauthorized); expect(body).toEqual(errorDto.unauthorized);
}); });
it('should return the disk information', async () => { it('should return the disk information', async () => {
const { status, body } = await request(app) const { status, body } = await request(app)
.get('/server-info') .get('/server-info/storage')
.set('Authorization', `Bearer ${admin.accessToken}`); .set('Authorization', `Bearer ${admin.accessToken}`);
expect(status).toBe(200); expect(status).toBe(200);
expect(body).toEqual({ expect(body).toEqual({

View File

@@ -5,7 +5,7 @@ import {
SharedLinkResponseDto, SharedLinkResponseDto,
SharedLinkType, SharedLinkType,
createAlbum, createAlbum,
deleteUser, deleteUserAdmin,
} from '@immich/sdk'; } from '@immich/sdk';
import { createUserDto, uuidDto } from 'src/fixtures'; import { createUserDto, uuidDto } from 'src/fixtures';
import { errorDto } from 'src/responses'; import { errorDto } from 'src/responses';
@@ -13,7 +13,7 @@ import { app, asBearerAuth, shareUrl, utils } from 'src/utils';
import request from 'supertest'; import request from 'supertest';
import { beforeAll, describe, expect, it } from 'vitest'; import { beforeAll, describe, expect, it } from 'vitest';
describe('/shared-link', () => { describe('/shared-links', () => {
let admin: LoginResponseDto; let admin: LoginResponseDto;
let asset1: AssetFileUploadResponseDto; let asset1: AssetFileUploadResponseDto;
let asset2: AssetFileUploadResponseDto; let asset2: AssetFileUploadResponseDto;
@@ -86,7 +86,7 @@ describe('/shared-link', () => {
}), }),
]); ]);
await deleteUser({ id: user2.userId, deleteUserDto: {} }, { headers: asBearerAuth(admin.accessToken) }); await deleteUserAdmin({ id: user2.userId, userAdminDeleteDto: {} }, { headers: asBearerAuth(admin.accessToken) });
}); });
describe('GET /share/${key}', () => { describe('GET /share/${key}', () => {
@@ -114,9 +114,9 @@ describe('/shared-link', () => {
}); });
}); });
describe('GET /shared-link', () => { describe('GET /shared-links', () => {
it('should require authentication', async () => { it('should require authentication', async () => {
const { status, body } = await request(app).get('/shared-link'); const { status, body } = await request(app).get('/shared-links');
expect(status).toBe(401); expect(status).toBe(401);
expect(body).toEqual(errorDto.unauthorized); expect(body).toEqual(errorDto.unauthorized);
@@ -124,7 +124,7 @@ describe('/shared-link', () => {
it('should get all shared links created by user', async () => { it('should get all shared links created by user', async () => {
const { status, body } = await request(app) const { status, body } = await request(app)
.get('/shared-link') .get('/shared-links')
.set('Authorization', `Bearer ${user1.accessToken}`); .set('Authorization', `Bearer ${user1.accessToken}`);
expect(status).toBe(200); expect(status).toBe(200);
@@ -142,7 +142,7 @@ describe('/shared-link', () => {
it('should not get shared links created by other users', async () => { it('should not get shared links created by other users', async () => {
const { status, body } = await request(app) const { status, body } = await request(app)
.get('/shared-link') .get('/shared-links')
.set('Authorization', `Bearer ${admin.accessToken}`); .set('Authorization', `Bearer ${admin.accessToken}`);
expect(status).toBe(200); expect(status).toBe(200);
@@ -150,15 +150,15 @@ describe('/shared-link', () => {
}); });
}); });
describe('GET /shared-link/me', () => { describe('GET /shared-links/me', () => {
it('should not require admin authentication', async () => { it('should not require admin authentication', async () => {
const { status } = await request(app).get('/shared-link/me').set('Authorization', `Bearer ${admin.accessToken}`); const { status } = await request(app).get('/shared-links/me').set('Authorization', `Bearer ${admin.accessToken}`);
expect(status).toBe(403); expect(status).toBe(403);
}); });
it('should get data for correct shared link', async () => { it('should get data for correct shared link', async () => {
const { status, body } = await request(app).get('/shared-link/me').query({ key: linkWithAlbum.key }); const { status, body } = await request(app).get('/shared-links/me').query({ key: linkWithAlbum.key });
expect(status).toBe(200); expect(status).toBe(200);
expect(body).toEqual( expect(body).toEqual(
@@ -172,7 +172,7 @@ describe('/shared-link', () => {
it('should return unauthorized for incorrect shared link', async () => { it('should return unauthorized for incorrect shared link', async () => {
const { status, body } = await request(app) const { status, body } = await request(app)
.get('/shared-link/me') .get('/shared-links/me')
.query({ key: linkWithAlbum.key + 'foo' }); .query({ key: linkWithAlbum.key + 'foo' });
expect(status).toBe(401); expect(status).toBe(401);
@@ -180,14 +180,14 @@ describe('/shared-link', () => {
}); });
it('should return unauthorized if target has been soft deleted', async () => { it('should return unauthorized if target has been soft deleted', async () => {
const { status, body } = await request(app).get('/shared-link/me').query({ key: linkWithDeletedAlbum.key }); const { status, body } = await request(app).get('/shared-links/me').query({ key: linkWithDeletedAlbum.key });
expect(status).toBe(401); expect(status).toBe(401);
expect(body).toEqual(errorDto.invalidShareKey); expect(body).toEqual(errorDto.invalidShareKey);
}); });
it('should return unauthorized for password protected link', async () => { it('should return unauthorized for password protected link', async () => {
const { status, body } = await request(app).get('/shared-link/me').query({ key: linkWithPassword.key }); const { status, body } = await request(app).get('/shared-links/me').query({ key: linkWithPassword.key });
expect(status).toBe(401); expect(status).toBe(401);
expect(body).toEqual(errorDto.invalidSharePassword); expect(body).toEqual(errorDto.invalidSharePassword);
@@ -195,7 +195,7 @@ describe('/shared-link', () => {
it('should get data for correct password protected link', async () => { it('should get data for correct password protected link', async () => {
const { status, body } = await request(app) const { status, body } = await request(app)
.get('/shared-link/me') .get('/shared-links/me')
.query({ key: linkWithPassword.key, password: 'foo' }); .query({ key: linkWithPassword.key, password: 'foo' });
expect(status).toBe(200); expect(status).toBe(200);
@@ -209,7 +209,7 @@ describe('/shared-link', () => {
}); });
it('should return metadata for album shared link', async () => { it('should return metadata for album shared link', async () => {
const { status, body } = await request(app).get('/shared-link/me').query({ key: linkWithMetadata.key }); const { status, body } = await request(app).get('/shared-links/me').query({ key: linkWithMetadata.key });
expect(status).toBe(200); expect(status).toBe(200);
expect(body.assets).toHaveLength(1); expect(body.assets).toHaveLength(1);
@@ -225,7 +225,7 @@ describe('/shared-link', () => {
}); });
it('should not return metadata for album shared link without metadata', async () => { it('should not return metadata for album shared link without metadata', async () => {
const { status, body } = await request(app).get('/shared-link/me').query({ key: linkWithoutMetadata.key }); const { status, body } = await request(app).get('/shared-links/me').query({ key: linkWithoutMetadata.key });
expect(status).toBe(200); expect(status).toBe(200);
expect(body.assets).toHaveLength(1); expect(body.assets).toHaveLength(1);
@@ -239,9 +239,9 @@ describe('/shared-link', () => {
}); });
}); });
describe('GET /shared-link/:id', () => { describe('GET /shared-links/:id', () => {
it('should require authentication', async () => { it('should require authentication', async () => {
const { status, body } = await request(app).get(`/shared-link/${linkWithAlbum.id}`); const { status, body } = await request(app).get(`/shared-links/${linkWithAlbum.id}`);
expect(status).toBe(401); expect(status).toBe(401);
expect(body).toEqual(errorDto.unauthorized); expect(body).toEqual(errorDto.unauthorized);
@@ -249,7 +249,7 @@ describe('/shared-link', () => {
it('should get shared link by id', async () => { it('should get shared link by id', async () => {
const { status, body } = await request(app) const { status, body } = await request(app)
.get(`/shared-link/${linkWithAlbum.id}`) .get(`/shared-links/${linkWithAlbum.id}`)
.set('Authorization', `Bearer ${user1.accessToken}`); .set('Authorization', `Bearer ${user1.accessToken}`);
expect(status).toBe(200); expect(status).toBe(200);
@@ -264,7 +264,7 @@ describe('/shared-link', () => {
it('should not get shared link by id if user has not created the link or it does not exist', async () => { it('should not get shared link by id if user has not created the link or it does not exist', async () => {
const { status, body } = await request(app) const { status, body } = await request(app)
.get(`/shared-link/${linkWithAlbum.id}`) .get(`/shared-links/${linkWithAlbum.id}`)
.set('Authorization', `Bearer ${admin.accessToken}`); .set('Authorization', `Bearer ${admin.accessToken}`);
expect(status).toBe(400); expect(status).toBe(400);
@@ -272,10 +272,10 @@ describe('/shared-link', () => {
}); });
}); });
describe('POST /shared-link', () => { describe('POST /shared-links', () => {
it('should require authentication', async () => { it('should require authentication', async () => {
const { status, body } = await request(app) const { status, body } = await request(app)
.post('/shared-link') .post('/shared-links')
.send({ type: SharedLinkType.Album, albumId: uuidDto.notFound }); .send({ type: SharedLinkType.Album, albumId: uuidDto.notFound });
expect(status).toBe(401); expect(status).toBe(401);
@@ -284,7 +284,7 @@ describe('/shared-link', () => {
it('should require a type and the correspondent asset/album id', async () => { it('should require a type and the correspondent asset/album id', async () => {
const { status, body } = await request(app) const { status, body } = await request(app)
.post('/shared-link') .post('/shared-links')
.set('Authorization', `Bearer ${user1.accessToken}`); .set('Authorization', `Bearer ${user1.accessToken}`);
expect(status).toBe(400); expect(status).toBe(400);
@@ -293,7 +293,7 @@ describe('/shared-link', () => {
it('should require an asset/album id', async () => { it('should require an asset/album id', async () => {
const { status, body } = await request(app) const { status, body } = await request(app)
.post('/shared-link') .post('/shared-links')
.set('Authorization', `Bearer ${user1.accessToken}`) .set('Authorization', `Bearer ${user1.accessToken}`)
.send({ type: SharedLinkType.Album }); .send({ type: SharedLinkType.Album });
@@ -303,7 +303,7 @@ describe('/shared-link', () => {
it('should require a valid asset id', async () => { it('should require a valid asset id', async () => {
const { status, body } = await request(app) const { status, body } = await request(app)
.post('/shared-link') .post('/shared-links')
.set('Authorization', `Bearer ${user1.accessToken}`) .set('Authorization', `Bearer ${user1.accessToken}`)
.send({ type: SharedLinkType.Individual, assetId: uuidDto.notFound }); .send({ type: SharedLinkType.Individual, assetId: uuidDto.notFound });
@@ -313,7 +313,7 @@ describe('/shared-link', () => {
it('should create a shared link', async () => { it('should create a shared link', async () => {
const { status, body } = await request(app) const { status, body } = await request(app)
.post('/shared-link') .post('/shared-links')
.set('Authorization', `Bearer ${user1.accessToken}`) .set('Authorization', `Bearer ${user1.accessToken}`)
.send({ type: SharedLinkType.Album, albumId: album.id }); .send({ type: SharedLinkType.Album, albumId: album.id });
@@ -327,10 +327,10 @@ describe('/shared-link', () => {
}); });
}); });
describe('PATCH /shared-link/:id', () => { describe('PATCH /shared-links/:id', () => {
it('should require authentication', async () => { it('should require authentication', async () => {
const { status, body } = await request(app) const { status, body } = await request(app)
.patch(`/shared-link/${linkWithAlbum.id}`) .patch(`/shared-links/${linkWithAlbum.id}`)
.send({ description: 'foo' }); .send({ description: 'foo' });
expect(status).toBe(401); expect(status).toBe(401);
@@ -339,7 +339,7 @@ describe('/shared-link', () => {
it('should fail if invalid link', async () => { it('should fail if invalid link', async () => {
const { status, body } = await request(app) const { status, body } = await request(app)
.patch(`/shared-link/${uuidDto.notFound}`) .patch(`/shared-links/${uuidDto.notFound}`)
.set('Authorization', `Bearer ${user1.accessToken}`) .set('Authorization', `Bearer ${user1.accessToken}`)
.send({ description: 'foo' }); .send({ description: 'foo' });
@@ -349,7 +349,7 @@ describe('/shared-link', () => {
it('should update shared link', async () => { it('should update shared link', async () => {
const { status, body } = await request(app) const { status, body } = await request(app)
.patch(`/shared-link/${linkWithAlbum.id}`) .patch(`/shared-links/${linkWithAlbum.id}`)
.set('Authorization', `Bearer ${user1.accessToken}`) .set('Authorization', `Bearer ${user1.accessToken}`)
.send({ description: 'foo' }); .send({ description: 'foo' });
@@ -364,10 +364,10 @@ describe('/shared-link', () => {
}); });
}); });
describe('PUT /shared-link/:id/assets', () => { describe('PUT /shared-links/:id/assets', () => {
it('should not add assets to shared link (album)', async () => { it('should not add assets to shared link (album)', async () => {
const { status, body } = await request(app) const { status, body } = await request(app)
.put(`/shared-link/${linkWithAlbum.id}/assets`) .put(`/shared-links/${linkWithAlbum.id}/assets`)
.set('Authorization', `Bearer ${user1.accessToken}`) .set('Authorization', `Bearer ${user1.accessToken}`)
.send({ assetIds: [asset2.id] }); .send({ assetIds: [asset2.id] });
@@ -377,7 +377,7 @@ describe('/shared-link', () => {
it('should add an assets to a shared link (individual)', async () => { it('should add an assets to a shared link (individual)', async () => {
const { status, body } = await request(app) const { status, body } = await request(app)
.put(`/shared-link/${linkWithAssets.id}/assets`) .put(`/shared-links/${linkWithAssets.id}/assets`)
.set('Authorization', `Bearer ${user1.accessToken}`) .set('Authorization', `Bearer ${user1.accessToken}`)
.send({ assetIds: [asset2.id] }); .send({ assetIds: [asset2.id] });
@@ -386,10 +386,10 @@ describe('/shared-link', () => {
}); });
}); });
describe('DELETE /shared-link/:id/assets', () => { describe('DELETE /shared-links/:id/assets', () => {
it('should not remove assets from a shared link (album)', async () => { it('should not remove assets from a shared link (album)', async () => {
const { status, body } = await request(app) const { status, body } = await request(app)
.delete(`/shared-link/${linkWithAlbum.id}/assets`) .delete(`/shared-links/${linkWithAlbum.id}/assets`)
.set('Authorization', `Bearer ${user1.accessToken}`) .set('Authorization', `Bearer ${user1.accessToken}`)
.send({ assetIds: [asset2.id] }); .send({ assetIds: [asset2.id] });
@@ -399,7 +399,7 @@ describe('/shared-link', () => {
it('should remove assets from a shared link (individual)', async () => { it('should remove assets from a shared link (individual)', async () => {
const { status, body } = await request(app) const { status, body } = await request(app)
.delete(`/shared-link/${linkWithAssets.id}/assets`) .delete(`/shared-links/${linkWithAssets.id}/assets`)
.set('Authorization', `Bearer ${user1.accessToken}`) .set('Authorization', `Bearer ${user1.accessToken}`)
.send({ assetIds: [asset2.id] }); .send({ assetIds: [asset2.id] });
@@ -408,9 +408,9 @@ describe('/shared-link', () => {
}); });
}); });
describe('DELETE /shared-link/:id', () => { describe('DELETE /shared-links/:id', () => {
it('should require authentication', async () => { it('should require authentication', async () => {
const { status, body } = await request(app).delete(`/shared-link/${linkWithAlbum.id}`); const { status, body } = await request(app).delete(`/shared-links/${linkWithAlbum.id}`);
expect(status).toBe(401); expect(status).toBe(401);
expect(body).toEqual(errorDto.unauthorized); expect(body).toEqual(errorDto.unauthorized);
@@ -418,7 +418,7 @@ describe('/shared-link', () => {
it('should fail if invalid link', async () => { it('should fail if invalid link', async () => {
const { status, body } = await request(app) const { status, body } = await request(app)
.delete(`/shared-link/${uuidDto.notFound}`) .delete(`/shared-links/${uuidDto.notFound}`)
.set('Authorization', `Bearer ${user1.accessToken}`); .set('Authorization', `Bearer ${user1.accessToken}`);
expect(status).toBe(400); expect(status).toBe(400);
@@ -427,7 +427,7 @@ describe('/shared-link', () => {
it('should delete a shared link', async () => { it('should delete a shared link', async () => {
const { status } = await request(app) const { status } = await request(app)
.delete(`/shared-link/${linkWithAlbum.id}`) .delete(`/shared-links/${linkWithAlbum.id}`)
.set('Authorization', `Bearer ${user1.accessToken}`); .set('Authorization', `Bearer ${user1.accessToken}`);
expect(status).toBe(200); expect(status).toBe(200);

View File

@@ -1,4 +1,4 @@
import { LoginResponseDto, getAllAssets } from '@immich/sdk'; import { LoginResponseDto, getAssetInfo, getAssetStatistics } from '@immich/sdk';
import { Socket } from 'socket.io-client'; import { Socket } from 'socket.io-client';
import { errorDto } from 'src/responses'; import { errorDto } from 'src/responses';
import { app, asBearerAuth, utils } from 'src/utils'; import { app, asBearerAuth, utils } from 'src/utils';
@@ -31,16 +31,16 @@ describe('/trash', () => {
const { id: assetId } = await utils.createAsset(admin.accessToken); const { id: assetId } = await utils.createAsset(admin.accessToken);
await utils.deleteAssets(admin.accessToken, [assetId]); await utils.deleteAssets(admin.accessToken, [assetId]);
const before = await getAllAssets({}, { headers: asBearerAuth(admin.accessToken) }); const before = await getAssetInfo({ id: assetId }, { headers: asBearerAuth(admin.accessToken) });
expect(before).toStrictEqual([expect.objectContaining({ id: assetId, isTrashed: true })]); expect(before).toStrictEqual(expect.objectContaining({ id: assetId, isTrashed: true }));
const { status } = await request(app).post('/trash/empty').set('Authorization', `Bearer ${admin.accessToken}`); const { status } = await request(app).post('/trash/empty').set('Authorization', `Bearer ${admin.accessToken}`);
expect(status).toBe(204); expect(status).toBe(204);
await utils.waitForWebsocketEvent({ event: 'assetDelete', id: assetId }); await utils.waitForWebsocketEvent({ event: 'assetDelete', id: assetId });
const after = await getAllAssets({}, { headers: asBearerAuth(admin.accessToken) }); const after = await getAssetStatistics({ isTrashed: true }, { headers: asBearerAuth(admin.accessToken) });
expect(after.length).toBe(0); expect(after.total).toBe(0);
}); });
}); });
@@ -56,14 +56,14 @@ describe('/trash', () => {
const { id: assetId } = await utils.createAsset(admin.accessToken); const { id: assetId } = await utils.createAsset(admin.accessToken);
await utils.deleteAssets(admin.accessToken, [assetId]); await utils.deleteAssets(admin.accessToken, [assetId]);
const before = await getAllAssets({}, { headers: asBearerAuth(admin.accessToken) }); const before = await getAssetInfo({ id: assetId }, { headers: asBearerAuth(admin.accessToken) });
expect(before).toStrictEqual([expect.objectContaining({ id: assetId, isTrashed: true })]); expect(before).toStrictEqual(expect.objectContaining({ id: assetId, isTrashed: true }));
const { status } = await request(app).post('/trash/restore').set('Authorization', `Bearer ${admin.accessToken}`); const { status } = await request(app).post('/trash/restore').set('Authorization', `Bearer ${admin.accessToken}`);
expect(status).toBe(204); expect(status).toBe(204);
const after = await getAllAssets({}, { headers: asBearerAuth(admin.accessToken) }); const after = await getAssetInfo({ id: assetId }, { headers: asBearerAuth(admin.accessToken) });
expect(after).toStrictEqual([expect.objectContaining({ id: assetId, isTrashed: false })]); expect(after).toStrictEqual(expect.objectContaining({ id: assetId, isTrashed: false }));
}); });
}); });

View File

@@ -0,0 +1,317 @@
import { LoginResponseDto, deleteUserAdmin, getMyUser, getUserAdmin, login } from '@immich/sdk';
import { Socket } from 'socket.io-client';
import { createUserDto, uuidDto } from 'src/fixtures';
import { errorDto } from 'src/responses';
import { app, asBearerAuth, utils } from 'src/utils';
import request from 'supertest';
import { afterAll, beforeAll, describe, expect, it } from 'vitest';
describe('/admin/users', () => {
let websocket: Socket;
let admin: LoginResponseDto;
let nonAdmin: LoginResponseDto;
let deletedUser: LoginResponseDto;
let userToDelete: LoginResponseDto;
let userToHardDelete: LoginResponseDto;
beforeAll(async () => {
await utils.resetDatabase();
admin = await utils.adminSetup({ onboarding: false });
[websocket, nonAdmin, deletedUser, userToDelete, userToHardDelete] = await Promise.all([
utils.connectWebsocket(admin.accessToken),
utils.userSetup(admin.accessToken, createUserDto.user1),
utils.userSetup(admin.accessToken, createUserDto.user2),
utils.userSetup(admin.accessToken, createUserDto.user3),
utils.userSetup(admin.accessToken, createUserDto.user4),
]);
await deleteUserAdmin(
{ id: deletedUser.userId, userAdminDeleteDto: {} },
{ headers: asBearerAuth(admin.accessToken) },
);
});
afterAll(() => {
utils.disconnectWebsocket(websocket);
});
describe('GET /admin/users', () => {
it('should require authentication', async () => {
const { status, body } = await request(app).get(`/admin/users`);
expect(status).toBe(401);
expect(body).toEqual(errorDto.unauthorized);
});
it('should require authorization', async () => {
const { status, body } = await request(app)
.get(`/admin/users`)
.set('Authorization', `Bearer ${nonAdmin.accessToken}`);
expect(status).toBe(403);
expect(body).toEqual(errorDto.forbidden);
});
it('should hide deleted users by default', async () => {
const { status, body } = await request(app)
.get(`/admin/users`)
.set('Authorization', `Bearer ${admin.accessToken}`);
expect(status).toBe(200);
expect(body).toHaveLength(4);
expect(body).toEqual(
expect.arrayContaining([
expect.objectContaining({ email: admin.userEmail }),
expect.objectContaining({ email: nonAdmin.userEmail }),
expect.objectContaining({ email: userToDelete.userEmail }),
expect.objectContaining({ email: userToHardDelete.userEmail }),
]),
);
});
it('should include deleted users', async () => {
const { status, body } = await request(app)
.get(`/admin/users?withDeleted=true`)
.set('Authorization', `Bearer ${admin.accessToken}`);
expect(status).toBe(200);
expect(body).toHaveLength(5);
expect(body).toEqual(
expect.arrayContaining([
expect.objectContaining({ email: admin.userEmail }),
expect.objectContaining({ email: nonAdmin.userEmail }),
expect.objectContaining({ email: userToDelete.userEmail }),
expect.objectContaining({ email: userToHardDelete.userEmail }),
expect.objectContaining({ email: deletedUser.userEmail }),
]),
);
});
});
describe('POST /admin/users', () => {
it('should require authentication', async () => {
const { status, body } = await request(app).post(`/admin/users`).send(createUserDto.user1);
expect(status).toBe(401);
expect(body).toEqual(errorDto.unauthorized);
});
it('should require authorization', async () => {
const { status, body } = await request(app)
.post(`/admin/users`)
.set('Authorization', `Bearer ${nonAdmin.accessToken}`)
.send(createUserDto.user1);
expect(status).toBe(403);
expect(body).toEqual(errorDto.forbidden);
});
for (const key of [
'password',
'email',
'name',
'quotaSizeInBytes',
'shouldChangePassword',
'memoriesEnabled',
'notify',
]) {
it(`should not allow null ${key}`, async () => {
const { status, body } = await request(app)
.post(`/admin/users`)
.set('Authorization', `Bearer ${admin.accessToken}`)
.send({ ...createUserDto.user1, [key]: null });
expect(status).toBe(400);
expect(body).toEqual(errorDto.badRequest());
});
}
it('should ignore `isAdmin`', async () => {
const { status, body } = await request(app)
.post(`/admin/users`)
.send({
isAdmin: true,
email: 'user5@immich.cloud',
password: 'password123',
name: 'Immich',
})
.set('Authorization', `Bearer ${admin.accessToken}`);
expect(body).toMatchObject({
email: 'user5@immich.cloud',
isAdmin: false,
shouldChangePassword: true,
});
expect(status).toBe(201);
});
it('should create a user without memories enabled', async () => {
const { status, body } = await request(app)
.post(`/admin/users`)
.send({
email: 'no-memories@immich.cloud',
password: 'Password123',
name: 'No Memories',
memoriesEnabled: false,
})
.set('Authorization', `Bearer ${admin.accessToken}`);
expect(body).toMatchObject({
email: 'no-memories@immich.cloud',
memoriesEnabled: false,
});
expect(status).toBe(201);
});
});
describe('PUT /admin/users/:id', () => {
it('should require authentication', async () => {
const { status, body } = await request(app).put(`/admin/users/${uuidDto.notFound}`);
expect(status).toBe(401);
expect(body).toEqual(errorDto.unauthorized);
});
it('should require authorization', async () => {
const { status, body } = await request(app)
.put(`/admin/users/${uuidDto.notFound}`)
.set('Authorization', `Bearer ${nonAdmin.accessToken}`);
expect(status).toBe(403);
expect(body).toEqual(errorDto.forbidden);
});
for (const key of ['password', 'email', 'name', 'shouldChangePassword', 'memoriesEnabled']) {
it(`should not allow null ${key}`, async () => {
const { status, body } = await request(app)
.put(`/admin/users/${uuidDto.notFound}`)
.set('Authorization', `Bearer ${admin.accessToken}`)
.send({ [key]: null });
expect(status).toBe(400);
expect(body).toEqual(errorDto.badRequest());
});
}
it('should not allow a non-admin to become an admin', async () => {
const { status, body } = await request(app)
.put(`/admin/users/${nonAdmin.userId}`)
.send({ isAdmin: true })
.set('Authorization', `Bearer ${admin.accessToken}`);
expect(status).toBe(200);
expect(body).toMatchObject({ isAdmin: false });
});
it('ignores updates to profileImagePath', async () => {
const { status, body } = await request(app)
.put(`/admin/users/${admin.userId}`)
.send({ profileImagePath: 'invalid.jpg' })
.set('Authorization', `Bearer ${admin.accessToken}`);
expect(status).toBe(200);
expect(body).toMatchObject({ id: admin.userId, profileImagePath: '' });
});
it('should update first and last name', async () => {
const before = await getUserAdmin({ id: admin.userId }, { headers: asBearerAuth(admin.accessToken) });
const { status, body } = await request(app)
.put(`/admin/users/${admin.userId}`)
.send({ name: 'Name' })
.set('Authorization', `Bearer ${admin.accessToken}`);
expect(status).toBe(200);
expect(body).toEqual({
...before,
updatedAt: expect.any(String),
name: 'Name',
});
expect(before.updatedAt).not.toEqual(body.updatedAt);
});
it('should update memories enabled', async () => {
const before = await getUserAdmin({ id: admin.userId }, { headers: asBearerAuth(admin.accessToken) });
const { status, body } = await request(app)
.put(`/admin/users/${admin.userId}`)
.send({ memoriesEnabled: false })
.set('Authorization', `Bearer ${admin.accessToken}`);
expect(status).toBe(200);
expect(body).toMatchObject({
...before,
updatedAt: expect.anything(),
memoriesEnabled: false,
});
expect(before.updatedAt).not.toEqual(body.updatedAt);
});
it('should update password', async () => {
const { status, body } = await request(app)
.put(`/admin/users/${nonAdmin.userId}`)
.send({ password: 'super-secret' })
.set('Authorization', `Bearer ${admin.accessToken}`);
expect(status).toBe(200);
expect(body).toMatchObject({ email: nonAdmin.userEmail });
const token = await login({ loginCredentialDto: { email: nonAdmin.userEmail, password: 'super-secret' } });
expect(token.accessToken).toBeDefined();
const user = await getMyUser({ headers: asBearerAuth(token.accessToken) });
expect(user).toMatchObject({ email: nonAdmin.userEmail });
});
});
describe('DELETE /admin/users/:id', () => {
it('should require authentication', async () => {
const { status, body } = await request(app).delete(`/admin/users/${userToDelete.userId}`);
expect(status).toBe(401);
expect(body).toEqual(errorDto.unauthorized);
});
it('should require authorization', async () => {
const { status, body } = await request(app)
.delete(`/admin/users/${userToDelete.userId}`)
.set('Authorization', `Bearer ${nonAdmin.accessToken}`);
expect(status).toBe(403);
expect(body).toEqual(errorDto.forbidden);
});
it('should delete user', async () => {
const { status, body } = await request(app)
.delete(`/admin/users/${userToDelete.userId}`)
.set('Authorization', `Bearer ${admin.accessToken}`);
expect(status).toBe(200);
expect(body).toMatchObject({
id: userToDelete.userId,
updatedAt: expect.any(String),
deletedAt: expect.any(String),
});
});
it('should hard delete a user', async () => {
const { status, body } = await request(app)
.delete(`/admin/users/${userToHardDelete.userId}`)
.send({ force: true })
.set('Authorization', `Bearer ${admin.accessToken}`);
expect(status).toBe(200);
expect(body).toMatchObject({
id: userToHardDelete.userId,
updatedAt: expect.any(String),
deletedAt: expect.any(String),
});
await utils.waitForWebsocketEvent({ event: 'userDelete', id: userToHardDelete.userId, timeout: 5000 });
});
});
describe('POST /admin/users/:id/restore', () => {
it('should require authentication', async () => {
const { status, body } = await request(app).post(`/admin/users/${userToDelete.userId}/restore`);
expect(status).toBe(401);
expect(body).toEqual(errorDto.unauthorized);
});
it('should require authorization', async () => {
const { status, body } = await request(app)
.post(`/admin/users/${userToDelete.userId}/restore`)
.set('Authorization', `Bearer ${nonAdmin.accessToken}`);
expect(status).toBe(403);
expect(body).toEqual(errorDto.forbidden);
});
});
});

View File

@@ -1,288 +1,105 @@
import { LoginResponseDto, deleteUser, getUserById } from '@immich/sdk'; import { LoginResponseDto, SharedLinkType, deleteUserAdmin, getMyUser, login } from '@immich/sdk';
import { Socket } from 'socket.io-client'; import { createUserDto } from 'src/fixtures';
import { createUserDto, userDto } from 'src/fixtures';
import { errorDto } from 'src/responses'; import { errorDto } from 'src/responses';
import { app, asBearerAuth, utils } from 'src/utils'; import { app, asBearerAuth, utils } from 'src/utils';
import request from 'supertest'; import request from 'supertest';
import { afterAll, beforeAll, describe, expect, it } from 'vitest'; import { beforeAll, describe, expect, it } from 'vitest';
describe('/user', () => {
let websocket: Socket;
describe('/users', () => {
let admin: LoginResponseDto; let admin: LoginResponseDto;
let deletedUser: LoginResponseDto; let deletedUser: LoginResponseDto;
let userToDelete: LoginResponseDto;
let userToHardDelete: LoginResponseDto;
let nonAdmin: LoginResponseDto; let nonAdmin: LoginResponseDto;
beforeAll(async () => { beforeAll(async () => {
await utils.resetDatabase(); await utils.resetDatabase();
admin = await utils.adminSetup({ onboarding: false }); admin = await utils.adminSetup({ onboarding: false });
[websocket, deletedUser, nonAdmin, userToDelete, userToHardDelete] = await Promise.all([ [deletedUser, nonAdmin] = await Promise.all([
utils.connectWebsocket(admin.accessToken),
utils.userSetup(admin.accessToken, createUserDto.user1), utils.userSetup(admin.accessToken, createUserDto.user1),
utils.userSetup(admin.accessToken, createUserDto.user2), utils.userSetup(admin.accessToken, createUserDto.user2),
utils.userSetup(admin.accessToken, createUserDto.user3),
utils.userSetup(admin.accessToken, createUserDto.user4),
]); ]);
await deleteUser({ id: deletedUser.userId, deleteUserDto: {} }, { headers: asBearerAuth(admin.accessToken) }); await deleteUserAdmin(
{ id: deletedUser.userId, userAdminDeleteDto: {} },
{ headers: asBearerAuth(admin.accessToken) },
);
}); });
afterAll(() => { describe('GET /users', () => {
utils.disconnectWebsocket(websocket);
});
describe('GET /user', () => {
it('should require authentication', async () => { it('should require authentication', async () => {
const { status, body } = await request(app).get('/user'); const { status, body } = await request(app).get('/users');
expect(status).toBe(401); expect(status).toBe(401);
expect(body).toEqual(errorDto.unauthorized); expect(body).toEqual(errorDto.unauthorized);
}); });
it('should get users', async () => { it('should get users', async () => {
const { status, body } = await request(app).get('/user').set('Authorization', `Bearer ${admin.accessToken}`); const { status, body } = await request(app).get('/users').set('Authorization', `Bearer ${admin.accessToken}`);
expect(status).toEqual(200); expect(status).toEqual(200);
expect(body).toHaveLength(5); expect(body).toHaveLength(2);
expect(body).toEqual(
expect.arrayContaining([
expect.objectContaining({ email: 'admin@immich.cloud' }),
expect.objectContaining({ email: 'user1@immich.cloud' }),
expect.objectContaining({ email: 'user2@immich.cloud' }),
expect.objectContaining({ email: 'user3@immich.cloud' }),
expect.objectContaining({ email: 'user4@immich.cloud' }),
]),
);
});
it('should hide deleted users', async () => {
const { status, body } = await request(app)
.get(`/user`)
.query({ isAll: true })
.set('Authorization', `Bearer ${admin.accessToken}`);
expect(status).toBe(200);
expect(body).toHaveLength(4);
expect(body).toEqual( expect(body).toEqual(
expect.arrayContaining([ expect.arrayContaining([
expect.objectContaining({ email: 'admin@immich.cloud' }), expect.objectContaining({ email: 'admin@immich.cloud' }),
expect.objectContaining({ email: 'user2@immich.cloud' }), expect.objectContaining({ email: 'user2@immich.cloud' }),
expect.objectContaining({ email: 'user3@immich.cloud' }),
expect.objectContaining({ email: 'user4@immich.cloud' }),
]),
);
});
it('should include deleted users', async () => {
const { status, body } = await request(app)
.get(`/user`)
.query({ isAll: false })
.set('Authorization', `Bearer ${admin.accessToken}`);
expect(status).toBe(200);
expect(body).toHaveLength(5);
expect(body).toEqual(
expect.arrayContaining([
expect.objectContaining({ email: 'admin@immich.cloud' }),
expect.objectContaining({ email: 'user1@immich.cloud' }),
expect.objectContaining({ email: 'user2@immich.cloud' }),
expect.objectContaining({ email: 'user3@immich.cloud' }),
expect.objectContaining({ email: 'user4@immich.cloud' }),
]), ]),
); );
}); });
}); });
describe('GET /user/info/:id', () => { describe('GET /users/me', () => {
it('should require authentication', async () => { it('should require authentication', async () => {
const { status } = await request(app).get(`/user/info/${admin.userId}`); const { status, body } = await request(app).get(`/users/me`);
expect(status).toEqual(401); expect(status).toBe(401);
expect(body).toEqual(errorDto.unauthorized);
}); });
it('should get the user info', async () => { it('should not work for shared links', async () => {
const { status, body } = await request(app) const album = await utils.createAlbum(admin.accessToken, { albumName: 'Album' });
.get(`/user/info/${admin.userId}`) const sharedLink = await utils.createSharedLink(admin.accessToken, {
.set('Authorization', `Bearer ${admin.accessToken}`); type: SharedLinkType.Album,
albumId: album.id,
});
const { status, body } = await request(app).get(`/users/me?key=${sharedLink.key}`);
expect(status).toBe(403);
expect(body).toEqual(errorDto.forbidden);
});
it('should get my user', async () => {
const { status, body } = await request(app).get(`/users/me`).set('Authorization', `Bearer ${admin.accessToken}`);
expect(status).toBe(200); expect(status).toBe(200);
expect(body).toMatchObject({ expect(body).toMatchObject({
id: admin.userId, id: admin.userId,
email: 'admin@immich.cloud', email: 'admin@immich.cloud',
memoriesEnabled: true,
quotaUsageInBytes: 0,
}); });
}); });
}); });
describe('GET /user/me', () => { describe('PUT /users/me', () => {
it('should require authentication', async () => { it('should require authentication', async () => {
const { status, body } = await request(app).get(`/user/me`); const { status, body } = await request(app).put(`/users/me`);
expect(status).toBe(401); expect(status).toBe(401);
expect(body).toEqual(errorDto.unauthorized); expect(body).toEqual(errorDto.unauthorized);
}); });
it('should get my info', async () => { for (const key of ['email', 'name', 'memoriesEnabled', 'avatarColor']) {
const { status, body } = await request(app).get(`/user/me`).set('Authorization', `Bearer ${admin.accessToken}`);
expect(status).toBe(200);
expect(body).toMatchObject({
id: admin.userId,
email: 'admin@immich.cloud',
});
});
});
describe('POST /user', () => {
it('should require authentication', async () => {
const { status, body } = await request(app).post(`/user`).send(createUserDto.user1);
expect(status).toBe(401);
expect(body).toEqual(errorDto.unauthorized);
});
for (const key of Object.keys(createUserDto.user1)) {
it(`should not allow null ${key}`, async () => { it(`should not allow null ${key}`, async () => {
const dto = { [key]: null };
const { status, body } = await request(app) const { status, body } = await request(app)
.post(`/user`) .put(`/users/me`)
.set('Authorization', `Bearer ${admin.accessToken}`) .set('Authorization', `Bearer ${admin.accessToken}`)
.send({ ...createUserDto.user1, [key]: null }); .send(dto);
expect(status).toBe(400); expect(status).toBe(400);
expect(body).toEqual(errorDto.badRequest()); expect(body).toEqual(errorDto.badRequest());
}); });
} }
it('should ignore `isAdmin`', async () => {
const { status, body } = await request(app)
.post(`/user`)
.send({
isAdmin: true,
email: 'user5@immich.cloud',
password: 'password123',
name: 'Immich',
})
.set('Authorization', `Bearer ${admin.accessToken}`);
expect(body).toMatchObject({
email: 'user5@immich.cloud',
isAdmin: false,
shouldChangePassword: true,
});
expect(status).toBe(201);
});
it('should create a user without memories enabled', async () => {
const { status, body } = await request(app)
.post(`/user`)
.send({
email: 'no-memories@immich.cloud',
password: 'Password123',
name: 'No Memories',
memoriesEnabled: false,
})
.set('Authorization', `Bearer ${admin.accessToken}`);
expect(body).toMatchObject({
email: 'no-memories@immich.cloud',
memoriesEnabled: false,
});
expect(status).toBe(201);
});
});
describe('DELETE /user/:id', () => {
it('should require authentication', async () => {
const { status, body } = await request(app).delete(`/user/${userToDelete.userId}`);
expect(status).toBe(401);
expect(body).toEqual(errorDto.unauthorized);
});
it('should delete user', async () => {
const { status, body } = await request(app)
.delete(`/user/${userToDelete.userId}`)
.set('Authorization', `Bearer ${admin.accessToken}`);
expect(status).toBe(200);
expect(body).toMatchObject({
id: userToDelete.userId,
updatedAt: expect.any(String),
deletedAt: expect.any(String),
});
});
it('should hard delete user', async () => {
const { status, body } = await request(app)
.delete(`/user/${userToHardDelete.userId}`)
.send({ force: true })
.set('Authorization', `Bearer ${admin.accessToken}`);
expect(status).toBe(200);
expect(body).toMatchObject({
id: userToHardDelete.userId,
updatedAt: expect.any(String),
deletedAt: expect.any(String),
});
await utils.waitForWebsocketEvent({ event: 'userDelete', id: userToHardDelete.userId, timeout: 5000 });
});
});
describe('PUT /user', () => {
it('should require authentication', async () => {
const { status, body } = await request(app).put(`/user`);
expect(status).toBe(401);
expect(body).toEqual(errorDto.unauthorized);
});
for (const key of Object.keys(userDto.admin)) {
it(`should not allow null ${key}`, async () => {
const { status, body } = await request(app)
.put(`/user`)
.set('Authorization', `Bearer ${admin.accessToken}`)
.send({ ...userDto.admin, [key]: null });
expect(status).toBe(400);
expect(body).toEqual(errorDto.badRequest());
});
}
it('should not allow a non-admin to become an admin', async () => {
const { status, body } = await request(app)
.put(`/user`)
.send({ isAdmin: true, id: nonAdmin.userId })
.set('Authorization', `Bearer ${admin.accessToken}`);
expect(status).toBe(400);
expect(body).toEqual(errorDto.alreadyHasAdmin);
});
it('ignores updates to profileImagePath', async () => {
const { status, body } = await request(app)
.put(`/user`)
.send({ id: admin.userId, profileImagePath: 'invalid.jpg' })
.set('Authorization', `Bearer ${admin.accessToken}`);
expect(status).toBe(200);
expect(body).toMatchObject({ id: admin.userId, profileImagePath: '' });
});
it('should ignore updates to createdAt, updatedAt and deletedAt', async () => {
const before = await getUserById({ id: admin.userId }, { headers: asBearerAuth(admin.accessToken) });
const { status, body } = await request(app)
.put(`/user`)
.send({
id: admin.userId,
createdAt: '2023-01-01T00:00:00.000Z',
updatedAt: '2023-01-01T00:00:00.000Z',
deletedAt: '2023-01-01T00:00:00.000Z',
})
.set('Authorization', `Bearer ${admin.accessToken}`);
expect(status).toBe(200);
expect(body).toStrictEqual(before);
});
it('should update first and last name', async () => { it('should update first and last name', async () => {
const before = await getUserById({ id: admin.userId }, { headers: asBearerAuth(admin.accessToken) }); const before = await getMyUser({ headers: asBearerAuth(admin.accessToken) });
const { status, body } = await request(app) const { status, body } = await request(app)
.put(`/user`) .put(`/users/me`)
.send({ .send({ name: 'Name' })
id: admin.userId,
name: 'Name',
})
.set('Authorization', `Bearer ${admin.accessToken}`); .set('Authorization', `Bearer ${admin.accessToken}`);
expect(status).toBe(200); expect(status).toBe(200);
@@ -291,17 +108,13 @@ describe('/user', () => {
updatedAt: expect.any(String), updatedAt: expect.any(String),
name: 'Name', name: 'Name',
}); });
expect(before.updatedAt).not.toEqual(body.updatedAt);
}); });
it('should update memories enabled', async () => { it('should update memories enabled', async () => {
const before = await getUserById({ id: admin.userId }, { headers: asBearerAuth(admin.accessToken) }); const before = await getMyUser({ headers: asBearerAuth(admin.accessToken) });
const { status, body } = await request(app) const { status, body } = await request(app)
.put(`/user`) .put(`/users/me`)
.send({ .send({ memoriesEnabled: false })
id: admin.userId,
memoriesEnabled: false,
})
.set('Authorization', `Bearer ${admin.accessToken}`); .set('Authorization', `Bearer ${admin.accessToken}`);
expect(status).toBe(200); expect(status).toBe(200);
@@ -310,7 +123,80 @@ describe('/user', () => {
updatedAt: expect.anything(), updatedAt: expect.anything(),
memoriesEnabled: false, memoriesEnabled: false,
}); });
expect(before.updatedAt).not.toEqual(body.updatedAt);
const after = await getMyUser({ headers: asBearerAuth(admin.accessToken) });
expect(after.memoriesEnabled).toBe(false);
});
/** @deprecated */
it('should allow a user to change their password (deprecated)', async () => {
const user = await getMyUser({ headers: asBearerAuth(nonAdmin.accessToken) });
expect(user.shouldChangePassword).toBe(true);
const { status, body } = await request(app)
.put(`/users/me`)
.send({ password: 'super-secret' })
.set('Authorization', `Bearer ${nonAdmin.accessToken}`);
expect(status).toBe(200);
expect(body).toMatchObject({
email: nonAdmin.userEmail,
shouldChangePassword: false,
});
const token = await login({ loginCredentialDto: { email: nonAdmin.userEmail, password: 'super-secret' } });
expect(token.accessToken).toBeDefined();
});
it('should not allow user to change to a taken email', async () => {
const { status, body } = await request(app)
.put(`/users/me`)
.send({ email: 'admin@immich.cloud' })
.set('Authorization', `Bearer ${nonAdmin.accessToken}`);
expect(status).toBe(400);
expect(body).toMatchObject(errorDto.badRequest('Email already in use by another account'));
});
it('should update my email', async () => {
const before = await getMyUser({ headers: asBearerAuth(nonAdmin.accessToken) });
const { status, body } = await request(app)
.put(`/users/me`)
.send({ email: 'non-admin@immich.cloud' })
.set('Authorization', `Bearer ${nonAdmin.accessToken}`);
expect(status).toBe(200);
expect(body).toMatchObject({
...before,
email: 'non-admin@immich.cloud',
updatedAt: expect.anything(),
});
});
});
describe('GET /users/:id', () => {
it('should require authentication', async () => {
const { status } = await request(app).get(`/users/${admin.userId}`);
expect(status).toEqual(401);
});
it('should get the user', async () => {
const { status, body } = await request(app)
.get(`/users/${admin.userId}`)
.set('Authorization', `Bearer ${admin.accessToken}`);
expect(status).toBe(200);
expect(body).toMatchObject({
id: admin.userId,
email: 'admin@immich.cloud',
});
expect(body).not.toMatchObject({
shouldChangePassword: expect.anything(),
memoriesEnabled: expect.anything(),
storageLabel: expect.anything(),
});
}); });
}); });
}); });

View File

@@ -1,4 +1,4 @@
import { LoginResponseDto, getAllAlbums, getAllAssets } from '@immich/sdk'; import { LoginResponseDto, getAllAlbums, getAssetStatistics } from '@immich/sdk';
import { readFileSync } from 'node:fs'; import { readFileSync } from 'node:fs';
import { mkdir, readdir, rm, symlink } from 'node:fs/promises'; import { mkdir, readdir, rm, symlink } from 'node:fs/promises';
import { asKeyAuth, immichCli, testAssetDir, utils } from 'src/utils'; import { asKeyAuth, immichCli, testAssetDir, utils } from 'src/utils';
@@ -28,8 +28,8 @@ describe(`immich upload`, () => {
); );
expect(exitCode).toBe(0); expect(exitCode).toBe(0);
const assets = await getAllAssets({}, { headers: asKeyAuth(key) }); const assets = await getAssetStatistics({}, { headers: asKeyAuth(key) });
expect(assets.length).toBe(1); expect(assets.total).toBe(1);
}); });
it('should skip a duplicate file', async () => { it('should skip a duplicate file', async () => {
@@ -40,8 +40,8 @@ describe(`immich upload`, () => {
); );
expect(first.exitCode).toBe(0); expect(first.exitCode).toBe(0);
const assets = await getAllAssets({}, { headers: asKeyAuth(key) }); const assets = await getAssetStatistics({}, { headers: asKeyAuth(key) });
expect(assets.length).toBe(1); expect(assets.total).toBe(1);
const second = await immichCli(['upload', `${testAssetDir}/albums/nature/silver_fir.jpg`]); const second = await immichCli(['upload', `${testAssetDir}/albums/nature/silver_fir.jpg`]);
expect(second.stderr).toBe(''); expect(second.stderr).toBe('');
@@ -60,8 +60,8 @@ describe(`immich upload`, () => {
expect(stdout.split('\n')).toEqual(expect.arrayContaining([expect.stringContaining('No files found, exiting')])); expect(stdout.split('\n')).toEqual(expect.arrayContaining([expect.stringContaining('No files found, exiting')]));
expect(exitCode).toBe(0); expect(exitCode).toBe(0);
const assets = await getAllAssets({}, { headers: asKeyAuth(key) }); const assets = await getAssetStatistics({}, { headers: asKeyAuth(key) });
expect(assets.length).toBe(0); expect(assets.total).toBe(0);
}); });
it('should have accurate dry run', async () => { it('should have accurate dry run', async () => {
@@ -76,8 +76,8 @@ describe(`immich upload`, () => {
); );
expect(exitCode).toBe(0); expect(exitCode).toBe(0);
const assets = await getAllAssets({}, { headers: asKeyAuth(key) }); const assets = await getAssetStatistics({}, { headers: asKeyAuth(key) });
expect(assets.length).toBe(0); expect(assets.total).toBe(0);
}); });
it('dry run should handle duplicates', async () => { it('dry run should handle duplicates', async () => {
@@ -88,8 +88,8 @@ describe(`immich upload`, () => {
); );
expect(first.exitCode).toBe(0); expect(first.exitCode).toBe(0);
const assets = await getAllAssets({}, { headers: asKeyAuth(key) }); const assets = await getAssetStatistics({}, { headers: asKeyAuth(key) });
expect(assets.length).toBe(1); expect(assets.total).toBe(1);
const second = await immichCli(['upload', `${testAssetDir}/albums/nature/`, '--dry-run']); const second = await immichCli(['upload', `${testAssetDir}/albums/nature/`, '--dry-run']);
expect(second.stderr).toBe(''); expect(second.stderr).toBe('');
@@ -112,8 +112,8 @@ describe(`immich upload`, () => {
); );
expect(exitCode).toBe(0); expect(exitCode).toBe(0);
const assets = await getAllAssets({}, { headers: asKeyAuth(key) }); const assets = await getAssetStatistics({}, { headers: asKeyAuth(key) });
expect(assets.length).toBe(9); expect(assets.total).toBe(9);
}); });
}); });
@@ -135,8 +135,8 @@ describe(`immich upload`, () => {
expect(stderr).toBe(''); expect(stderr).toBe('');
expect(exitCode).toBe(0); expect(exitCode).toBe(0);
const assets = await getAllAssets({}, { headers: asKeyAuth(key) }); const assets = await getAssetStatistics({}, { headers: asKeyAuth(key) });
expect(assets.length).toBe(9); expect(assets.total).toBe(9);
const albums = await getAllAlbums({}, { headers: asKeyAuth(key) }); const albums = await getAllAlbums({}, { headers: asKeyAuth(key) });
expect(albums.length).toBe(1); expect(albums.length).toBe(1);
@@ -151,8 +151,8 @@ describe(`immich upload`, () => {
expect(response1.stderr).toBe(''); expect(response1.stderr).toBe('');
expect(response1.exitCode).toBe(0); expect(response1.exitCode).toBe(0);
const assets1 = await getAllAssets({}, { headers: asKeyAuth(key) }); const assets1 = await getAssetStatistics({}, { headers: asKeyAuth(key) });
expect(assets1.length).toBe(9); expect(assets1.total).toBe(9);
const albums1 = await getAllAlbums({}, { headers: asKeyAuth(key) }); const albums1 = await getAllAlbums({}, { headers: asKeyAuth(key) });
expect(albums1.length).toBe(0); expect(albums1.length).toBe(0);
@@ -167,8 +167,8 @@ describe(`immich upload`, () => {
expect(response2.stderr).toBe(''); expect(response2.stderr).toBe('');
expect(response2.exitCode).toBe(0); expect(response2.exitCode).toBe(0);
const assets2 = await getAllAssets({}, { headers: asKeyAuth(key) }); const assets2 = await getAssetStatistics({}, { headers: asKeyAuth(key) });
expect(assets2.length).toBe(9); expect(assets2.total).toBe(9);
const albums2 = await getAllAlbums({}, { headers: asKeyAuth(key) }); const albums2 = await getAllAlbums({}, { headers: asKeyAuth(key) });
expect(albums2.length).toBe(1); expect(albums2.length).toBe(1);
@@ -193,8 +193,8 @@ describe(`immich upload`, () => {
expect(stderr).toBe(''); expect(stderr).toBe('');
expect(exitCode).toBe(0); expect(exitCode).toBe(0);
const assets = await getAllAssets({}, { headers: asKeyAuth(key) }); const assets = await getAssetStatistics({}, { headers: asKeyAuth(key) });
expect(assets.length).toBe(0); expect(assets.total).toBe(0);
const albums = await getAllAlbums({}, { headers: asKeyAuth(key) }); const albums = await getAllAlbums({}, { headers: asKeyAuth(key) });
expect(albums.length).toBe(0); expect(albums.length).toBe(0);
@@ -219,8 +219,8 @@ describe(`immich upload`, () => {
expect(stderr).toBe(''); expect(stderr).toBe('');
expect(exitCode).toBe(0); expect(exitCode).toBe(0);
const assets = await getAllAssets({}, { headers: asKeyAuth(key) }); const assets = await getAssetStatistics({}, { headers: asKeyAuth(key) });
expect(assets.length).toBe(9); expect(assets.total).toBe(9);
const albums = await getAllAlbums({}, { headers: asKeyAuth(key) }); const albums = await getAllAlbums({}, { headers: asKeyAuth(key) });
expect(albums.length).toBe(1); expect(albums.length).toBe(1);
@@ -245,8 +245,8 @@ describe(`immich upload`, () => {
expect(stderr).toBe(''); expect(stderr).toBe('');
expect(exitCode).toBe(0); expect(exitCode).toBe(0);
const assets = await getAllAssets({}, { headers: asKeyAuth(key) }); const assets = await getAssetStatistics({}, { headers: asKeyAuth(key) });
expect(assets.length).toBe(0); expect(assets.total).toBe(0);
const albums = await getAllAlbums({}, { headers: asKeyAuth(key) }); const albums = await getAllAlbums({}, { headers: asKeyAuth(key) });
expect(albums.length).toBe(0); expect(albums.length).toBe(0);
@@ -276,8 +276,8 @@ describe(`immich upload`, () => {
expect(stderr).toBe(''); expect(stderr).toBe('');
expect(exitCode).toBe(0); expect(exitCode).toBe(0);
const assets = await getAllAssets({}, { headers: asKeyAuth(key) }); const assets = await getAssetStatistics({}, { headers: asKeyAuth(key) });
expect(assets.length).toBe(9); expect(assets.total).toBe(9);
}); });
it('should have accurate dry run', async () => { it('should have accurate dry run', async () => {
@@ -302,8 +302,8 @@ describe(`immich upload`, () => {
expect(stderr).toBe(''); expect(stderr).toBe('');
expect(exitCode).toBe(0); expect(exitCode).toBe(0);
const assets = await getAllAssets({}, { headers: asKeyAuth(key) }); const assets = await getAssetStatistics({}, { headers: asKeyAuth(key) });
expect(assets.length).toBe(0); expect(assets.total).toBe(0);
}); });
}); });
@@ -328,8 +328,8 @@ describe(`immich upload`, () => {
); );
expect(exitCode).toBe(0); expect(exitCode).toBe(0);
const assets = await getAllAssets({}, { headers: asKeyAuth(key) }); const assets = await getAssetStatistics({}, { headers: asKeyAuth(key) });
expect(assets.length).toBe(1); expect(assets.total).toBe(1);
}); });
it('should throw an error if attempting dry run', async () => { it('should throw an error if attempting dry run', async () => {
@@ -344,8 +344,8 @@ describe(`immich upload`, () => {
expect(stderr).toEqual(`error: option '-n, --dry-run' cannot be used with option '-h, --skip-hash'`); expect(stderr).toEqual(`error: option '-n, --dry-run' cannot be used with option '-h, --skip-hash'`);
expect(exitCode).not.toBe(0); expect(exitCode).not.toBe(0);
const assets = await getAllAssets({}, { headers: asKeyAuth(key) }); const assets = await getAssetStatistics({}, { headers: asKeyAuth(key) });
expect(assets.length).toBe(0); expect(assets.total).toBe(0);
}); });
}); });
@@ -367,8 +367,8 @@ describe(`immich upload`, () => {
); );
expect(exitCode).toBe(0); expect(exitCode).toBe(0);
const assets = await getAllAssets({}, { headers: asKeyAuth(key) }); const assets = await getAssetStatistics({}, { headers: asKeyAuth(key) });
expect(assets.length).toBe(9); expect(assets.total).toBe(9);
}); });
it('should reject string argument', async () => { it('should reject string argument', async () => {
@@ -408,8 +408,8 @@ describe(`immich upload`, () => {
); );
expect(exitCode).toBe(0); expect(exitCode).toBe(0);
const assets = await getAllAssets({}, { headers: asKeyAuth(key) }); const assets = await getAssetStatistics({}, { headers: asKeyAuth(key) });
expect(assets.length).toBe(8); expect(assets.total).toBe(8);
}); });
it('should ignore assets matching glob pattern', async () => { it('should ignore assets matching glob pattern', async () => {
@@ -429,8 +429,8 @@ describe(`immich upload`, () => {
); );
expect(exitCode).toBe(0); expect(exitCode).toBe(0);
const assets = await getAllAssets({}, { headers: asKeyAuth(key) }); const assets = await getAssetStatistics({}, { headers: asKeyAuth(key) });
expect(assets.length).toBe(1); expect(assets.total).toBe(1);
}); });
it('should have accurate dry run', async () => { it('should have accurate dry run', async () => {
@@ -451,8 +451,8 @@ describe(`immich upload`, () => {
); );
expect(exitCode).toBe(0); expect(exitCode).toBe(0);
const assets = await getAllAssets({}, { headers: asKeyAuth(key) }); const assets = await getAssetStatistics({}, { headers: asKeyAuth(key) });
expect(assets.length).toBe(0); expect(assets.total).toBe(0);
}); });
}); });
}); });

View File

@@ -5,19 +5,19 @@ import {
CreateAlbumDto, CreateAlbumDto,
CreateAssetDto, CreateAssetDto,
CreateLibraryDto, CreateLibraryDto,
CreateUserDto,
MetadataSearchDto, MetadataSearchDto,
PersonCreateDto, PersonCreateDto,
SharedLinkCreateDto, SharedLinkCreateDto,
UserAdminCreateDto,
ValidateLibraryDto, ValidateLibraryDto,
createAlbum, createAlbum,
createApiKey, createApiKey,
createLibrary, createLibrary,
createPartner,
createPerson, createPerson,
createSharedLink, createSharedLink,
createUser, createUserAdmin,
deleteAssets, deleteAssets,
getAllAssets,
getAllJobsStatus, getAllJobsStatus,
getAssetInfo, getAssetInfo,
getConfigDefaults, getConfigDefaults,
@@ -273,8 +273,8 @@ export const utils = {
return response; return response;
}, },
userSetup: async (accessToken: string, dto: CreateUserDto) => { userSetup: async (accessToken: string, dto: UserAdminCreateDto) => {
await createUser({ createUserDto: dto }, { headers: asBearerAuth(accessToken) }); await createUserAdmin({ userAdminCreateDto: dto }, { headers: asBearerAuth(accessToken) });
return login({ return login({
loginCredentialDto: { email: dto.email, password: dto.password }, loginCredentialDto: { email: dto.email, password: dto.password },
}); });
@@ -340,8 +340,6 @@ export const utils = {
getAssetInfo: (accessToken: string, id: string) => getAssetInfo({ id }, { headers: asBearerAuth(accessToken) }), getAssetInfo: (accessToken: string, id: string) => getAssetInfo({ id }, { headers: asBearerAuth(accessToken) }),
getAllAssets: (accessToken: string) => getAllAssets({}, { headers: asBearerAuth(accessToken) }),
metadataSearch: async (accessToken: string, dto: MetadataSearchDto) => { metadataSearch: async (accessToken: string, dto: MetadataSearchDto) => {
return searchMetadata({ metadataSearchDto: dto }, { headers: asBearerAuth(accessToken) }); return searchMetadata({ metadataSearchDto: dto }, { headers: asBearerAuth(accessToken) });
}, },
@@ -388,6 +386,8 @@ export const utils = {
validateLibrary: (accessToken: string, id: string, dto: ValidateLibraryDto) => validateLibrary: (accessToken: string, id: string, dto: ValidateLibraryDto) =>
validate({ id, validateLibraryDto: dto }, { headers: asBearerAuth(accessToken) }), validate({ id, validateLibraryDto: dto }, { headers: asBearerAuth(accessToken) }),
createPartner: (accessToken: string, id: string) => createPartner({ id }, { headers: asBearerAuth(accessToken) }),
setAuthCookies: async (context: BrowserContext, accessToken: string) => setAuthCookies: async (context: BrowserContext, accessToken: string) =>
await context.addCookies([ await context.addCookies([
{ {

View File

@@ -0,0 +1,60 @@
import { AssetFileUploadResponseDto, LoginResponseDto, SharedLinkType } from '@immich/sdk';
import { expect, test } from '@playwright/test';
import { utils } from 'src/utils';
test.describe('Detail Panel', () => {
let admin: LoginResponseDto;
let asset: AssetFileUploadResponseDto;
test.beforeAll(async () => {
utils.initSdk();
await utils.resetDatabase();
admin = await utils.adminSetup();
asset = await utils.createAsset(admin.accessToken);
});
test('can be opened for shared links', async ({ page }) => {
const sharedLink = await utils.createSharedLink(admin.accessToken, {
type: SharedLinkType.Individual,
assetIds: [asset.id],
});
await page.goto(`/share/${sharedLink.key}/photos/${asset.id}`);
await page.waitForSelector('#immich-asset-viewer');
await expect(page.getByRole('button', { name: 'Info' })).toBeVisible();
await page.keyboard.press('i');
await expect(page.locator('#detail-panel')).toBeVisible();
await page.keyboard.press('i');
await expect(page.locator('#detail-panel')).toHaveCount(0);
});
test('cannot be opened for shared links with hidden metadata', async ({ page }) => {
const sharedLink = await utils.createSharedLink(admin.accessToken, {
type: SharedLinkType.Individual,
assetIds: [asset.id],
showMetadata: false,
});
await page.goto(`/share/${sharedLink.key}/photos/${asset.id}`);
await page.waitForSelector('#immich-asset-viewer');
await expect(page.getByRole('button', { name: 'Info' })).toHaveCount(0);
await page.keyboard.press('i');
await expect(page.locator('#detail-panel')).toHaveCount(0);
await page.keyboard.press('i');
await expect(page.locator('#detail-panel')).toHaveCount(0);
});
test('description is visible for owner on shared links', async ({ context, page }) => {
const sharedLink = await utils.createSharedLink(admin.accessToken, {
type: SharedLinkType.Individual,
assetIds: [asset.id],
});
await utils.setAuthCookies(context, admin.accessToken);
await page.goto(`/share/${sharedLink.key}/photos/${asset.id}`);
const textarea = page.getByRole('textbox', { name: 'Add a description' });
await page.getByRole('button', { name: 'Info' }).click();
await expect(textarea).toBeVisible();
await expect(textarea).not.toBeDisabled();
});
});

View File

@@ -0,0 +1,52 @@
import { AssetFileUploadResponseDto, LoginResponseDto, SharedLinkType } from '@immich/sdk';
import { expect, test } from '@playwright/test';
import { utils } from 'src/utils';
test.describe('Asset Viewer Navbar', () => {
let admin: LoginResponseDto;
let asset: AssetFileUploadResponseDto;
test.beforeAll(async () => {
utils.initSdk();
await utils.resetDatabase();
admin = await utils.adminSetup();
asset = await utils.createAsset(admin.accessToken);
});
test.describe('shared link without metadata', () => {
test('visible guest actions', async ({ page }) => {
const sharedLink = await utils.createSharedLink(admin.accessToken, {
type: SharedLinkType.Individual,
assetIds: [asset.id],
showMetadata: false,
});
await page.goto(`/share/${sharedLink.key}/photos/${asset.id}`);
await page.waitForSelector('#immich-asset-viewer');
const expected = ['Zoom Image', 'Copy Image', 'Download'];
const buttons = await page.getByTestId('asset-viewer-navbar-actions').getByRole('button').all();
for (const [i, button] of buttons.entries()) {
await expect(button).toHaveAccessibleName(expected[i]);
}
});
test('visible owner actions', async ({ context, page }) => {
const sharedLink = await utils.createSharedLink(admin.accessToken, {
type: SharedLinkType.Individual,
assetIds: [asset.id],
showMetadata: false,
});
await utils.setAuthCookies(context, admin.accessToken);
await page.goto(`/share/${sharedLink.key}/photos/${asset.id}`);
await page.waitForSelector('#immich-asset-viewer');
const expected = ['Share', 'Zoom Image', 'Copy Image', 'Download'];
const buttons = await page.getByTestId('asset-viewer-navbar-actions').getByRole('button').all();
for (const [i, button] of buttons.entries()) {
await expect(button).toHaveAccessibleName(expected[i]);
}
});
});
});

View File

@@ -95,3 +95,5 @@ COPY start.sh log_conf.json ./
COPY app . COPY app .
ENTRYPOINT ["tini", "--"] ENTRYPOINT ["tini", "--"]
CMD ["./start.sh"] CMD ["./start.sh"]
HEALTHCHECK CMD python3 healthcheck.py

View File

@@ -0,0 +1,14 @@
import os
import sys
import requests
port = os.getenv("IMMICH_PORT", 3003)
try:
response = requests.get(f"http://localhost:{port}/ping", timeout=2)
if response.status_code == 200:
sys.exit(0)
sys.exit(1)
except requests.RequestException:
sys.exit(1)

View File

@@ -62,6 +62,8 @@ fi
if [ "$CURRENT_SERVER" != "$NEXT_SERVER" ]; then if [ "$CURRENT_SERVER" != "$NEXT_SERVER" ]; then
echo "Pumping Server: $CURRENT_SERVER => $NEXT_SERVER" echo "Pumping Server: $CURRENT_SERVER => $NEXT_SERVER"
npm --prefix server version "$SERVER_PUMP" npm --prefix server version "$SERVER_PUMP"
npm --prefix server ci
npm --prefix server run build
make open-api make open-api
npm --prefix open-api/typescript-sdk version "$SERVER_PUMP" npm --prefix open-api/typescript-sdk version "$SERVER_PUMP"
npm --prefix web version "$SERVER_PUMP" npm --prefix web version "$SERVER_PUMP"

View File

@@ -10,7 +10,7 @@ GEM
artifactory (3.0.17) artifactory (3.0.17)
atomos (0.1.3) atomos (0.1.3)
aws-eventstream (1.3.0) aws-eventstream (1.3.0)
aws-partitions (1.931.0) aws-partitions (1.932.0)
aws-sdk-core (3.196.1) aws-sdk-core (3.196.1)
aws-eventstream (~> 1, >= 1.3.0) aws-eventstream (~> 1, >= 1.3.0)
aws-partitions (~> 1, >= 1.651.0) aws-partitions (~> 1, >= 1.651.0)

View File

@@ -10,7 +10,7 @@ GEM
artifactory (3.0.17) artifactory (3.0.17)
atomos (0.1.3) atomos (0.1.3)
aws-eventstream (1.3.0) aws-eventstream (1.3.0)
aws-partitions (1.931.0) aws-partitions (1.932.0)
aws-sdk-core (3.196.1) aws-sdk-core (3.196.1)
aws-eventstream (~> 1, >= 1.3.0) aws-eventstream (~> 1, >= 1.3.0)
aws-partitions (~> 1, >= 1.651.0) aws-partitions (~> 1, >= 1.651.0)

View File

@@ -145,9 +145,10 @@ class Album {
.remoteIdEqualTo(dto.albumThumbnailAssetId) .remoteIdEqualTo(dto.albumThumbnailAssetId)
.findFirst(); .findFirst();
} }
if (dto.sharedUsers.isNotEmpty) { if (dto.albumUsers.isNotEmpty) {
final users = await db.users final users = await db.users.getAllById(
.getAllById(dto.sharedUsers.map((e) => e.id).toList(growable: false)); dto.albumUsers.map((e) => e.user.id).toList(growable: false),
);
a.sharedUsers.addAll(users.cast()); a.sharedUsers.addAll(users.cast());
} }
if (dto.assets.isNotEmpty) { if (dto.assets.isNotEmpty) {

View File

@@ -27,7 +27,7 @@ class User {
Id get isarId => fastHash(id); Id get isarId => fastHash(id);
User.fromUserDto(UserResponseDto dto) User.fromUserDto(UserAdminResponseDto dto)
: id = dto.id, : id = dto.id,
updatedAt = dto.updatedAt, updatedAt = dto.updatedAt,
email = dto.email, email = dto.email,
@@ -44,21 +44,21 @@ class User {
User.fromPartnerDto(PartnerResponseDto dto) User.fromPartnerDto(PartnerResponseDto dto)
: id = dto.id, : id = dto.id,
updatedAt = dto.updatedAt, updatedAt = DateTime.now(),
email = dto.email, email = dto.email,
name = dto.name, name = dto.name,
isPartnerSharedBy = false, isPartnerSharedBy = false,
isPartnerSharedWith = false, isPartnerSharedWith = false,
profileImagePath = dto.profileImagePath, profileImagePath = dto.profileImagePath,
isAdmin = dto.isAdmin, isAdmin = false,
memoryEnabled = dto.memoriesEnabled ?? false, memoryEnabled = false,
avatarColor = dto.avatarColor.toAvatarColor(), avatarColor = dto.avatarColor.toAvatarColor(),
inTimeline = dto.inTimeline ?? false, inTimeline = dto.inTimeline ?? false,
quotaUsageInBytes = dto.quotaUsageInBytes ?? 0, quotaUsageInBytes = 0,
quotaSizeInBytes = dto.quotaSizeInBytes ?? 0; quotaSizeInBytes = 0;
/// Base user dto used where the complete user object is not required /// Base user dto used where the complete user object is not required
User.fromSimpleUserDto(UserDto dto) User.fromSimpleUserDto(UserResponseDto dto)
: id = dto.id, : id = dto.id,
email = dto.email, email = dto.email,
name = dto.name, name = dto.name,

View File

@@ -32,7 +32,7 @@ class ServerDiskInfo {
return 'ServerDiskInfo(diskAvailable: $diskAvailable, diskSize: $diskSize, diskUse: $diskUse, diskUsagePercentage: $diskUsagePercentage)'; return 'ServerDiskInfo(diskAvailable: $diskAvailable, diskSize: $diskSize, diskUse: $diskUse, diskUsagePercentage: $diskUsagePercentage)';
} }
ServerDiskInfo.fromDto(ServerInfoResponseDto dto) ServerDiskInfo.fromDto(ServerStorageResponseDto dto)
: diskAvailable = dto.diskAvailable, : diskAvailable = dto.diskAvailable,
diskSize = dto.diskSize, diskSize = dto.diskSize,
diskUse = dto.diskUse, diskUse = dto.diskUse,

View File

@@ -133,7 +133,7 @@ class AlbumViewerPage extends HookConsumerWidget {
Widget buildTitle(Album album) { Widget buildTitle(Album album) {
return Padding( return Padding(
padding: const EdgeInsets.only(left: 8, right: 8, top: 24), padding: const EdgeInsets.only(left: 8, right: 8),
child: userId == album.ownerId && album.isRemote child: userId == album.ownerId && album.isRemote
? AlbumViewerEditableTitle( ? AlbumViewerEditableTitle(
album: album, album: album,
@@ -228,9 +228,30 @@ class AlbumViewerPage extends HookConsumerWidget {
} }
return Scaffold( return Scaffold(
appBar: ref.watch(multiselectProvider) body: Stack(
? null children: [
: album.when( album.widgetWhen(
onData: (data) => MultiselectGrid(
renderListProvider: albumRenderlistProvider(albumId),
topWidget: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
buildHeader(data),
if (data.isRemote) buildControlButton(data),
],
),
onRemoveFromAlbum: onRemoveFromAlbumPressed,
editEnabled: data.ownerId == userId,
),
),
AnimatedPositioned(
duration: const Duration(milliseconds: 300),
top: ref.watch(multiselectProvider)
? -(kToolbarHeight + MediaQuery.of(context).padding.top)
: 0,
left: 0,
right: 0,
child: album.when(
data: (data) => AlbumViewerAppbar( data: (data) => AlbumViewerAppbar(
titleFocusNode: titleFocusNode, titleFocusNode: titleFocusNode,
album: data, album: data,
@@ -242,19 +263,8 @@ class AlbumViewerPage extends HookConsumerWidget {
error: (error, stackTrace) => AppBar(title: const Text("Error")), error: (error, stackTrace) => AppBar(title: const Text("Error")),
loading: () => AppBar(), loading: () => AppBar(),
), ),
body: album.widgetWhen(
onData: (data) => MultiselectGrid(
renderListProvider: albumRenderlistProvider(albumId),
topWidget: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
buildHeader(data),
if (data.isRemote) buildControlButton(data),
],
), ),
onRemoveFromAlbum: onRemoveFromAlbumPressed, ],
editEnabled: data.ownerId == userId,
),
), ),
); );
} }

View File

@@ -138,11 +138,9 @@ class AuthenticationNotifier extends StateNotifier<AuthenticationState> {
Future<bool> changePassword(String newPassword) async { Future<bool> changePassword(String newPassword) async {
try { try {
await _apiService.userApi.updateUser( await _apiService.userApi.updateMyUser(
UpdateUserDto( UserUpdateMeDto(
id: state.userId,
password: newPassword, password: newPassword,
shouldChangePassword: false,
), ),
); );
@@ -178,9 +176,9 @@ class AuthenticationNotifier extends StateNotifier<AuthenticationState> {
user = offlineUser; user = offlineUser;
retResult = false; retResult = false;
} else { } else {
UserResponseDto? userResponseDto; UserAdminResponseDto? userResponseDto;
try { try {
userResponseDto = await _apiService.userApi.getMyUserInfo(); userResponseDto = await _apiService.userApi.getMyUser();
} on ApiException catch (error, stackTrace) { } on ApiException catch (error, stackTrace) {
_log.severe( _log.severe(
"Error getting user information from the server [API EXCEPTION]", "Error getting user information from the server [API EXCEPTION]",

View File

@@ -374,7 +374,7 @@ class BackupNotifier extends StateNotifier<BackUpState> {
if (state.backupProgress != BackUpProgressEnum.inBackground) { if (state.backupProgress != BackUpProgressEnum.inBackground) {
await _getBackupAlbumsInfo(); await _getBackupAlbumsInfo();
await updateServerInfo(); await updateDiskInfo();
await _updateBackupAssetCount(); await _updateBackupAssetCount();
} else { } else {
log.warning("cannot get backup info - background backup is in progress!"); log.warning("cannot get backup info - background backup is in progress!");
@@ -542,7 +542,7 @@ class BackupNotifier extends StateNotifier<BackUpState> {
_updatePersistentAlbumsSelection(); _updatePersistentAlbumsSelection();
} }
updateServerInfo(); updateDiskInfo();
} }
void _onUploadProgress(int sent, int total) { void _onUploadProgress(int sent, int total) {
@@ -579,13 +579,13 @@ class BackupNotifier extends StateNotifier<BackUpState> {
); );
} }
Future<void> updateServerInfo() async { Future<void> updateDiskInfo() async {
final serverInfo = await _serverInfoService.getServerInfo(); final diskInfo = await _serverInfoService.getDiskInfo();
// Update server info // Update server info
if (serverInfo != null) { if (diskInfo != null) {
state = state.copyWith( state = state.copyWith(
serverInfo: serverInfo, serverInfo: diskInfo,
); );
} }
} }

View File

@@ -121,7 +121,7 @@ class ManualUploadNotifier extends StateNotifier<ManualUploadState> {
bool isDuplicated, bool isDuplicated,
) { ) {
state = state.copyWith(successfulUploads: state.successfulUploads + 1); state = state.copyWith(successfulUploads: state.successfulUploads + 1);
_backupProvider.updateServerInfo(); _backupProvider.updateDiskInfo();
} }
void _onAssetUploadError(ErrorUploadAsset errorAssetInfo) { void _onAssetUploadError(ErrorUploadAsset errorAssetInfo) {

View File

@@ -20,7 +20,7 @@ class CurrentUserProvider extends StateNotifier<User?> {
refresh() async { refresh() async {
try { try {
final user = await _apiService.userApi.getMyUserInfo(); final user = await _apiService.userApi.getMyUser();
if (user != null) { if (user != null) {
Store.put( Store.put(
StoreKey.currentUser, StoreKey.currentUser,

View File

@@ -57,7 +57,7 @@ class TabNavigationObserver extends AutoRouterObserver {
// Update user info // Update user info
try { try {
final userResponseDto = final userResponseDto =
await ref.read(apiServiceProvider).userApi.getMyUserInfo(); await ref.read(apiServiceProvider).userApi.getMyUser();
if (userResponseDto == null) { if (userResponseDto == null) {
return; return;

View File

@@ -180,7 +180,14 @@ class AlbumService {
CreateAlbumDto( CreateAlbumDto(
albumName: albumName, albumName: albumName,
assetIds: assets.map((asset) => asset.remoteId!).toList(), assetIds: assets.map((asset) => asset.remoteId!).toList(),
sharedWithUserIds: sharedUsers.map((e) => e.id).toList(), albumUsers: sharedUsers
.map(
(e) => AlbumUserCreateDto(
userId: e.id,
role: AlbumUserRole.editor,
),
)
.toList(),
), ),
); );
if (remote != null) { if (remote != null) {

View File

@@ -84,7 +84,7 @@ class AssetService {
final AssetResponseDto? dto = final AssetResponseDto? dto =
await _apiService.assetApi.getAssetInfo(remoteId); await _apiService.assetApi.getAssetInfo(remoteId);
return dto?.people; return dto?.people?.visiblePeople;
} catch (error, stack) { } catch (error, stack) {
log.severe( log.severe(
'Error while getting remote asset info: ${error.toString()}', 'Error while getting remote asset info: ${error.toString()}',

View File

@@ -20,6 +20,7 @@ import 'package:immich_mobile/entities/store.entity.dart';
import 'package:immich_mobile/services/api.service.dart'; import 'package:immich_mobile/services/api.service.dart';
import 'package:immich_mobile/utils/backup_progress.dart'; import 'package:immich_mobile/utils/backup_progress.dart';
import 'package:immich_mobile/utils/diff.dart'; import 'package:immich_mobile/utils/diff.dart';
import 'package:immich_mobile/utils/http_ssl_cert_override.dart';
import 'package:isar/isar.dart'; import 'package:isar/isar.dart';
import 'package:path_provider_ios/path_provider_ios.dart'; import 'package:path_provider_ios/path_provider_ios.dart';
import 'package:photo_manager/photo_manager.dart'; import 'package:photo_manager/photo_manager.dart';
@@ -590,6 +591,7 @@ enum IosBackgroundTask { fetch, processing }
/// entry point called by Kotlin/Java code; needs to be a top-level function /// entry point called by Kotlin/Java code; needs to be a top-level function
@pragma('vm:entry-point') @pragma('vm:entry-point')
void _nativeEntry() { void _nativeEntry() {
HttpOverrides.global = HttpSSLCertOverride();
WidgetsFlutterBinding.ensureInitialized(); WidgetsFlutterBinding.ensureInitialized();
DartPluginRegistrant.ensureInitialized(); DartPluginRegistrant.ensureInitialized();
BackgroundService backgroundService = BackgroundService(); BackgroundService backgroundService = BackgroundService();

View File

@@ -8,6 +8,8 @@ import 'package:isar/isar.dart';
import 'package:logging/logging.dart'; import 'package:logging/logging.dart';
import 'package:openapi/api.dart'; import 'package:openapi/api.dart';
import '../utils/string_helper.dart';
final memoryServiceProvider = StateProvider<MemoryService>((ref) { final memoryServiceProvider = StateProvider<MemoryService>((ref) {
return MemoryService( return MemoryService(
ref.watch(apiServiceProvider), ref.watch(apiServiceProvider),
@@ -36,13 +38,13 @@ class MemoryService {
} }
List<Memory> memories = []; List<Memory> memories = [];
for (final MemoryLaneResponseDto(:title, :assets) in data) { for (final MemoryLaneResponseDto(:yearsAgo, :assets) in data) {
final dbAssets = final dbAssets =
await _db.assets.getAllByRemoteId(assets.map((e) => e.id)); await _db.assets.getAllByRemoteId(assets.map((e) => e.id));
if (dbAssets.isNotEmpty) { if (dbAssets.isNotEmpty) {
memories.add( memories.add(
Memory( Memory(
title: title, title: '$yearsAgo year${s(yearsAgo)} ago',
assets: dbAssets, assets: dbAssets,
), ),
); );

View File

@@ -18,14 +18,14 @@ class ServerInfoService {
ServerInfoService(this._apiService); ServerInfoService(this._apiService);
Future<ServerDiskInfo?> getServerInfo() async { Future<ServerDiskInfo?> getDiskInfo() async {
try { try {
final dto = await _apiService.serverInfoApi.getServerInfo(); final dto = await _apiService.serverInfoApi.getStorage();
if (dto != null) { if (dto != null) {
return ServerDiskInfo.fromDto(dto); return ServerDiskInfo.fromDto(dto);
} }
} catch (e) { } catch (e) {
debugPrint("Error [getServerInfo] ${e.toString()}"); debugPrint("Error [getDiskInfo] ${e.toString()}");
} }
return null; return null;
} }

View File

@@ -362,15 +362,15 @@ class SyncService {
// update shared users // update shared users
final List<User> sharedUsers = album.sharedUsers.toList(growable: false); final List<User> sharedUsers = album.sharedUsers.toList(growable: false);
sharedUsers.sort((a, b) => a.id.compareTo(b.id)); sharedUsers.sort((a, b) => a.id.compareTo(b.id));
dto.sharedUsers.sort((a, b) => a.id.compareTo(b.id)); dto.albumUsers.sort((a, b) => a.user.id.compareTo(b.user.id));
final List<String> userIdsToAdd = []; final List<String> userIdsToAdd = [];
final List<User> usersToUnlink = []; final List<User> usersToUnlink = [];
diffSortedListsSync( diffSortedListsSync(
dto.sharedUsers, dto.albumUsers,
sharedUsers, sharedUsers,
compare: (UserResponseDto a, User b) => a.id.compareTo(b.id), compare: (AlbumUserResponseDto a, User b) => a.user.id.compareTo(b.id),
both: (a, b) => false, both: (a, b) => false,
onlyFirst: (UserResponseDto a) => userIdsToAdd.add(a.id), onlyFirst: (AlbumUserResponseDto a) => userIdsToAdd.add(a.user.id),
onlySecond: (User a) => usersToUnlink.add(a), onlySecond: (User a) => usersToUnlink.add(a),
); );
@@ -905,7 +905,7 @@ bool _hasAlbumResponseDtoChanged(AlbumResponseDto dto, Album a) {
dto.albumName != a.name || dto.albumName != a.name ||
dto.albumThumbnailAssetId != a.thumbnail.value?.remoteId || dto.albumThumbnailAssetId != a.thumbnail.value?.remoteId ||
dto.shared != a.shared || dto.shared != a.shared ||
dto.sharedUsers.length != a.sharedUsers.length || dto.albumUsers.length != a.sharedUsers.length ||
!dto.updatedAt.isAtSameMomentAs(a.modifiedAt) || !dto.updatedAt.isAtSameMomentAs(a.modifiedAt) ||
!isAtSameMomentAs(dto.startDate, a.startDate) || !isAtSameMomentAs(dto.startDate, a.startDate) ||
!isAtSameMomentAs(dto.endDate, a.endDate) || !isAtSameMomentAs(dto.endDate, a.endDate) ||

View File

@@ -37,10 +37,10 @@ class UserService {
this._partnerService, this._partnerService,
); );
Future<List<User>?> _getAllUsers({required bool isAll}) async { Future<List<User>?> _getAllUsers() async {
try { try {
final dto = await _apiService.userApi.getAllUsers(isAll); final dto = await _apiService.userApi.searchUsers();
return dto?.map(User.fromUserDto).toList(); return dto?.map(User.fromSimpleUserDto).toList();
} catch (e) { } catch (e) {
_log.warning("Failed get all users", e); _log.warning("Failed get all users", e);
return null; return null;
@@ -71,7 +71,7 @@ class UserService {
} }
Future<List<User>?> getUsersFromServer() async { Future<List<User>?> getUsersFromServer() async {
final List<User>? users = await _getAllUsers(isAll: true); final List<User>? users = await _getAllUsers();
final List<User>? sharedBy = final List<User>? sharedBy =
await _partnerService.getPartners(PartnerDirection.sharedBy); await _partnerService.getPartners(PartnerDirection.sharedBy);
final List<User>? sharedWith = final List<User>? sharedWith =

View File

@@ -77,5 +77,5 @@ String getThumbnailUrlForRemoteId(
} }
String getFaceThumbnailUrl(final String personId) { String getFaceThumbnailUrl(final String personId) {
return '${Store.get(StoreKey.serverEndpoint)}/person/$personId/thumbnail'; return '${Store.get(StoreKey.serverEndpoint)}/people/$personId/thumbnail';
} }

View File

@@ -3,3 +3,5 @@ extension StringExtension on String {
return "${this[0].toUpperCase()}${substring(1).toLowerCase()}"; return "${this[0].toUpperCase()}${substring(1).toLowerCase()}";
} }
} }
String s(num count) => (count == 1 ? '' : 's');

View File

@@ -36,58 +36,62 @@ class AlbumViewerEditableTitle extends HookConsumerWidget {
[], [],
); );
return TextField( return Material(
onChanged: (value) { color: Colors.transparent,
if (value.isEmpty) { child: TextField(
} else { onChanged: (value) {
ref.watch(albumViewerProvider.notifier).setEditTitleText(value); if (value.isEmpty) {
} } else {
}, ref.watch(albumViewerProvider.notifier).setEditTitleText(value);
focusNode: titleFocusNode, }
style: context.textTheme.headlineMedium, },
controller: titleTextEditController, focusNode: titleFocusNode,
onTap: () { style: context.textTheme.headlineMedium,
FocusScope.of(context).requestFocus(titleFocusNode); controller: titleTextEditController,
onTap: () {
FocusScope.of(context).requestFocus(titleFocusNode);
ref.watch(albumViewerProvider.notifier).setEditTitleText(album.name); ref.watch(albumViewerProvider.notifier).setEditTitleText(album.name);
ref.watch(albumViewerProvider.notifier).enableEditAlbum(); ref.watch(albumViewerProvider.notifier).enableEditAlbum();
if (titleTextEditController.text == 'Untitled') { if (titleTextEditController.text == 'Untitled') {
titleTextEditController.clear(); titleTextEditController.clear();
} }
}, },
decoration: InputDecoration( decoration: InputDecoration(
contentPadding: const EdgeInsets.symmetric(horizontal: 8, vertical: 8), contentPadding:
suffixIcon: titleFocusNode.hasFocus const EdgeInsets.symmetric(horizontal: 8, vertical: 8),
? IconButton( suffixIcon: titleFocusNode.hasFocus
onPressed: () { ? IconButton(
titleTextEditController.clear(); onPressed: () {
}, titleTextEditController.clear();
icon: Icon( },
Icons.cancel_rounded, icon: Icon(
color: context.primaryColor, Icons.cancel_rounded,
), color: context.primaryColor,
splashRadius: 10, ),
) splashRadius: 10,
: null, )
enabledBorder: OutlineInputBorder( : null,
borderSide: const BorderSide(color: Colors.transparent), enabledBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(10), borderSide: const BorderSide(color: Colors.transparent),
), borderRadius: BorderRadius.circular(10),
focusedBorder: OutlineInputBorder( ),
borderSide: const BorderSide(color: Colors.transparent), focusedBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(10), borderSide: const BorderSide(color: Colors.transparent),
), borderRadius: BorderRadius.circular(10),
focusColor: Colors.grey[300], ),
fillColor: context.isDarkTheme focusColor: Colors.grey[300],
? const Color.fromARGB(255, 32, 33, 35) fillColor: context.isDarkTheme
: Colors.grey[200], ? const Color.fromARGB(255, 32, 33, 35)
filled: titleFocusNode.hasFocus, : Colors.grey[200],
hintText: 'share_add_title'.tr(), filled: titleFocusNode.hasFocus,
hintStyle: TextStyle( hintText: 'share_add_title'.tr(),
fontSize: 28, hintStyle: TextStyle(
color: context.isDarkTheme ? Colors.grey[300] : Colors.grey[700], fontSize: 28,
fontWeight: FontWeight.bold, color: context.isDarkTheme ? Colors.grey[300] : Colors.grey[700],
fontWeight: FontWeight.bold,
),
), ),
), ),
); );

View File

@@ -238,8 +238,10 @@ class ImmichAssetGridViewState extends ConsumerState<ImmichAssetGridView> {
} }
bool appBarOffset() { bool appBarOffset() {
return ref.watch(tabProvider).index == 0 && return (ref.watch(tabProvider).index == 0 &&
ModalRoute.of(context)?.settings.name == TabControllerRoute.name; ModalRoute.of(context)?.settings.name ==
TabControllerRoute.name) ||
(ModalRoute.of(context)?.settings.name == AlbumViewerRoute.name);
} }
final listWidget = ScrollablePositionedList.builder( final listWidget = ScrollablePositionedList.builder(

View File

@@ -31,7 +31,7 @@ class ImmichAppBarDialog extends HookConsumerWidget {
useEffect( useEffect(
() { () {
ref.read(backupProvider.notifier).updateServerInfo(); ref.read(backupProvider.notifier).updateDiskInfo();
ref.read(currentUserProvider.notifier).refresh(); ref.read(currentUserProvider.notifier).refresh();
return null; return null;
}, },

View File

@@ -215,7 +215,7 @@ class _ManualPicker extends HookWidget {
decorationText: "location_picker_longitude", decorationText: "location_picker_longitude",
hintText: "location_picker_longitude_hint", hintText: "location_picker_longitude_hint",
errorText: "location_picker_longitude_error", errorText: "location_picker_longitude_error",
focusNode: latitiudeFocusNode, focusNode: longitudeFocusNode,
validator: _validateLong, validator: _validateLong,
onUpdated: onLongitudeEditingCompleted, onUpdated: onLongitudeEditingCompleted,
), ),

View File

@@ -6,7 +6,7 @@ import 'package:immich_mobile/entities/user.entity.dart';
Widget userAvatar(BuildContext context, User u, {double? radius}) { Widget userAvatar(BuildContext context, User u, {double? radius}) {
final url = final url =
"${Store.get(StoreKey.serverEndpoint)}/user/profile-image/${u.id}"; "${Store.get(StoreKey.serverEndpoint)}/users/${u.id}/profile-image";
final nameFirstLetter = u.name.isNotEmpty ? u.name[0] : ""; final nameFirstLetter = u.name.isNotEmpty ? u.name[0] : "";
return CircleAvatar( return CircleAvatar(
radius: radius, radius: radius,

View File

@@ -24,7 +24,7 @@ class UserCircleAvatar extends ConsumerWidget {
Widget build(BuildContext context, WidgetRef ref) { Widget build(BuildContext context, WidgetRef ref) {
bool isDarkTheme = Theme.of(context).brightness == Brightness.dark; bool isDarkTheme = Theme.of(context).brightness == Brightness.dark;
final profileImageUrl = final profileImageUrl =
'${Store.get(StoreKey.serverEndpoint)}/user/profile-image/${user.id}?d=${Random().nextInt(1024)}'; '${Store.get(StoreKey.serverEndpoint)}/users/${user.id}/profile-image?d=${Random().nextInt(1024)}';
final textIcon = Text( final textIcon = Text(
user.name[0].toUpperCase(), user.name[0].toUpperCase(),

171
mobile/openapi/README.md generated
View File

@@ -73,30 +73,29 @@ All URIs are relative to */api*
Class | Method | HTTP request | Description Class | Method | HTTP request | Description
------------ | ------------- | ------------- | ------------- ------------ | ------------- | ------------- | -------------
*APIKeyApi* | [**createApiKey**](doc//APIKeyApi.md#createapikey) | **POST** /api-key | *APIKeyApi* | [**createApiKey**](doc//APIKeyApi.md#createapikey) | **POST** /api-keys |
*APIKeyApi* | [**deleteApiKey**](doc//APIKeyApi.md#deleteapikey) | **DELETE** /api-key/{id} | *APIKeyApi* | [**deleteApiKey**](doc//APIKeyApi.md#deleteapikey) | **DELETE** /api-keys/{id} |
*APIKeyApi* | [**getApiKey**](doc//APIKeyApi.md#getapikey) | **GET** /api-key/{id} | *APIKeyApi* | [**getApiKey**](doc//APIKeyApi.md#getapikey) | **GET** /api-keys/{id} |
*APIKeyApi* | [**getApiKeys**](doc//APIKeyApi.md#getapikeys) | **GET** /api-key | *APIKeyApi* | [**getApiKeys**](doc//APIKeyApi.md#getapikeys) | **GET** /api-keys |
*APIKeyApi* | [**updateApiKey**](doc//APIKeyApi.md#updateapikey) | **PUT** /api-key/{id} | *APIKeyApi* | [**updateApiKey**](doc//APIKeyApi.md#updateapikey) | **PUT** /api-keys/{id} |
*ActivityApi* | [**createActivity**](doc//ActivityApi.md#createactivity) | **POST** /activity | *ActivityApi* | [**createActivity**](doc//ActivityApi.md#createactivity) | **POST** /activities |
*ActivityApi* | [**deleteActivity**](doc//ActivityApi.md#deleteactivity) | **DELETE** /activity/{id} | *ActivityApi* | [**deleteActivity**](doc//ActivityApi.md#deleteactivity) | **DELETE** /activities/{id} |
*ActivityApi* | [**getActivities**](doc//ActivityApi.md#getactivities) | **GET** /activity | *ActivityApi* | [**getActivities**](doc//ActivityApi.md#getactivities) | **GET** /activities |
*ActivityApi* | [**getActivityStatistics**](doc//ActivityApi.md#getactivitystatistics) | **GET** /activity/statistics | *ActivityApi* | [**getActivityStatistics**](doc//ActivityApi.md#getactivitystatistics) | **GET** /activities/statistics |
*AlbumApi* | [**addAssetsToAlbum**](doc//AlbumApi.md#addassetstoalbum) | **PUT** /album/{id}/assets | *AlbumApi* | [**addAssetsToAlbum**](doc//AlbumApi.md#addassetstoalbum) | **PUT** /albums/{id}/assets |
*AlbumApi* | [**addUsersToAlbum**](doc//AlbumApi.md#adduserstoalbum) | **PUT** /album/{id}/users | *AlbumApi* | [**addUsersToAlbum**](doc//AlbumApi.md#adduserstoalbum) | **PUT** /albums/{id}/users |
*AlbumApi* | [**createAlbum**](doc//AlbumApi.md#createalbum) | **POST** /album | *AlbumApi* | [**createAlbum**](doc//AlbumApi.md#createalbum) | **POST** /albums |
*AlbumApi* | [**deleteAlbum**](doc//AlbumApi.md#deletealbum) | **DELETE** /album/{id} | *AlbumApi* | [**deleteAlbum**](doc//AlbumApi.md#deletealbum) | **DELETE** /albums/{id} |
*AlbumApi* | [**getAlbumCount**](doc//AlbumApi.md#getalbumcount) | **GET** /album/count | *AlbumApi* | [**getAlbumCount**](doc//AlbumApi.md#getalbumcount) | **GET** /albums/count |
*AlbumApi* | [**getAlbumInfo**](doc//AlbumApi.md#getalbuminfo) | **GET** /album/{id} | *AlbumApi* | [**getAlbumInfo**](doc//AlbumApi.md#getalbuminfo) | **GET** /albums/{id} |
*AlbumApi* | [**getAllAlbums**](doc//AlbumApi.md#getallalbums) | **GET** /album | *AlbumApi* | [**getAllAlbums**](doc//AlbumApi.md#getallalbums) | **GET** /albums |
*AlbumApi* | [**removeAssetFromAlbum**](doc//AlbumApi.md#removeassetfromalbum) | **DELETE** /album/{id}/assets | *AlbumApi* | [**removeAssetFromAlbum**](doc//AlbumApi.md#removeassetfromalbum) | **DELETE** /albums/{id}/assets |
*AlbumApi* | [**removeUserFromAlbum**](doc//AlbumApi.md#removeuserfromalbum) | **DELETE** /album/{id}/user/{userId} | *AlbumApi* | [**removeUserFromAlbum**](doc//AlbumApi.md#removeuserfromalbum) | **DELETE** /albums/{id}/user/{userId} |
*AlbumApi* | [**updateAlbumInfo**](doc//AlbumApi.md#updatealbuminfo) | **PATCH** /album/{id} | *AlbumApi* | [**updateAlbumInfo**](doc//AlbumApi.md#updatealbuminfo) | **PATCH** /albums/{id} |
*AlbumApi* | [**updateAlbumUser**](doc//AlbumApi.md#updatealbumuser) | **PUT** /album/{id}/user/{userId} | *AlbumApi* | [**updateAlbumUser**](doc//AlbumApi.md#updatealbumuser) | **PUT** /albums/{id}/user/{userId} |
*AssetApi* | [**checkBulkUpload**](doc//AssetApi.md#checkbulkupload) | **POST** /asset/bulk-upload-check | *AssetApi* | [**checkBulkUpload**](doc//AssetApi.md#checkbulkupload) | **POST** /asset/bulk-upload-check |
*AssetApi* | [**checkExistingAssets**](doc//AssetApi.md#checkexistingassets) | **POST** /asset/exist | *AssetApi* | [**checkExistingAssets**](doc//AssetApi.md#checkexistingassets) | **POST** /asset/exist |
*AssetApi* | [**deleteAssets**](doc//AssetApi.md#deleteassets) | **DELETE** /asset | *AssetApi* | [**deleteAssets**](doc//AssetApi.md#deleteassets) | **DELETE** /asset |
*AssetApi* | [**getAllAssets**](doc//AssetApi.md#getallassets) | **GET** /asset |
*AssetApi* | [**getAllUserAssetsByDeviceId**](doc//AssetApi.md#getalluserassetsbydeviceid) | **GET** /asset/device/{deviceId} | *AssetApi* | [**getAllUserAssetsByDeviceId**](doc//AssetApi.md#getalluserassetsbydeviceid) | **GET** /asset/device/{deviceId} |
*AssetApi* | [**getAssetInfo**](doc//AssetApi.md#getassetinfo) | **GET** /asset/{id} | *AssetApi* | [**getAssetInfo**](doc//AssetApi.md#getassetinfo) | **GET** /asset/{id} |
*AssetApi* | [**getAssetStatistics**](doc//AssetApi.md#getassetstatistics) | **GET** /asset/statistics | *AssetApi* | [**getAssetStatistics**](doc//AssetApi.md#getassetstatistics) | **GET** /asset/statistics |
@@ -104,6 +103,7 @@ Class | Method | HTTP request | Description
*AssetApi* | [**getMapMarkers**](doc//AssetApi.md#getmapmarkers) | **GET** /asset/map-marker | *AssetApi* | [**getMapMarkers**](doc//AssetApi.md#getmapmarkers) | **GET** /asset/map-marker |
*AssetApi* | [**getMemoryLane**](doc//AssetApi.md#getmemorylane) | **GET** /asset/memory-lane | *AssetApi* | [**getMemoryLane**](doc//AssetApi.md#getmemorylane) | **GET** /asset/memory-lane |
*AssetApi* | [**getRandom**](doc//AssetApi.md#getrandom) | **GET** /asset/random | *AssetApi* | [**getRandom**](doc//AssetApi.md#getrandom) | **GET** /asset/random |
*AssetApi* | [**replaceAsset**](doc//AssetApi.md#replaceasset) | **PUT** /asset/{id}/file |
*AssetApi* | [**runAssetJobs**](doc//AssetApi.md#runassetjobs) | **POST** /asset/jobs | *AssetApi* | [**runAssetJobs**](doc//AssetApi.md#runassetjobs) | **POST** /asset/jobs |
*AssetApi* | [**serveFile**](doc//AssetApi.md#servefile) | **GET** /asset/file/{id} | *AssetApi* | [**serveFile**](doc//AssetApi.md#servefile) | **GET** /asset/file/{id} |
*AssetApi* | [**updateAsset**](doc//AssetApi.md#updateasset) | **PUT** /asset/{id} | *AssetApi* | [**updateAsset**](doc//AssetApi.md#updateasset) | **PUT** /asset/{id} |
@@ -120,22 +120,23 @@ Class | Method | HTTP request | Description
*DownloadApi* | [**downloadFile**](doc//DownloadApi.md#downloadfile) | **POST** /download/asset/{id} | *DownloadApi* | [**downloadFile**](doc//DownloadApi.md#downloadfile) | **POST** /download/asset/{id} |
*DownloadApi* | [**getDownloadInfo**](doc//DownloadApi.md#getdownloadinfo) | **POST** /download/info | *DownloadApi* | [**getDownloadInfo**](doc//DownloadApi.md#getdownloadinfo) | **POST** /download/info |
*DuplicateApi* | [**getAssetDuplicates**](doc//DuplicateApi.md#getassetduplicates) | **GET** /duplicates | *DuplicateApi* | [**getAssetDuplicates**](doc//DuplicateApi.md#getassetduplicates) | **GET** /duplicates |
*FaceApi* | [**getFaces**](doc//FaceApi.md#getfaces) | **GET** /face | *FaceApi* | [**getFaces**](doc//FaceApi.md#getfaces) | **GET** /faces |
*FaceApi* | [**reassignFacesById**](doc//FaceApi.md#reassignfacesbyid) | **PUT** /face/{id} | *FaceApi* | [**reassignFacesById**](doc//FaceApi.md#reassignfacesbyid) | **PUT** /faces/{id} |
*FileReportApi* | [**fixAuditFiles**](doc//FileReportApi.md#fixauditfiles) | **POST** /report/fix | *FaceApi* | [**unassignFace**](doc//FaceApi.md#unassignface) | **DELETE** /faces/{id} |
*FileReportApi* | [**getAuditFiles**](doc//FileReportApi.md#getauditfiles) | **GET** /report | *FileReportApi* | [**fixAuditFiles**](doc//FileReportApi.md#fixauditfiles) | **POST** /reports/fix |
*FileReportApi* | [**getFileChecksums**](doc//FileReportApi.md#getfilechecksums) | **POST** /report/checksum | *FileReportApi* | [**getAuditFiles**](doc//FileReportApi.md#getauditfiles) | **GET** /reports |
*FileReportApi* | [**getFileChecksums**](doc//FileReportApi.md#getfilechecksums) | **POST** /reports/checksum |
*JobApi* | [**getAllJobsStatus**](doc//JobApi.md#getalljobsstatus) | **GET** /jobs | *JobApi* | [**getAllJobsStatus**](doc//JobApi.md#getalljobsstatus) | **GET** /jobs |
*JobApi* | [**sendJobCommand**](doc//JobApi.md#sendjobcommand) | **PUT** /jobs/{id} | *JobApi* | [**sendJobCommand**](doc//JobApi.md#sendjobcommand) | **PUT** /jobs/{id} |
*LibraryApi* | [**createLibrary**](doc//LibraryApi.md#createlibrary) | **POST** /library | *LibraryApi* | [**createLibrary**](doc//LibraryApi.md#createlibrary) | **POST** /libraries |
*LibraryApi* | [**deleteLibrary**](doc//LibraryApi.md#deletelibrary) | **DELETE** /library/{id} | *LibraryApi* | [**deleteLibrary**](doc//LibraryApi.md#deletelibrary) | **DELETE** /libraries/{id} |
*LibraryApi* | [**getAllLibraries**](doc//LibraryApi.md#getalllibraries) | **GET** /library | *LibraryApi* | [**getAllLibraries**](doc//LibraryApi.md#getalllibraries) | **GET** /libraries |
*LibraryApi* | [**getLibrary**](doc//LibraryApi.md#getlibrary) | **GET** /library/{id} | *LibraryApi* | [**getLibrary**](doc//LibraryApi.md#getlibrary) | **GET** /libraries/{id} |
*LibraryApi* | [**getLibraryStatistics**](doc//LibraryApi.md#getlibrarystatistics) | **GET** /library/{id}/statistics | *LibraryApi* | [**getLibraryStatistics**](doc//LibraryApi.md#getlibrarystatistics) | **GET** /libraries/{id}/statistics |
*LibraryApi* | [**removeOfflineFiles**](doc//LibraryApi.md#removeofflinefiles) | **POST** /library/{id}/removeOffline | *LibraryApi* | [**removeOfflineFiles**](doc//LibraryApi.md#removeofflinefiles) | **POST** /libraries/{id}/removeOffline |
*LibraryApi* | [**scanLibrary**](doc//LibraryApi.md#scanlibrary) | **POST** /library/{id}/scan | *LibraryApi* | [**scanLibrary**](doc//LibraryApi.md#scanlibrary) | **POST** /libraries/{id}/scan |
*LibraryApi* | [**updateLibrary**](doc//LibraryApi.md#updatelibrary) | **PUT** /library/{id} | *LibraryApi* | [**updateLibrary**](doc//LibraryApi.md#updatelibrary) | **PUT** /libraries/{id} |
*LibraryApi* | [**validate**](doc//LibraryApi.md#validate) | **POST** /library/{id}/validate | *LibraryApi* | [**validate**](doc//LibraryApi.md#validate) | **POST** /libraries/{id}/validate |
*MemoryApi* | [**addMemoryAssets**](doc//MemoryApi.md#addmemoryassets) | **PUT** /memories/{id}/assets | *MemoryApi* | [**addMemoryAssets**](doc//MemoryApi.md#addmemoryassets) | **PUT** /memories/{id}/assets |
*MemoryApi* | [**createMemory**](doc//MemoryApi.md#creatememory) | **POST** /memories | *MemoryApi* | [**createMemory**](doc//MemoryApi.md#creatememory) | **POST** /memories |
*MemoryApi* | [**deleteMemory**](doc//MemoryApi.md#deletememory) | **DELETE** /memories/{id} | *MemoryApi* | [**deleteMemory**](doc//MemoryApi.md#deletememory) | **DELETE** /memories/{id} |
@@ -148,20 +149,21 @@ Class | Method | HTTP request | Description
*OAuthApi* | [**redirectOAuthToMobile**](doc//OAuthApi.md#redirectoauthtomobile) | **GET** /oauth/mobile-redirect | *OAuthApi* | [**redirectOAuthToMobile**](doc//OAuthApi.md#redirectoauthtomobile) | **GET** /oauth/mobile-redirect |
*OAuthApi* | [**startOAuth**](doc//OAuthApi.md#startoauth) | **POST** /oauth/authorize | *OAuthApi* | [**startOAuth**](doc//OAuthApi.md#startoauth) | **POST** /oauth/authorize |
*OAuthApi* | [**unlinkOAuthAccount**](doc//OAuthApi.md#unlinkoauthaccount) | **POST** /oauth/unlink | *OAuthApi* | [**unlinkOAuthAccount**](doc//OAuthApi.md#unlinkoauthaccount) | **POST** /oauth/unlink |
*PartnerApi* | [**createPartner**](doc//PartnerApi.md#createpartner) | **POST** /partner/{id} | *PartnerApi* | [**createPartner**](doc//PartnerApi.md#createpartner) | **POST** /partners/{id} |
*PartnerApi* | [**getPartners**](doc//PartnerApi.md#getpartners) | **GET** /partner | *PartnerApi* | [**getPartners**](doc//PartnerApi.md#getpartners) | **GET** /partners |
*PartnerApi* | [**removePartner**](doc//PartnerApi.md#removepartner) | **DELETE** /partner/{id} | *PartnerApi* | [**removePartner**](doc//PartnerApi.md#removepartner) | **DELETE** /partners/{id} |
*PartnerApi* | [**updatePartner**](doc//PartnerApi.md#updatepartner) | **PUT** /partner/{id} | *PartnerApi* | [**updatePartner**](doc//PartnerApi.md#updatepartner) | **PUT** /partners/{id} |
*PersonApi* | [**createPerson**](doc//PersonApi.md#createperson) | **POST** /person | *PersonApi* | [**createPerson**](doc//PersonApi.md#createperson) | **POST** /people |
*PersonApi* | [**getAllPeople**](doc//PersonApi.md#getallpeople) | **GET** /person | *PersonApi* | [**getAllPeople**](doc//PersonApi.md#getallpeople) | **GET** /people |
*PersonApi* | [**getPerson**](doc//PersonApi.md#getperson) | **GET** /person/{id} | *PersonApi* | [**getPerson**](doc//PersonApi.md#getperson) | **GET** /people/{id} |
*PersonApi* | [**getPersonAssets**](doc//PersonApi.md#getpersonassets) | **GET** /person/{id}/assets | *PersonApi* | [**getPersonAssets**](doc//PersonApi.md#getpersonassets) | **GET** /people/{id}/assets |
*PersonApi* | [**getPersonStatistics**](doc//PersonApi.md#getpersonstatistics) | **GET** /person/{id}/statistics | *PersonApi* | [**getPersonStatistics**](doc//PersonApi.md#getpersonstatistics) | **GET** /people/{id}/statistics |
*PersonApi* | [**getPersonThumbnail**](doc//PersonApi.md#getpersonthumbnail) | **GET** /person/{id}/thumbnail | *PersonApi* | [**getPersonThumbnail**](doc//PersonApi.md#getpersonthumbnail) | **GET** /people/{id}/thumbnail |
*PersonApi* | [**mergePerson**](doc//PersonApi.md#mergeperson) | **POST** /person/{id}/merge | *PersonApi* | [**mergePerson**](doc//PersonApi.md#mergeperson) | **POST** /people/{id}/merge |
*PersonApi* | [**reassignFaces**](doc//PersonApi.md#reassignfaces) | **PUT** /person/{id}/reassign | *PersonApi* | [**reassignFaces**](doc//PersonApi.md#reassignfaces) | **PUT** /people/{id}/reassign |
*PersonApi* | [**updatePeople**](doc//PersonApi.md#updatepeople) | **PUT** /person | *PersonApi* | [**unassignFaces**](doc//PersonApi.md#unassignfaces) | **DELETE** /people |
*PersonApi* | [**updatePerson**](doc//PersonApi.md#updateperson) | **PUT** /person/{id} | *PersonApi* | [**updatePeople**](doc//PersonApi.md#updatepeople) | **PUT** /people |
*PersonApi* | [**updatePerson**](doc//PersonApi.md#updateperson) | **PUT** /people/{id} |
*SearchApi* | [**getAssetsByCity**](doc//SearchApi.md#getassetsbycity) | **GET** /search/cities | *SearchApi* | [**getAssetsByCity**](doc//SearchApi.md#getassetsbycity) | **GET** /search/cities |
*SearchApi* | [**getExploreData**](doc//SearchApi.md#getexploredata) | **GET** /search/explore | *SearchApi* | [**getExploreData**](doc//SearchApi.md#getexploredata) | **GET** /search/explore |
*SearchApi* | [**getSearchSuggestions**](doc//SearchApi.md#getsearchsuggestions) | **GET** /search/suggestions | *SearchApi* | [**getSearchSuggestions**](doc//SearchApi.md#getsearchsuggestions) | **GET** /search/suggestions |
@@ -171,23 +173,23 @@ Class | Method | HTTP request | Description
*SearchApi* | [**searchSmart**](doc//SearchApi.md#searchsmart) | **POST** /search/smart | *SearchApi* | [**searchSmart**](doc//SearchApi.md#searchsmart) | **POST** /search/smart |
*ServerInfoApi* | [**getServerConfig**](doc//ServerInfoApi.md#getserverconfig) | **GET** /server-info/config | *ServerInfoApi* | [**getServerConfig**](doc//ServerInfoApi.md#getserverconfig) | **GET** /server-info/config |
*ServerInfoApi* | [**getServerFeatures**](doc//ServerInfoApi.md#getserverfeatures) | **GET** /server-info/features | *ServerInfoApi* | [**getServerFeatures**](doc//ServerInfoApi.md#getserverfeatures) | **GET** /server-info/features |
*ServerInfoApi* | [**getServerInfo**](doc//ServerInfoApi.md#getserverinfo) | **GET** /server-info |
*ServerInfoApi* | [**getServerStatistics**](doc//ServerInfoApi.md#getserverstatistics) | **GET** /server-info/statistics | *ServerInfoApi* | [**getServerStatistics**](doc//ServerInfoApi.md#getserverstatistics) | **GET** /server-info/statistics |
*ServerInfoApi* | [**getServerVersion**](doc//ServerInfoApi.md#getserverversion) | **GET** /server-info/version | *ServerInfoApi* | [**getServerVersion**](doc//ServerInfoApi.md#getserverversion) | **GET** /server-info/version |
*ServerInfoApi* | [**getStorage**](doc//ServerInfoApi.md#getstorage) | **GET** /server-info/storage |
*ServerInfoApi* | [**getSupportedMediaTypes**](doc//ServerInfoApi.md#getsupportedmediatypes) | **GET** /server-info/media-types | *ServerInfoApi* | [**getSupportedMediaTypes**](doc//ServerInfoApi.md#getsupportedmediatypes) | **GET** /server-info/media-types |
*ServerInfoApi* | [**getTheme**](doc//ServerInfoApi.md#gettheme) | **GET** /server-info/theme | *ServerInfoApi* | [**getTheme**](doc//ServerInfoApi.md#gettheme) | **GET** /server-info/theme |
*ServerInfoApi* | [**pingServer**](doc//ServerInfoApi.md#pingserver) | **GET** /server-info/ping | *ServerInfoApi* | [**pingServer**](doc//ServerInfoApi.md#pingserver) | **GET** /server-info/ping |
*SessionsApi* | [**deleteAllSessions**](doc//SessionsApi.md#deleteallsessions) | **DELETE** /sessions | *SessionsApi* | [**deleteAllSessions**](doc//SessionsApi.md#deleteallsessions) | **DELETE** /sessions |
*SessionsApi* | [**deleteSession**](doc//SessionsApi.md#deletesession) | **DELETE** /sessions/{id} | *SessionsApi* | [**deleteSession**](doc//SessionsApi.md#deletesession) | **DELETE** /sessions/{id} |
*SessionsApi* | [**getSessions**](doc//SessionsApi.md#getsessions) | **GET** /sessions | *SessionsApi* | [**getSessions**](doc//SessionsApi.md#getsessions) | **GET** /sessions |
*SharedLinkApi* | [**addSharedLinkAssets**](doc//SharedLinkApi.md#addsharedlinkassets) | **PUT** /shared-link/{id}/assets | *SharedLinkApi* | [**addSharedLinkAssets**](doc//SharedLinkApi.md#addsharedlinkassets) | **PUT** /shared-links/{id}/assets |
*SharedLinkApi* | [**createSharedLink**](doc//SharedLinkApi.md#createsharedlink) | **POST** /shared-link | *SharedLinkApi* | [**createSharedLink**](doc//SharedLinkApi.md#createsharedlink) | **POST** /shared-links |
*SharedLinkApi* | [**getAllSharedLinks**](doc//SharedLinkApi.md#getallsharedlinks) | **GET** /shared-link | *SharedLinkApi* | [**getAllSharedLinks**](doc//SharedLinkApi.md#getallsharedlinks) | **GET** /shared-links |
*SharedLinkApi* | [**getMySharedLink**](doc//SharedLinkApi.md#getmysharedlink) | **GET** /shared-link/me | *SharedLinkApi* | [**getMySharedLink**](doc//SharedLinkApi.md#getmysharedlink) | **GET** /shared-links/me |
*SharedLinkApi* | [**getSharedLinkById**](doc//SharedLinkApi.md#getsharedlinkbyid) | **GET** /shared-link/{id} | *SharedLinkApi* | [**getSharedLinkById**](doc//SharedLinkApi.md#getsharedlinkbyid) | **GET** /shared-links/{id} |
*SharedLinkApi* | [**removeSharedLink**](doc//SharedLinkApi.md#removesharedlink) | **DELETE** /shared-link/{id} | *SharedLinkApi* | [**removeSharedLink**](doc//SharedLinkApi.md#removesharedlink) | **DELETE** /shared-links/{id} |
*SharedLinkApi* | [**removeSharedLinkAssets**](doc//SharedLinkApi.md#removesharedlinkassets) | **DELETE** /shared-link/{id}/assets | *SharedLinkApi* | [**removeSharedLinkAssets**](doc//SharedLinkApi.md#removesharedlinkassets) | **DELETE** /shared-links/{id}/assets |
*SharedLinkApi* | [**updateSharedLink**](doc//SharedLinkApi.md#updatesharedlink) | **PATCH** /shared-link/{id} | *SharedLinkApi* | [**updateSharedLink**](doc//SharedLinkApi.md#updatesharedlink) | **PATCH** /shared-links/{id} |
*SyncApi* | [**getDeltaSync**](doc//SyncApi.md#getdeltasync) | **POST** /sync/delta-sync | *SyncApi* | [**getDeltaSync**](doc//SyncApi.md#getdeltasync) | **POST** /sync/delta-sync |
*SyncApi* | [**getFullSyncForUser**](doc//SyncApi.md#getfullsyncforuser) | **POST** /sync/full-sync | *SyncApi* | [**getFullSyncForUser**](doc//SyncApi.md#getfullsyncforuser) | **POST** /sync/full-sync |
*SystemConfigApi* | [**getConfig**](doc//SystemConfigApi.md#getconfig) | **GET** /system-config | *SystemConfigApi* | [**getConfig**](doc//SystemConfigApi.md#getconfig) | **GET** /system-config |
@@ -198,29 +200,32 @@ Class | Method | HTTP request | Description
*SystemMetadataApi* | [**getAdminOnboarding**](doc//SystemMetadataApi.md#getadminonboarding) | **GET** /system-metadata/admin-onboarding | *SystemMetadataApi* | [**getAdminOnboarding**](doc//SystemMetadataApi.md#getadminonboarding) | **GET** /system-metadata/admin-onboarding |
*SystemMetadataApi* | [**getReverseGeocodingState**](doc//SystemMetadataApi.md#getreversegeocodingstate) | **GET** /system-metadata/reverse-geocoding-state | *SystemMetadataApi* | [**getReverseGeocodingState**](doc//SystemMetadataApi.md#getreversegeocodingstate) | **GET** /system-metadata/reverse-geocoding-state |
*SystemMetadataApi* | [**updateAdminOnboarding**](doc//SystemMetadataApi.md#updateadminonboarding) | **POST** /system-metadata/admin-onboarding | *SystemMetadataApi* | [**updateAdminOnboarding**](doc//SystemMetadataApi.md#updateadminonboarding) | **POST** /system-metadata/admin-onboarding |
*TagApi* | [**createTag**](doc//TagApi.md#createtag) | **POST** /tag | *TagApi* | [**createTag**](doc//TagApi.md#createtag) | **POST** /tags |
*TagApi* | [**deleteTag**](doc//TagApi.md#deletetag) | **DELETE** /tag/{id} | *TagApi* | [**deleteTag**](doc//TagApi.md#deletetag) | **DELETE** /tags/{id} |
*TagApi* | [**getAllTags**](doc//TagApi.md#getalltags) | **GET** /tag | *TagApi* | [**getAllTags**](doc//TagApi.md#getalltags) | **GET** /tags |
*TagApi* | [**getTagAssets**](doc//TagApi.md#gettagassets) | **GET** /tag/{id}/assets | *TagApi* | [**getTagAssets**](doc//TagApi.md#gettagassets) | **GET** /tags/{id}/assets |
*TagApi* | [**getTagById**](doc//TagApi.md#gettagbyid) | **GET** /tag/{id} | *TagApi* | [**getTagById**](doc//TagApi.md#gettagbyid) | **GET** /tags/{id} |
*TagApi* | [**tagAssets**](doc//TagApi.md#tagassets) | **PUT** /tag/{id}/assets | *TagApi* | [**tagAssets**](doc//TagApi.md#tagassets) | **PUT** /tags/{id}/assets |
*TagApi* | [**untagAssets**](doc//TagApi.md#untagassets) | **DELETE** /tag/{id}/assets | *TagApi* | [**untagAssets**](doc//TagApi.md#untagassets) | **DELETE** /tags/{id}/assets |
*TagApi* | [**updateTag**](doc//TagApi.md#updatetag) | **PATCH** /tag/{id} | *TagApi* | [**updateTag**](doc//TagApi.md#updatetag) | **PATCH** /tags/{id} |
*TimelineApi* | [**getTimeBucket**](doc//TimelineApi.md#gettimebucket) | **GET** /timeline/bucket | *TimelineApi* | [**getTimeBucket**](doc//TimelineApi.md#gettimebucket) | **GET** /timeline/bucket |
*TimelineApi* | [**getTimeBuckets**](doc//TimelineApi.md#gettimebuckets) | **GET** /timeline/buckets | *TimelineApi* | [**getTimeBuckets**](doc//TimelineApi.md#gettimebuckets) | **GET** /timeline/buckets |
*TrashApi* | [**emptyTrash**](doc//TrashApi.md#emptytrash) | **POST** /trash/empty | *TrashApi* | [**emptyTrash**](doc//TrashApi.md#emptytrash) | **POST** /trash/empty |
*TrashApi* | [**restoreAssets**](doc//TrashApi.md#restoreassets) | **POST** /trash/restore/assets | *TrashApi* | [**restoreAssets**](doc//TrashApi.md#restoreassets) | **POST** /trash/restore/assets |
*TrashApi* | [**restoreTrash**](doc//TrashApi.md#restoretrash) | **POST** /trash/restore | *TrashApi* | [**restoreTrash**](doc//TrashApi.md#restoretrash) | **POST** /trash/restore |
*UserApi* | [**createProfileImage**](doc//UserApi.md#createprofileimage) | **POST** /user/profile-image | *UserApi* | [**createProfileImage**](doc//UserApi.md#createprofileimage) | **POST** /users/profile-image |
*UserApi* | [**createUser**](doc//UserApi.md#createuser) | **POST** /user | *UserApi* | [**createUserAdmin**](doc//UserApi.md#createuseradmin) | **POST** /admin/users |
*UserApi* | [**deleteProfileImage**](doc//UserApi.md#deleteprofileimage) | **DELETE** /user/profile-image | *UserApi* | [**deleteProfileImage**](doc//UserApi.md#deleteprofileimage) | **DELETE** /users/profile-image |
*UserApi* | [**deleteUser**](doc//UserApi.md#deleteuser) | **DELETE** /user/{id} | *UserApi* | [**deleteUserAdmin**](doc//UserApi.md#deleteuseradmin) | **DELETE** /admin/users/{id} |
*UserApi* | [**getAllUsers**](doc//UserApi.md#getallusers) | **GET** /user | *UserApi* | [**getMyUser**](doc//UserApi.md#getmyuser) | **GET** /users/me |
*UserApi* | [**getMyUserInfo**](doc//UserApi.md#getmyuserinfo) | **GET** /user/me | *UserApi* | [**getProfileImage**](doc//UserApi.md#getprofileimage) | **GET** /users/{id}/profile-image |
*UserApi* | [**getProfileImage**](doc//UserApi.md#getprofileimage) | **GET** /user/profile-image/{id} | *UserApi* | [**getUser**](doc//UserApi.md#getuser) | **GET** /users/{id} |
*UserApi* | [**getUserById**](doc//UserApi.md#getuserbyid) | **GET** /user/info/{id} | *UserApi* | [**getUserAdmin**](doc//UserApi.md#getuseradmin) | **GET** /admin/users/{id} |
*UserApi* | [**restoreUser**](doc//UserApi.md#restoreuser) | **POST** /user/{id}/restore | *UserApi* | [**restoreUserAdmin**](doc//UserApi.md#restoreuseradmin) | **POST** /admin/users/{id}/restore |
*UserApi* | [**updateUser**](doc//UserApi.md#updateuser) | **PUT** /user | *UserApi* | [**searchUsers**](doc//UserApi.md#searchusers) | **GET** /users |
*UserApi* | [**searchUsersAdmin**](doc//UserApi.md#searchusersadmin) | **GET** /admin/users |
*UserApi* | [**updateMyUser**](doc//UserApi.md#updatemyuser) | **PUT** /users/me |
*UserApi* | [**updateUserAdmin**](doc//UserApi.md#updateuseradmin) | **PUT** /admin/users/{id} |
## Documentation For Models ## Documentation For Models
@@ -259,6 +264,8 @@ Class | Method | HTTP request | Description
- [AssetIdsResponseDto](doc//AssetIdsResponseDto.md) - [AssetIdsResponseDto](doc//AssetIdsResponseDto.md)
- [AssetJobName](doc//AssetJobName.md) - [AssetJobName](doc//AssetJobName.md)
- [AssetJobsDto](doc//AssetJobsDto.md) - [AssetJobsDto](doc//AssetJobsDto.md)
- [AssetMediaResponseDto](doc//AssetMediaResponseDto.md)
- [AssetMediaStatus](doc//AssetMediaStatus.md)
- [AssetOrder](doc//AssetOrder.md) - [AssetOrder](doc//AssetOrder.md)
- [AssetResponseDto](doc//AssetResponseDto.md) - [AssetResponseDto](doc//AssetResponseDto.md)
- [AssetStatsResponseDto](doc//AssetStatsResponseDto.md) - [AssetStatsResponseDto](doc//AssetStatsResponseDto.md)
@@ -278,8 +285,6 @@ Class | Method | HTTP request | Description
- [CreateLibraryDto](doc//CreateLibraryDto.md) - [CreateLibraryDto](doc//CreateLibraryDto.md)
- [CreateProfileImageResponseDto](doc//CreateProfileImageResponseDto.md) - [CreateProfileImageResponseDto](doc//CreateProfileImageResponseDto.md)
- [CreateTagDto](doc//CreateTagDto.md) - [CreateTagDto](doc//CreateTagDto.md)
- [CreateUserDto](doc//CreateUserDto.md)
- [DeleteUserDto](doc//DeleteUserDto.md)
- [DownloadArchiveInfo](doc//DownloadArchiveInfo.md) - [DownloadArchiveInfo](doc//DownloadArchiveInfo.md)
- [DownloadInfoDto](doc//DownloadInfoDto.md) - [DownloadInfoDto](doc//DownloadInfoDto.md)
- [DownloadResponseDto](doc//DownloadResponseDto.md) - [DownloadResponseDto](doc//DownloadResponseDto.md)
@@ -326,6 +331,7 @@ Class | Method | HTTP request | Description
- [PeopleResponseDto](doc//PeopleResponseDto.md) - [PeopleResponseDto](doc//PeopleResponseDto.md)
- [PeopleUpdateDto](doc//PeopleUpdateDto.md) - [PeopleUpdateDto](doc//PeopleUpdateDto.md)
- [PeopleUpdateItem](doc//PeopleUpdateItem.md) - [PeopleUpdateItem](doc//PeopleUpdateItem.md)
- [PeopleWithFacesResponseDto](doc//PeopleWithFacesResponseDto.md)
- [PersonCreateDto](doc//PersonCreateDto.md) - [PersonCreateDto](doc//PersonCreateDto.md)
- [PersonResponseDto](doc//PersonResponseDto.md) - [PersonResponseDto](doc//PersonResponseDto.md)
- [PersonStatisticsResponseDto](doc//PersonStatisticsResponseDto.md) - [PersonStatisticsResponseDto](doc//PersonStatisticsResponseDto.md)
@@ -348,10 +354,10 @@ Class | Method | HTTP request | Description
- [SearchSuggestionType](doc//SearchSuggestionType.md) - [SearchSuggestionType](doc//SearchSuggestionType.md)
- [ServerConfigDto](doc//ServerConfigDto.md) - [ServerConfigDto](doc//ServerConfigDto.md)
- [ServerFeaturesDto](doc//ServerFeaturesDto.md) - [ServerFeaturesDto](doc//ServerFeaturesDto.md)
- [ServerInfoResponseDto](doc//ServerInfoResponseDto.md)
- [ServerMediaTypesResponseDto](doc//ServerMediaTypesResponseDto.md) - [ServerMediaTypesResponseDto](doc//ServerMediaTypesResponseDto.md)
- [ServerPingResponse](doc//ServerPingResponse.md) - [ServerPingResponse](doc//ServerPingResponse.md)
- [ServerStatsResponseDto](doc//ServerStatsResponseDto.md) - [ServerStatsResponseDto](doc//ServerStatsResponseDto.md)
- [ServerStorageResponseDto](doc//ServerStorageResponseDto.md)
- [ServerThemeDto](doc//ServerThemeDto.md) - [ServerThemeDto](doc//ServerThemeDto.md)
- [ServerVersionResponseDto](doc//ServerVersionResponseDto.md) - [ServerVersionResponseDto](doc//ServerVersionResponseDto.md)
- [SessionResponseDto](doc//SessionResponseDto.md) - [SessionResponseDto](doc//SessionResponseDto.md)
@@ -400,12 +406,15 @@ Class | Method | HTTP request | Description
- [UpdatePartnerDto](doc//UpdatePartnerDto.md) - [UpdatePartnerDto](doc//UpdatePartnerDto.md)
- [UpdateStackParentDto](doc//UpdateStackParentDto.md) - [UpdateStackParentDto](doc//UpdateStackParentDto.md)
- [UpdateTagDto](doc//UpdateTagDto.md) - [UpdateTagDto](doc//UpdateTagDto.md)
- [UpdateUserDto](doc//UpdateUserDto.md)
- [UsageByUserDto](doc//UsageByUserDto.md) - [UsageByUserDto](doc//UsageByUserDto.md)
- [UserAdminCreateDto](doc//UserAdminCreateDto.md)
- [UserAdminDeleteDto](doc//UserAdminDeleteDto.md)
- [UserAdminResponseDto](doc//UserAdminResponseDto.md)
- [UserAdminUpdateDto](doc//UserAdminUpdateDto.md)
- [UserAvatarColor](doc//UserAvatarColor.md) - [UserAvatarColor](doc//UserAvatarColor.md)
- [UserDto](doc//UserDto.md)
- [UserResponseDto](doc//UserResponseDto.md) - [UserResponseDto](doc//UserResponseDto.md)
- [UserStatus](doc//UserStatus.md) - [UserStatus](doc//UserStatus.md)
- [UserUpdateMeDto](doc//UserUpdateMeDto.md)
- [ValidateAccessTokenResponseDto](doc//ValidateAccessTokenResponseDto.md) - [ValidateAccessTokenResponseDto](doc//ValidateAccessTokenResponseDto.md)
- [ValidateLibraryDto](doc//ValidateLibraryDto.md) - [ValidateLibraryDto](doc//ValidateLibraryDto.md)
- [ValidateLibraryImportPathResponseDto](doc//ValidateLibraryImportPathResponseDto.md) - [ValidateLibraryImportPathResponseDto](doc//ValidateLibraryImportPathResponseDto.md)

View File

@@ -0,0 +1,16 @@
# openapi.model.PeopleWithFacesResponseDto
## Load the model package
```dart
import 'package:openapi/api.dart';
```
## Properties
Name | Type | Description | Notes
------------ | ------------- | ------------- | -------------
**numberOfFaces** | **int** | |
**visiblePeople** | [**List<PersonWithFacesResponseDto>**](PersonWithFacesResponseDto.md) | | [default to const []]
[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md)

View File

@@ -91,6 +91,8 @@ part 'model/asset_ids_dto.dart';
part 'model/asset_ids_response_dto.dart'; part 'model/asset_ids_response_dto.dart';
part 'model/asset_job_name.dart'; part 'model/asset_job_name.dart';
part 'model/asset_jobs_dto.dart'; part 'model/asset_jobs_dto.dart';
part 'model/asset_media_response_dto.dart';
part 'model/asset_media_status.dart';
part 'model/asset_order.dart'; part 'model/asset_order.dart';
part 'model/asset_response_dto.dart'; part 'model/asset_response_dto.dart';
part 'model/asset_stats_response_dto.dart'; part 'model/asset_stats_response_dto.dart';
@@ -110,8 +112,6 @@ part 'model/create_album_dto.dart';
part 'model/create_library_dto.dart'; part 'model/create_library_dto.dart';
part 'model/create_profile_image_response_dto.dart'; part 'model/create_profile_image_response_dto.dart';
part 'model/create_tag_dto.dart'; part 'model/create_tag_dto.dart';
part 'model/create_user_dto.dart';
part 'model/delete_user_dto.dart';
part 'model/download_archive_info.dart'; part 'model/download_archive_info.dart';
part 'model/download_info_dto.dart'; part 'model/download_info_dto.dart';
part 'model/download_response_dto.dart'; part 'model/download_response_dto.dart';
@@ -158,6 +158,7 @@ part 'model/path_type.dart';
part 'model/people_response_dto.dart'; part 'model/people_response_dto.dart';
part 'model/people_update_dto.dart'; part 'model/people_update_dto.dart';
part 'model/people_update_item.dart'; part 'model/people_update_item.dart';
part 'model/people_with_faces_response_dto.dart';
part 'model/person_create_dto.dart'; part 'model/person_create_dto.dart';
part 'model/person_response_dto.dart'; part 'model/person_response_dto.dart';
part 'model/person_statistics_response_dto.dart'; part 'model/person_statistics_response_dto.dart';
@@ -180,10 +181,10 @@ part 'model/search_response_dto.dart';
part 'model/search_suggestion_type.dart'; part 'model/search_suggestion_type.dart';
part 'model/server_config_dto.dart'; part 'model/server_config_dto.dart';
part 'model/server_features_dto.dart'; part 'model/server_features_dto.dart';
part 'model/server_info_response_dto.dart';
part 'model/server_media_types_response_dto.dart'; part 'model/server_media_types_response_dto.dart';
part 'model/server_ping_response.dart'; part 'model/server_ping_response.dart';
part 'model/server_stats_response_dto.dart'; part 'model/server_stats_response_dto.dart';
part 'model/server_storage_response_dto.dart';
part 'model/server_theme_dto.dart'; part 'model/server_theme_dto.dart';
part 'model/server_version_response_dto.dart'; part 'model/server_version_response_dto.dart';
part 'model/session_response_dto.dart'; part 'model/session_response_dto.dart';
@@ -232,12 +233,15 @@ part 'model/update_library_dto.dart';
part 'model/update_partner_dto.dart'; part 'model/update_partner_dto.dart';
part 'model/update_stack_parent_dto.dart'; part 'model/update_stack_parent_dto.dart';
part 'model/update_tag_dto.dart'; part 'model/update_tag_dto.dart';
part 'model/update_user_dto.dart';
part 'model/usage_by_user_dto.dart'; part 'model/usage_by_user_dto.dart';
part 'model/user_admin_create_dto.dart';
part 'model/user_admin_delete_dto.dart';
part 'model/user_admin_response_dto.dart';
part 'model/user_admin_update_dto.dart';
part 'model/user_avatar_color.dart'; part 'model/user_avatar_color.dart';
part 'model/user_dto.dart';
part 'model/user_response_dto.dart'; part 'model/user_response_dto.dart';
part 'model/user_status.dart'; part 'model/user_status.dart';
part 'model/user_update_me_dto.dart';
part 'model/validate_access_token_response_dto.dart'; part 'model/validate_access_token_response_dto.dart';
part 'model/validate_library_dto.dart'; part 'model/validate_library_dto.dart';
part 'model/validate_library_import_path_response_dto.dart'; part 'model/validate_library_import_path_response_dto.dart';

View File

@@ -16,13 +16,13 @@ class ActivityApi {
final ApiClient apiClient; final ApiClient apiClient;
/// Performs an HTTP 'POST /activity' operation and returns the [Response]. /// Performs an HTTP 'POST /activities' operation and returns the [Response].
/// Parameters: /// Parameters:
/// ///
/// * [ActivityCreateDto] activityCreateDto (required): /// * [ActivityCreateDto] activityCreateDto (required):
Future<Response> createActivityWithHttpInfo(ActivityCreateDto activityCreateDto,) async { Future<Response> createActivityWithHttpInfo(ActivityCreateDto activityCreateDto,) async {
// ignore: prefer_const_declarations // ignore: prefer_const_declarations
final path = r'/activity'; final path = r'/activities';
// ignore: prefer_final_locals // ignore: prefer_final_locals
Object? postBody = activityCreateDto; Object? postBody = activityCreateDto;
@@ -63,13 +63,13 @@ class ActivityApi {
return null; return null;
} }
/// Performs an HTTP 'DELETE /activity/{id}' operation and returns the [Response]. /// Performs an HTTP 'DELETE /activities/{id}' operation and returns the [Response].
/// Parameters: /// Parameters:
/// ///
/// * [String] id (required): /// * [String] id (required):
Future<Response> deleteActivityWithHttpInfo(String id,) async { Future<Response> deleteActivityWithHttpInfo(String id,) async {
// ignore: prefer_const_declarations // ignore: prefer_const_declarations
final path = r'/activity/{id}' final path = r'/activities/{id}'
.replaceAll('{id}', id); .replaceAll('{id}', id);
// ignore: prefer_final_locals // ignore: prefer_final_locals
@@ -103,7 +103,7 @@ class ActivityApi {
} }
} }
/// Performs an HTTP 'GET /activity' operation and returns the [Response]. /// Performs an HTTP 'GET /activities' operation and returns the [Response].
/// Parameters: /// Parameters:
/// ///
/// * [String] albumId (required): /// * [String] albumId (required):
@@ -117,7 +117,7 @@ class ActivityApi {
/// * [String] userId: /// * [String] userId:
Future<Response> getActivitiesWithHttpInfo(String albumId, { String? assetId, ReactionLevel? level, ReactionType? type, String? userId, }) async { Future<Response> getActivitiesWithHttpInfo(String albumId, { String? assetId, ReactionLevel? level, ReactionType? type, String? userId, }) async {
// ignore: prefer_const_declarations // ignore: prefer_const_declarations
final path = r'/activity'; final path = r'/activities';
// ignore: prefer_final_locals // ignore: prefer_final_locals
Object? postBody; Object? postBody;
@@ -183,7 +183,7 @@ class ActivityApi {
return null; return null;
} }
/// Performs an HTTP 'GET /activity/statistics' operation and returns the [Response]. /// Performs an HTTP 'GET /activities/statistics' operation and returns the [Response].
/// Parameters: /// Parameters:
/// ///
/// * [String] albumId (required): /// * [String] albumId (required):
@@ -191,7 +191,7 @@ class ActivityApi {
/// * [String] assetId: /// * [String] assetId:
Future<Response> getActivityStatisticsWithHttpInfo(String albumId, { String? assetId, }) async { Future<Response> getActivityStatisticsWithHttpInfo(String albumId, { String? assetId, }) async {
// ignore: prefer_const_declarations // ignore: prefer_const_declarations
final path = r'/activity/statistics'; final path = r'/activities/statistics';
// ignore: prefer_final_locals // ignore: prefer_final_locals
Object? postBody; Object? postBody;

View File

@@ -16,7 +16,7 @@ class AlbumApi {
final ApiClient apiClient; final ApiClient apiClient;
/// Performs an HTTP 'PUT /album/{id}/assets' operation and returns the [Response]. /// Performs an HTTP 'PUT /albums/{id}/assets' operation and returns the [Response].
/// Parameters: /// Parameters:
/// ///
/// * [String] id (required): /// * [String] id (required):
@@ -26,7 +26,7 @@ class AlbumApi {
/// * [String] key: /// * [String] key:
Future<Response> addAssetsToAlbumWithHttpInfo(String id, BulkIdsDto bulkIdsDto, { String? key, }) async { Future<Response> addAssetsToAlbumWithHttpInfo(String id, BulkIdsDto bulkIdsDto, { String? key, }) async {
// ignore: prefer_const_declarations // ignore: prefer_const_declarations
final path = r'/album/{id}/assets' final path = r'/albums/{id}/assets'
.replaceAll('{id}', id); .replaceAll('{id}', id);
// ignore: prefer_final_locals // ignore: prefer_final_locals
@@ -79,7 +79,7 @@ class AlbumApi {
return null; return null;
} }
/// Performs an HTTP 'PUT /album/{id}/users' operation and returns the [Response]. /// Performs an HTTP 'PUT /albums/{id}/users' operation and returns the [Response].
/// Parameters: /// Parameters:
/// ///
/// * [String] id (required): /// * [String] id (required):
@@ -87,7 +87,7 @@ class AlbumApi {
/// * [AddUsersDto] addUsersDto (required): /// * [AddUsersDto] addUsersDto (required):
Future<Response> addUsersToAlbumWithHttpInfo(String id, AddUsersDto addUsersDto,) async { Future<Response> addUsersToAlbumWithHttpInfo(String id, AddUsersDto addUsersDto,) async {
// ignore: prefer_const_declarations // ignore: prefer_const_declarations
final path = r'/album/{id}/users' final path = r'/albums/{id}/users'
.replaceAll('{id}', id); .replaceAll('{id}', id);
// ignore: prefer_final_locals // ignore: prefer_final_locals
@@ -131,13 +131,13 @@ class AlbumApi {
return null; return null;
} }
/// Performs an HTTP 'POST /album' operation and returns the [Response]. /// Performs an HTTP 'POST /albums' operation and returns the [Response].
/// Parameters: /// Parameters:
/// ///
/// * [CreateAlbumDto] createAlbumDto (required): /// * [CreateAlbumDto] createAlbumDto (required):
Future<Response> createAlbumWithHttpInfo(CreateAlbumDto createAlbumDto,) async { Future<Response> createAlbumWithHttpInfo(CreateAlbumDto createAlbumDto,) async {
// ignore: prefer_const_declarations // ignore: prefer_const_declarations
final path = r'/album'; final path = r'/albums';
// ignore: prefer_final_locals // ignore: prefer_final_locals
Object? postBody = createAlbumDto; Object? postBody = createAlbumDto;
@@ -178,13 +178,13 @@ class AlbumApi {
return null; return null;
} }
/// Performs an HTTP 'DELETE /album/{id}' operation and returns the [Response]. /// Performs an HTTP 'DELETE /albums/{id}' operation and returns the [Response].
/// Parameters: /// Parameters:
/// ///
/// * [String] id (required): /// * [String] id (required):
Future<Response> deleteAlbumWithHttpInfo(String id,) async { Future<Response> deleteAlbumWithHttpInfo(String id,) async {
// ignore: prefer_const_declarations // ignore: prefer_const_declarations
final path = r'/album/{id}' final path = r'/albums/{id}'
.replaceAll('{id}', id); .replaceAll('{id}', id);
// ignore: prefer_final_locals // ignore: prefer_final_locals
@@ -218,10 +218,10 @@ class AlbumApi {
} }
} }
/// Performs an HTTP 'GET /album/count' operation and returns the [Response]. /// Performs an HTTP 'GET /albums/count' operation and returns the [Response].
Future<Response> getAlbumCountWithHttpInfo() async { Future<Response> getAlbumCountWithHttpInfo() async {
// ignore: prefer_const_declarations // ignore: prefer_const_declarations
final path = r'/album/count'; final path = r'/albums/count';
// ignore: prefer_final_locals // ignore: prefer_final_locals
Object? postBody; Object? postBody;
@@ -259,7 +259,7 @@ class AlbumApi {
return null; return null;
} }
/// Performs an HTTP 'GET /album/{id}' operation and returns the [Response]. /// Performs an HTTP 'GET /albums/{id}' operation and returns the [Response].
/// Parameters: /// Parameters:
/// ///
/// * [String] id (required): /// * [String] id (required):
@@ -269,7 +269,7 @@ class AlbumApi {
/// * [bool] withoutAssets: /// * [bool] withoutAssets:
Future<Response> getAlbumInfoWithHttpInfo(String id, { String? key, bool? withoutAssets, }) async { Future<Response> getAlbumInfoWithHttpInfo(String id, { String? key, bool? withoutAssets, }) async {
// ignore: prefer_const_declarations // ignore: prefer_const_declarations
final path = r'/album/{id}' final path = r'/albums/{id}'
.replaceAll('{id}', id); .replaceAll('{id}', id);
// ignore: prefer_final_locals // ignore: prefer_final_locals
@@ -322,7 +322,7 @@ class AlbumApi {
return null; return null;
} }
/// Performs an HTTP 'GET /album' operation and returns the [Response]. /// Performs an HTTP 'GET /albums' operation and returns the [Response].
/// Parameters: /// Parameters:
/// ///
/// * [String] assetId: /// * [String] assetId:
@@ -331,7 +331,7 @@ class AlbumApi {
/// * [bool] shared: /// * [bool] shared:
Future<Response> getAllAlbumsWithHttpInfo({ String? assetId, bool? shared, }) async { Future<Response> getAllAlbumsWithHttpInfo({ String? assetId, bool? shared, }) async {
// ignore: prefer_const_declarations // ignore: prefer_const_declarations
final path = r'/album'; final path = r'/albums';
// ignore: prefer_final_locals // ignore: prefer_final_locals
Object? postBody; Object? postBody;
@@ -385,7 +385,7 @@ class AlbumApi {
return null; return null;
} }
/// Performs an HTTP 'DELETE /album/{id}/assets' operation and returns the [Response]. /// Performs an HTTP 'DELETE /albums/{id}/assets' operation and returns the [Response].
/// Parameters: /// Parameters:
/// ///
/// * [String] id (required): /// * [String] id (required):
@@ -393,7 +393,7 @@ class AlbumApi {
/// * [BulkIdsDto] bulkIdsDto (required): /// * [BulkIdsDto] bulkIdsDto (required):
Future<Response> removeAssetFromAlbumWithHttpInfo(String id, BulkIdsDto bulkIdsDto,) async { Future<Response> removeAssetFromAlbumWithHttpInfo(String id, BulkIdsDto bulkIdsDto,) async {
// ignore: prefer_const_declarations // ignore: prefer_const_declarations
final path = r'/album/{id}/assets' final path = r'/albums/{id}/assets'
.replaceAll('{id}', id); .replaceAll('{id}', id);
// ignore: prefer_final_locals // ignore: prefer_final_locals
@@ -440,7 +440,7 @@ class AlbumApi {
return null; return null;
} }
/// Performs an HTTP 'DELETE /album/{id}/user/{userId}' operation and returns the [Response]. /// Performs an HTTP 'DELETE /albums/{id}/user/{userId}' operation and returns the [Response].
/// Parameters: /// Parameters:
/// ///
/// * [String] id (required): /// * [String] id (required):
@@ -448,7 +448,7 @@ class AlbumApi {
/// * [String] userId (required): /// * [String] userId (required):
Future<Response> removeUserFromAlbumWithHttpInfo(String id, String userId,) async { Future<Response> removeUserFromAlbumWithHttpInfo(String id, String userId,) async {
// ignore: prefer_const_declarations // ignore: prefer_const_declarations
final path = r'/album/{id}/user/{userId}' final path = r'/albums/{id}/user/{userId}'
.replaceAll('{id}', id) .replaceAll('{id}', id)
.replaceAll('{userId}', userId); .replaceAll('{userId}', userId);
@@ -485,7 +485,7 @@ class AlbumApi {
} }
} }
/// Performs an HTTP 'PATCH /album/{id}' operation and returns the [Response]. /// Performs an HTTP 'PATCH /albums/{id}' operation and returns the [Response].
/// Parameters: /// Parameters:
/// ///
/// * [String] id (required): /// * [String] id (required):
@@ -493,7 +493,7 @@ class AlbumApi {
/// * [UpdateAlbumDto] updateAlbumDto (required): /// * [UpdateAlbumDto] updateAlbumDto (required):
Future<Response> updateAlbumInfoWithHttpInfo(String id, UpdateAlbumDto updateAlbumDto,) async { Future<Response> updateAlbumInfoWithHttpInfo(String id, UpdateAlbumDto updateAlbumDto,) async {
// ignore: prefer_const_declarations // ignore: prefer_const_declarations
final path = r'/album/{id}' final path = r'/albums/{id}'
.replaceAll('{id}', id); .replaceAll('{id}', id);
// ignore: prefer_final_locals // ignore: prefer_final_locals
@@ -537,7 +537,7 @@ class AlbumApi {
return null; return null;
} }
/// Performs an HTTP 'PUT /album/{id}/user/{userId}' operation and returns the [Response]. /// Performs an HTTP 'PUT /albums/{id}/user/{userId}' operation and returns the [Response].
/// Parameters: /// Parameters:
/// ///
/// * [String] id (required): /// * [String] id (required):
@@ -547,7 +547,7 @@ class AlbumApi {
/// * [UpdateAlbumUserDto] updateAlbumUserDto (required): /// * [UpdateAlbumUserDto] updateAlbumUserDto (required):
Future<Response> updateAlbumUserWithHttpInfo(String id, String userId, UpdateAlbumUserDto updateAlbumUserDto,) async { Future<Response> updateAlbumUserWithHttpInfo(String id, String userId, UpdateAlbumUserDto updateAlbumUserDto,) async {
// ignore: prefer_const_declarations // ignore: prefer_const_declarations
final path = r'/album/{id}/user/{userId}' final path = r'/albums/{id}/user/{userId}'
.replaceAll('{id}', id) .replaceAll('{id}', id)
.replaceAll('{userId}', userId); .replaceAll('{userId}', userId);

View File

@@ -16,13 +16,13 @@ class APIKeyApi {
final ApiClient apiClient; final ApiClient apiClient;
/// Performs an HTTP 'POST /api-key' operation and returns the [Response]. /// Performs an HTTP 'POST /api-keys' operation and returns the [Response].
/// Parameters: /// Parameters:
/// ///
/// * [APIKeyCreateDto] aPIKeyCreateDto (required): /// * [APIKeyCreateDto] aPIKeyCreateDto (required):
Future<Response> createApiKeyWithHttpInfo(APIKeyCreateDto aPIKeyCreateDto,) async { Future<Response> createApiKeyWithHttpInfo(APIKeyCreateDto aPIKeyCreateDto,) async {
// ignore: prefer_const_declarations // ignore: prefer_const_declarations
final path = r'/api-key'; final path = r'/api-keys';
// ignore: prefer_final_locals // ignore: prefer_final_locals
Object? postBody = aPIKeyCreateDto; Object? postBody = aPIKeyCreateDto;
@@ -63,13 +63,13 @@ class APIKeyApi {
return null; return null;
} }
/// Performs an HTTP 'DELETE /api-key/{id}' operation and returns the [Response]. /// Performs an HTTP 'DELETE /api-keys/{id}' operation and returns the [Response].
/// Parameters: /// Parameters:
/// ///
/// * [String] id (required): /// * [String] id (required):
Future<Response> deleteApiKeyWithHttpInfo(String id,) async { Future<Response> deleteApiKeyWithHttpInfo(String id,) async {
// ignore: prefer_const_declarations // ignore: prefer_const_declarations
final path = r'/api-key/{id}' final path = r'/api-keys/{id}'
.replaceAll('{id}', id); .replaceAll('{id}', id);
// ignore: prefer_final_locals // ignore: prefer_final_locals
@@ -103,13 +103,13 @@ class APIKeyApi {
} }
} }
/// Performs an HTTP 'GET /api-key/{id}' operation and returns the [Response]. /// Performs an HTTP 'GET /api-keys/{id}' operation and returns the [Response].
/// Parameters: /// Parameters:
/// ///
/// * [String] id (required): /// * [String] id (required):
Future<Response> getApiKeyWithHttpInfo(String id,) async { Future<Response> getApiKeyWithHttpInfo(String id,) async {
// ignore: prefer_const_declarations // ignore: prefer_const_declarations
final path = r'/api-key/{id}' final path = r'/api-keys/{id}'
.replaceAll('{id}', id); .replaceAll('{id}', id);
// ignore: prefer_final_locals // ignore: prefer_final_locals
@@ -151,10 +151,10 @@ class APIKeyApi {
return null; return null;
} }
/// Performs an HTTP 'GET /api-key' operation and returns the [Response]. /// Performs an HTTP 'GET /api-keys' operation and returns the [Response].
Future<Response> getApiKeysWithHttpInfo() async { Future<Response> getApiKeysWithHttpInfo() async {
// ignore: prefer_const_declarations // ignore: prefer_const_declarations
final path = r'/api-key'; final path = r'/api-keys';
// ignore: prefer_final_locals // ignore: prefer_final_locals
Object? postBody; Object? postBody;
@@ -195,7 +195,7 @@ class APIKeyApi {
return null; return null;
} }
/// Performs an HTTP 'PUT /api-key/{id}' operation and returns the [Response]. /// Performs an HTTP 'PUT /api-keys/{id}' operation and returns the [Response].
/// Parameters: /// Parameters:
/// ///
/// * [String] id (required): /// * [String] id (required):
@@ -203,7 +203,7 @@ class APIKeyApi {
/// * [APIKeyUpdateDto] aPIKeyUpdateDto (required): /// * [APIKeyUpdateDto] aPIKeyUpdateDto (required):
Future<Response> updateApiKeyWithHttpInfo(String id, APIKeyUpdateDto aPIKeyUpdateDto,) async { Future<Response> updateApiKeyWithHttpInfo(String id, APIKeyUpdateDto aPIKeyUpdateDto,) async {
// ignore: prefer_const_declarations // ignore: prefer_const_declarations
final path = r'/api-key/{id}' final path = r'/api-keys/{id}'
.replaceAll('{id}', id); .replaceAll('{id}', id);
// ignore: prefer_final_locals // ignore: prefer_final_locals

View File

@@ -159,117 +159,6 @@ class AssetApi {
} }
} }
/// Get all AssetEntity belong to the user
///
/// Note: This method returns the HTTP [Response].
///
/// Parameters:
///
/// * [String] ifNoneMatch:
/// ETag of data already cached on the client
///
/// * [bool] isArchived:
///
/// * [bool] isFavorite:
///
/// * [int] skip:
///
/// * [int] take:
///
/// * [DateTime] updatedAfter:
///
/// * [DateTime] updatedBefore:
///
/// * [String] userId:
Future<Response> getAllAssetsWithHttpInfo({ String? ifNoneMatch, bool? isArchived, bool? isFavorite, int? skip, int? take, DateTime? updatedAfter, DateTime? updatedBefore, String? userId, }) async {
// ignore: prefer_const_declarations
final path = r'/asset';
// ignore: prefer_final_locals
Object? postBody;
final queryParams = <QueryParam>[];
final headerParams = <String, String>{};
final formParams = <String, String>{};
if (isArchived != null) {
queryParams.addAll(_queryParams('', 'isArchived', isArchived));
}
if (isFavorite != null) {
queryParams.addAll(_queryParams('', 'isFavorite', isFavorite));
}
if (skip != null) {
queryParams.addAll(_queryParams('', 'skip', skip));
}
if (take != null) {
queryParams.addAll(_queryParams('', 'take', take));
}
if (updatedAfter != null) {
queryParams.addAll(_queryParams('', 'updatedAfter', updatedAfter));
}
if (updatedBefore != null) {
queryParams.addAll(_queryParams('', 'updatedBefore', updatedBefore));
}
if (userId != null) {
queryParams.addAll(_queryParams('', 'userId', userId));
}
if (ifNoneMatch != null) {
headerParams[r'if-none-match'] = parameterToString(ifNoneMatch);
}
const contentTypes = <String>[];
return apiClient.invokeAPI(
path,
'GET',
queryParams,
postBody,
headerParams,
formParams,
contentTypes.isEmpty ? null : contentTypes.first,
);
}
/// Get all AssetEntity belong to the user
///
/// Parameters:
///
/// * [String] ifNoneMatch:
/// ETag of data already cached on the client
///
/// * [bool] isArchived:
///
/// * [bool] isFavorite:
///
/// * [int] skip:
///
/// * [int] take:
///
/// * [DateTime] updatedAfter:
///
/// * [DateTime] updatedBefore:
///
/// * [String] userId:
Future<List<AssetResponseDto>?> getAllAssets({ String? ifNoneMatch, bool? isArchived, bool? isFavorite, int? skip, int? take, DateTime? updatedAfter, DateTime? updatedBefore, String? userId, }) async {
final response = await getAllAssetsWithHttpInfo( ifNoneMatch: ifNoneMatch, isArchived: isArchived, isFavorite: isFavorite, skip: skip, take: take, updatedAfter: updatedAfter, updatedBefore: updatedBefore, userId: userId, );
if (response.statusCode >= HttpStatus.badRequest) {
throw ApiException(response.statusCode, await _decodeBodyBytes(response));
}
// When a remote server returns no body with a status of 204, we shall not decode it.
// At the time of writing this, `dart:convert` will throw an "Unexpected end of input"
// FormatException when trying to decode an empty string.
if (response.body.isNotEmpty && response.statusCode != HttpStatus.noContent) {
final responseBody = await _decodeBodyBytes(response);
return (await apiClient.deserializeAsync(responseBody, 'List<AssetResponseDto>') as List)
.cast<AssetResponseDto>()
.toList(growable: false);
}
return null;
}
/// Get all asset of a device that are in the database, ID only. /// Get all asset of a device that are in the database, ID only.
/// ///
/// Note: This method returns the HTTP [Response]. /// Note: This method returns the HTTP [Response].
@@ -710,6 +599,121 @@ class AssetApi {
return null; return null;
} }
/// Replace the asset with new file, without changing its id
///
/// Note: This method returns the HTTP [Response].
///
/// Parameters:
///
/// * [String] id (required):
///
/// * [MultipartFile] assetData (required):
///
/// * [String] deviceAssetId (required):
///
/// * [String] deviceId (required):
///
/// * [DateTime] fileCreatedAt (required):
///
/// * [DateTime] fileModifiedAt (required):
///
/// * [String] key:
///
/// * [String] duration:
Future<Response> replaceAssetWithHttpInfo(String id, MultipartFile assetData, String deviceAssetId, String deviceId, DateTime fileCreatedAt, DateTime fileModifiedAt, { String? key, String? duration, }) async {
// ignore: prefer_const_declarations
final path = r'/asset/{id}/file'
.replaceAll('{id}', id);
// ignore: prefer_final_locals
Object? postBody;
final queryParams = <QueryParam>[];
final headerParams = <String, String>{};
final formParams = <String, String>{};
if (key != null) {
queryParams.addAll(_queryParams('', 'key', key));
}
const contentTypes = <String>['multipart/form-data'];
bool hasFields = false;
final mp = MultipartRequest('PUT', Uri.parse(path));
if (assetData != null) {
hasFields = true;
mp.fields[r'assetData'] = assetData.field;
mp.files.add(assetData);
}
if (deviceAssetId != null) {
hasFields = true;
mp.fields[r'deviceAssetId'] = parameterToString(deviceAssetId);
}
if (deviceId != null) {
hasFields = true;
mp.fields[r'deviceId'] = parameterToString(deviceId);
}
if (duration != null) {
hasFields = true;
mp.fields[r'duration'] = parameterToString(duration);
}
if (fileCreatedAt != null) {
hasFields = true;
mp.fields[r'fileCreatedAt'] = parameterToString(fileCreatedAt);
}
if (fileModifiedAt != null) {
hasFields = true;
mp.fields[r'fileModifiedAt'] = parameterToString(fileModifiedAt);
}
if (hasFields) {
postBody = mp;
}
return apiClient.invokeAPI(
path,
'PUT',
queryParams,
postBody,
headerParams,
formParams,
contentTypes.isEmpty ? null : contentTypes.first,
);
}
/// Replace the asset with new file, without changing its id
///
/// Parameters:
///
/// * [String] id (required):
///
/// * [MultipartFile] assetData (required):
///
/// * [String] deviceAssetId (required):
///
/// * [String] deviceId (required):
///
/// * [DateTime] fileCreatedAt (required):
///
/// * [DateTime] fileModifiedAt (required):
///
/// * [String] key:
///
/// * [String] duration:
Future<AssetMediaResponseDto?> replaceAsset(String id, MultipartFile assetData, String deviceAssetId, String deviceId, DateTime fileCreatedAt, DateTime fileModifiedAt, { String? key, String? duration, }) async {
final response = await replaceAssetWithHttpInfo(id, assetData, deviceAssetId, deviceId, fileCreatedAt, fileModifiedAt, key: key, duration: duration, );
if (response.statusCode >= HttpStatus.badRequest) {
throw ApiException(response.statusCode, await _decodeBodyBytes(response));
}
// When a remote server returns no body with a status of 204, we shall not decode it.
// At the time of writing this, `dart:convert` will throw an "Unexpected end of input"
// FormatException when trying to decode an empty string.
if (response.body.isNotEmpty && response.statusCode != HttpStatus.noContent) {
return await apiClient.deserializeAsync(await _decodeBodyBytes(response), 'AssetMediaResponseDto',) as AssetMediaResponseDto;
}
return null;
}
/// Performs an HTTP 'POST /asset/jobs' operation and returns the [Response]. /// Performs an HTTP 'POST /asset/jobs' operation and returns the [Response].
/// Parameters: /// Parameters:
/// ///

View File

@@ -48,7 +48,7 @@ class AuthenticationApi {
/// Parameters: /// Parameters:
/// ///
/// * [ChangePasswordDto] changePasswordDto (required): /// * [ChangePasswordDto] changePasswordDto (required):
Future<UserResponseDto?> changePassword(ChangePasswordDto changePasswordDto,) async { Future<UserAdminResponseDto?> changePassword(ChangePasswordDto changePasswordDto,) async {
final response = await changePasswordWithHttpInfo(changePasswordDto,); final response = await changePasswordWithHttpInfo(changePasswordDto,);
if (response.statusCode >= HttpStatus.badRequest) { if (response.statusCode >= HttpStatus.badRequest) {
throw ApiException(response.statusCode, await _decodeBodyBytes(response)); throw ApiException(response.statusCode, await _decodeBodyBytes(response));
@@ -57,7 +57,7 @@ class AuthenticationApi {
// At the time of writing this, `dart:convert` will throw an "Unexpected end of input" // At the time of writing this, `dart:convert` will throw an "Unexpected end of input"
// FormatException when trying to decode an empty string. // FormatException when trying to decode an empty string.
if (response.body.isNotEmpty && response.statusCode != HttpStatus.noContent) { if (response.body.isNotEmpty && response.statusCode != HttpStatus.noContent) {
return await apiClient.deserializeAsync(await _decodeBodyBytes(response), 'UserResponseDto',) as UserResponseDto; return await apiClient.deserializeAsync(await _decodeBodyBytes(response), 'UserAdminResponseDto',) as UserAdminResponseDto;
} }
return null; return null;
@@ -183,7 +183,7 @@ class AuthenticationApi {
/// Parameters: /// Parameters:
/// ///
/// * [SignUpDto] signUpDto (required): /// * [SignUpDto] signUpDto (required):
Future<UserResponseDto?> signUpAdmin(SignUpDto signUpDto,) async { Future<UserAdminResponseDto?> signUpAdmin(SignUpDto signUpDto,) async {
final response = await signUpAdminWithHttpInfo(signUpDto,); final response = await signUpAdminWithHttpInfo(signUpDto,);
if (response.statusCode >= HttpStatus.badRequest) { if (response.statusCode >= HttpStatus.badRequest) {
throw ApiException(response.statusCode, await _decodeBodyBytes(response)); throw ApiException(response.statusCode, await _decodeBodyBytes(response));
@@ -192,7 +192,7 @@ class AuthenticationApi {
// At the time of writing this, `dart:convert` will throw an "Unexpected end of input" // At the time of writing this, `dart:convert` will throw an "Unexpected end of input"
// FormatException when trying to decode an empty string. // FormatException when trying to decode an empty string.
if (response.body.isNotEmpty && response.statusCode != HttpStatus.noContent) { if (response.body.isNotEmpty && response.statusCode != HttpStatus.noContent) {
return await apiClient.deserializeAsync(await _decodeBodyBytes(response), 'UserResponseDto',) as UserResponseDto; return await apiClient.deserializeAsync(await _decodeBodyBytes(response), 'UserAdminResponseDto',) as UserAdminResponseDto;
} }
return null; return null;

View File

@@ -16,13 +16,13 @@ class FaceApi {
final ApiClient apiClient; final ApiClient apiClient;
/// Performs an HTTP 'GET /face' operation and returns the [Response]. /// Performs an HTTP 'GET /faces' operation and returns the [Response].
/// Parameters: /// Parameters:
/// ///
/// * [String] id (required): /// * [String] id (required):
Future<Response> getFacesWithHttpInfo(String id,) async { Future<Response> getFacesWithHttpInfo(String id,) async {
// ignore: prefer_const_declarations // ignore: prefer_const_declarations
final path = r'/face'; final path = r'/faces';
// ignore: prefer_final_locals // ignore: prefer_final_locals
Object? postBody; Object? postBody;
@@ -68,7 +68,7 @@ class FaceApi {
return null; return null;
} }
/// Performs an HTTP 'PUT /face/{id}' operation and returns the [Response]. /// Performs an HTTP 'PUT /faces/{id}' operation and returns the [Response].
/// Parameters: /// Parameters:
/// ///
/// * [String] id (required): /// * [String] id (required):
@@ -76,7 +76,7 @@ class FaceApi {
/// * [FaceDto] faceDto (required): /// * [FaceDto] faceDto (required):
Future<Response> reassignFacesByIdWithHttpInfo(String id, FaceDto faceDto,) async { Future<Response> reassignFacesByIdWithHttpInfo(String id, FaceDto faceDto,) async {
// ignore: prefer_const_declarations // ignore: prefer_const_declarations
final path = r'/face/{id}' final path = r'/faces/{id}'
.replaceAll('{id}', id); .replaceAll('{id}', id);
// ignore: prefer_final_locals // ignore: prefer_final_locals
@@ -119,4 +119,52 @@ class FaceApi {
} }
return null; return null;
} }
/// Performs an HTTP 'DELETE /faces/{id}' operation and returns the [Response].
/// Parameters:
///
/// * [String] id (required):
Future<Response> unassignFaceWithHttpInfo(String id,) async {
// ignore: prefer_const_declarations
final path = r'/faces/{id}'
.replaceAll('{id}', id);
// ignore: prefer_final_locals
Object? postBody;
final queryParams = <QueryParam>[];
final headerParams = <String, String>{};
final formParams = <String, String>{};
const contentTypes = <String>[];
return apiClient.invokeAPI(
path,
'DELETE',
queryParams,
postBody,
headerParams,
formParams,
contentTypes.isEmpty ? null : contentTypes.first,
);
}
/// Parameters:
///
/// * [String] id (required):
Future<AssetFaceResponseDto?> unassignFace(String id,) async {
final response = await unassignFaceWithHttpInfo(id,);
if (response.statusCode >= HttpStatus.badRequest) {
throw ApiException(response.statusCode, await _decodeBodyBytes(response));
}
// When a remote server returns no body with a status of 204, we shall not decode it.
// At the time of writing this, `dart:convert` will throw an "Unexpected end of input"
// FormatException when trying to decode an empty string.
if (response.body.isNotEmpty && response.statusCode != HttpStatus.noContent) {
return await apiClient.deserializeAsync(await _decodeBodyBytes(response), 'AssetFaceResponseDto',) as AssetFaceResponseDto;
}
return null;
}
} }

View File

@@ -16,13 +16,13 @@ class FileReportApi {
final ApiClient apiClient; final ApiClient apiClient;
/// Performs an HTTP 'POST /report/fix' operation and returns the [Response]. /// Performs an HTTP 'POST /reports/fix' operation and returns the [Response].
/// Parameters: /// Parameters:
/// ///
/// * [FileReportFixDto] fileReportFixDto (required): /// * [FileReportFixDto] fileReportFixDto (required):
Future<Response> fixAuditFilesWithHttpInfo(FileReportFixDto fileReportFixDto,) async { Future<Response> fixAuditFilesWithHttpInfo(FileReportFixDto fileReportFixDto,) async {
// ignore: prefer_const_declarations // ignore: prefer_const_declarations
final path = r'/report/fix'; final path = r'/reports/fix';
// ignore: prefer_final_locals // ignore: prefer_final_locals
Object? postBody = fileReportFixDto; Object? postBody = fileReportFixDto;
@@ -55,10 +55,10 @@ class FileReportApi {
} }
} }
/// Performs an HTTP 'GET /report' operation and returns the [Response]. /// Performs an HTTP 'GET /reports' operation and returns the [Response].
Future<Response> getAuditFilesWithHttpInfo() async { Future<Response> getAuditFilesWithHttpInfo() async {
// ignore: prefer_const_declarations // ignore: prefer_const_declarations
final path = r'/report'; final path = r'/reports';
// ignore: prefer_final_locals // ignore: prefer_final_locals
Object? postBody; Object? postBody;
@@ -96,13 +96,13 @@ class FileReportApi {
return null; return null;
} }
/// Performs an HTTP 'POST /report/checksum' operation and returns the [Response]. /// Performs an HTTP 'POST /reports/checksum' operation and returns the [Response].
/// Parameters: /// Parameters:
/// ///
/// * [FileChecksumDto] fileChecksumDto (required): /// * [FileChecksumDto] fileChecksumDto (required):
Future<Response> getFileChecksumsWithHttpInfo(FileChecksumDto fileChecksumDto,) async { Future<Response> getFileChecksumsWithHttpInfo(FileChecksumDto fileChecksumDto,) async {
// ignore: prefer_const_declarations // ignore: prefer_const_declarations
final path = r'/report/checksum'; final path = r'/reports/checksum';
// ignore: prefer_final_locals // ignore: prefer_final_locals
Object? postBody = fileChecksumDto; Object? postBody = fileChecksumDto;

View File

@@ -16,13 +16,13 @@ class LibraryApi {
final ApiClient apiClient; final ApiClient apiClient;
/// Performs an HTTP 'POST /library' operation and returns the [Response]. /// Performs an HTTP 'POST /libraries' operation and returns the [Response].
/// Parameters: /// Parameters:
/// ///
/// * [CreateLibraryDto] createLibraryDto (required): /// * [CreateLibraryDto] createLibraryDto (required):
Future<Response> createLibraryWithHttpInfo(CreateLibraryDto createLibraryDto,) async { Future<Response> createLibraryWithHttpInfo(CreateLibraryDto createLibraryDto,) async {
// ignore: prefer_const_declarations // ignore: prefer_const_declarations
final path = r'/library'; final path = r'/libraries';
// ignore: prefer_final_locals // ignore: prefer_final_locals
Object? postBody = createLibraryDto; Object? postBody = createLibraryDto;
@@ -63,13 +63,13 @@ class LibraryApi {
return null; return null;
} }
/// Performs an HTTP 'DELETE /library/{id}' operation and returns the [Response]. /// Performs an HTTP 'DELETE /libraries/{id}' operation and returns the [Response].
/// Parameters: /// Parameters:
/// ///
/// * [String] id (required): /// * [String] id (required):
Future<Response> deleteLibraryWithHttpInfo(String id,) async { Future<Response> deleteLibraryWithHttpInfo(String id,) async {
// ignore: prefer_const_declarations // ignore: prefer_const_declarations
final path = r'/library/{id}' final path = r'/libraries/{id}'
.replaceAll('{id}', id); .replaceAll('{id}', id);
// ignore: prefer_final_locals // ignore: prefer_final_locals
@@ -103,10 +103,10 @@ class LibraryApi {
} }
} }
/// Performs an HTTP 'GET /library' operation and returns the [Response]. /// Performs an HTTP 'GET /libraries' operation and returns the [Response].
Future<Response> getAllLibrariesWithHttpInfo() async { Future<Response> getAllLibrariesWithHttpInfo() async {
// ignore: prefer_const_declarations // ignore: prefer_const_declarations
final path = r'/library'; final path = r'/libraries';
// ignore: prefer_final_locals // ignore: prefer_final_locals
Object? postBody; Object? postBody;
@@ -147,13 +147,13 @@ class LibraryApi {
return null; return null;
} }
/// Performs an HTTP 'GET /library/{id}' operation and returns the [Response]. /// Performs an HTTP 'GET /libraries/{id}' operation and returns the [Response].
/// Parameters: /// Parameters:
/// ///
/// * [String] id (required): /// * [String] id (required):
Future<Response> getLibraryWithHttpInfo(String id,) async { Future<Response> getLibraryWithHttpInfo(String id,) async {
// ignore: prefer_const_declarations // ignore: prefer_const_declarations
final path = r'/library/{id}' final path = r'/libraries/{id}'
.replaceAll('{id}', id); .replaceAll('{id}', id);
// ignore: prefer_final_locals // ignore: prefer_final_locals
@@ -195,13 +195,13 @@ class LibraryApi {
return null; return null;
} }
/// Performs an HTTP 'GET /library/{id}/statistics' operation and returns the [Response]. /// Performs an HTTP 'GET /libraries/{id}/statistics' operation and returns the [Response].
/// Parameters: /// Parameters:
/// ///
/// * [String] id (required): /// * [String] id (required):
Future<Response> getLibraryStatisticsWithHttpInfo(String id,) async { Future<Response> getLibraryStatisticsWithHttpInfo(String id,) async {
// ignore: prefer_const_declarations // ignore: prefer_const_declarations
final path = r'/library/{id}/statistics' final path = r'/libraries/{id}/statistics'
.replaceAll('{id}', id); .replaceAll('{id}', id);
// ignore: prefer_final_locals // ignore: prefer_final_locals
@@ -243,13 +243,13 @@ class LibraryApi {
return null; return null;
} }
/// Performs an HTTP 'POST /library/{id}/removeOffline' operation and returns the [Response]. /// Performs an HTTP 'POST /libraries/{id}/removeOffline' operation and returns the [Response].
/// Parameters: /// Parameters:
/// ///
/// * [String] id (required): /// * [String] id (required):
Future<Response> removeOfflineFilesWithHttpInfo(String id,) async { Future<Response> removeOfflineFilesWithHttpInfo(String id,) async {
// ignore: prefer_const_declarations // ignore: prefer_const_declarations
final path = r'/library/{id}/removeOffline' final path = r'/libraries/{id}/removeOffline'
.replaceAll('{id}', id); .replaceAll('{id}', id);
// ignore: prefer_final_locals // ignore: prefer_final_locals
@@ -283,7 +283,7 @@ class LibraryApi {
} }
} }
/// Performs an HTTP 'POST /library/{id}/scan' operation and returns the [Response]. /// Performs an HTTP 'POST /libraries/{id}/scan' operation and returns the [Response].
/// Parameters: /// Parameters:
/// ///
/// * [String] id (required): /// * [String] id (required):
@@ -291,7 +291,7 @@ class LibraryApi {
/// * [ScanLibraryDto] scanLibraryDto (required): /// * [ScanLibraryDto] scanLibraryDto (required):
Future<Response> scanLibraryWithHttpInfo(String id, ScanLibraryDto scanLibraryDto,) async { Future<Response> scanLibraryWithHttpInfo(String id, ScanLibraryDto scanLibraryDto,) async {
// ignore: prefer_const_declarations // ignore: prefer_const_declarations
final path = r'/library/{id}/scan' final path = r'/libraries/{id}/scan'
.replaceAll('{id}', id); .replaceAll('{id}', id);
// ignore: prefer_final_locals // ignore: prefer_final_locals
@@ -327,7 +327,7 @@ class LibraryApi {
} }
} }
/// Performs an HTTP 'PUT /library/{id}' operation and returns the [Response]. /// Performs an HTTP 'PUT /libraries/{id}' operation and returns the [Response].
/// Parameters: /// Parameters:
/// ///
/// * [String] id (required): /// * [String] id (required):
@@ -335,7 +335,7 @@ class LibraryApi {
/// * [UpdateLibraryDto] updateLibraryDto (required): /// * [UpdateLibraryDto] updateLibraryDto (required):
Future<Response> updateLibraryWithHttpInfo(String id, UpdateLibraryDto updateLibraryDto,) async { Future<Response> updateLibraryWithHttpInfo(String id, UpdateLibraryDto updateLibraryDto,) async {
// ignore: prefer_const_declarations // ignore: prefer_const_declarations
final path = r'/library/{id}' final path = r'/libraries/{id}'
.replaceAll('{id}', id); .replaceAll('{id}', id);
// ignore: prefer_final_locals // ignore: prefer_final_locals
@@ -379,7 +379,7 @@ class LibraryApi {
return null; return null;
} }
/// Performs an HTTP 'POST /library/{id}/validate' operation and returns the [Response]. /// Performs an HTTP 'POST /libraries/{id}/validate' operation and returns the [Response].
/// Parameters: /// Parameters:
/// ///
/// * [String] id (required): /// * [String] id (required):
@@ -387,7 +387,7 @@ class LibraryApi {
/// * [ValidateLibraryDto] validateLibraryDto (required): /// * [ValidateLibraryDto] validateLibraryDto (required):
Future<Response> validateWithHttpInfo(String id, ValidateLibraryDto validateLibraryDto,) async { Future<Response> validateWithHttpInfo(String id, ValidateLibraryDto validateLibraryDto,) async {
// ignore: prefer_const_declarations // ignore: prefer_const_declarations
final path = r'/library/{id}/validate' final path = r'/libraries/{id}/validate'
.replaceAll('{id}', id); .replaceAll('{id}', id);
// ignore: prefer_final_locals // ignore: prefer_final_locals

View File

@@ -95,7 +95,7 @@ class OAuthApi {
/// Parameters: /// Parameters:
/// ///
/// * [OAuthCallbackDto] oAuthCallbackDto (required): /// * [OAuthCallbackDto] oAuthCallbackDto (required):
Future<UserResponseDto?> linkOAuthAccount(OAuthCallbackDto oAuthCallbackDto,) async { Future<UserAdminResponseDto?> linkOAuthAccount(OAuthCallbackDto oAuthCallbackDto,) async {
final response = await linkOAuthAccountWithHttpInfo(oAuthCallbackDto,); final response = await linkOAuthAccountWithHttpInfo(oAuthCallbackDto,);
if (response.statusCode >= HttpStatus.badRequest) { if (response.statusCode >= HttpStatus.badRequest) {
throw ApiException(response.statusCode, await _decodeBodyBytes(response)); throw ApiException(response.statusCode, await _decodeBodyBytes(response));
@@ -104,7 +104,7 @@ class OAuthApi {
// At the time of writing this, `dart:convert` will throw an "Unexpected end of input" // At the time of writing this, `dart:convert` will throw an "Unexpected end of input"
// FormatException when trying to decode an empty string. // FormatException when trying to decode an empty string.
if (response.body.isNotEmpty && response.statusCode != HttpStatus.noContent) { if (response.body.isNotEmpty && response.statusCode != HttpStatus.noContent) {
return await apiClient.deserializeAsync(await _decodeBodyBytes(response), 'UserResponseDto',) as UserResponseDto; return await apiClient.deserializeAsync(await _decodeBodyBytes(response), 'UserAdminResponseDto',) as UserAdminResponseDto;
} }
return null; return null;
@@ -216,7 +216,7 @@ class OAuthApi {
); );
} }
Future<UserResponseDto?> unlinkOAuthAccount() async { Future<UserAdminResponseDto?> unlinkOAuthAccount() async {
final response = await unlinkOAuthAccountWithHttpInfo(); final response = await unlinkOAuthAccountWithHttpInfo();
if (response.statusCode >= HttpStatus.badRequest) { if (response.statusCode >= HttpStatus.badRequest) {
throw ApiException(response.statusCode, await _decodeBodyBytes(response)); throw ApiException(response.statusCode, await _decodeBodyBytes(response));
@@ -225,7 +225,7 @@ class OAuthApi {
// At the time of writing this, `dart:convert` will throw an "Unexpected end of input" // At the time of writing this, `dart:convert` will throw an "Unexpected end of input"
// FormatException when trying to decode an empty string. // FormatException when trying to decode an empty string.
if (response.body.isNotEmpty && response.statusCode != HttpStatus.noContent) { if (response.body.isNotEmpty && response.statusCode != HttpStatus.noContent) {
return await apiClient.deserializeAsync(await _decodeBodyBytes(response), 'UserResponseDto',) as UserResponseDto; return await apiClient.deserializeAsync(await _decodeBodyBytes(response), 'UserAdminResponseDto',) as UserAdminResponseDto;
} }
return null; return null;

View File

@@ -16,13 +16,13 @@ class PartnerApi {
final ApiClient apiClient; final ApiClient apiClient;
/// Performs an HTTP 'POST /partner/{id}' operation and returns the [Response]. /// Performs an HTTP 'POST /partners/{id}' operation and returns the [Response].
/// Parameters: /// Parameters:
/// ///
/// * [String] id (required): /// * [String] id (required):
Future<Response> createPartnerWithHttpInfo(String id,) async { Future<Response> createPartnerWithHttpInfo(String id,) async {
// ignore: prefer_const_declarations // ignore: prefer_const_declarations
final path = r'/partner/{id}' final path = r'/partners/{id}'
.replaceAll('{id}', id); .replaceAll('{id}', id);
// ignore: prefer_final_locals // ignore: prefer_final_locals
@@ -64,13 +64,13 @@ class PartnerApi {
return null; return null;
} }
/// Performs an HTTP 'GET /partner' operation and returns the [Response]. /// Performs an HTTP 'GET /partners' operation and returns the [Response].
/// Parameters: /// Parameters:
/// ///
/// * [String] direction (required): /// * [String] direction (required):
Future<Response> getPartnersWithHttpInfo(String direction,) async { Future<Response> getPartnersWithHttpInfo(String direction,) async {
// ignore: prefer_const_declarations // ignore: prefer_const_declarations
final path = r'/partner'; final path = r'/partners';
// ignore: prefer_final_locals // ignore: prefer_final_locals
Object? postBody; Object? postBody;
@@ -116,13 +116,13 @@ class PartnerApi {
return null; return null;
} }
/// Performs an HTTP 'DELETE /partner/{id}' operation and returns the [Response]. /// Performs an HTTP 'DELETE /partners/{id}' operation and returns the [Response].
/// Parameters: /// Parameters:
/// ///
/// * [String] id (required): /// * [String] id (required):
Future<Response> removePartnerWithHttpInfo(String id,) async { Future<Response> removePartnerWithHttpInfo(String id,) async {
// ignore: prefer_const_declarations // ignore: prefer_const_declarations
final path = r'/partner/{id}' final path = r'/partners/{id}'
.replaceAll('{id}', id); .replaceAll('{id}', id);
// ignore: prefer_final_locals // ignore: prefer_final_locals
@@ -156,7 +156,7 @@ class PartnerApi {
} }
} }
/// Performs an HTTP 'PUT /partner/{id}' operation and returns the [Response]. /// Performs an HTTP 'PUT /partners/{id}' operation and returns the [Response].
/// Parameters: /// Parameters:
/// ///
/// * [String] id (required): /// * [String] id (required):
@@ -164,7 +164,7 @@ class PartnerApi {
/// * [UpdatePartnerDto] updatePartnerDto (required): /// * [UpdatePartnerDto] updatePartnerDto (required):
Future<Response> updatePartnerWithHttpInfo(String id, UpdatePartnerDto updatePartnerDto,) async { Future<Response> updatePartnerWithHttpInfo(String id, UpdatePartnerDto updatePartnerDto,) async {
// ignore: prefer_const_declarations // ignore: prefer_const_declarations
final path = r'/partner/{id}' final path = r'/partners/{id}'
.replaceAll('{id}', id); .replaceAll('{id}', id);
// ignore: prefer_final_locals // ignore: prefer_final_locals

View File

@@ -16,13 +16,13 @@ class PersonApi {
final ApiClient apiClient; final ApiClient apiClient;
/// Performs an HTTP 'POST /person' operation and returns the [Response]. /// Performs an HTTP 'POST /people' operation and returns the [Response].
/// Parameters: /// Parameters:
/// ///
/// * [PersonCreateDto] personCreateDto (required): /// * [PersonCreateDto] personCreateDto (required):
Future<Response> createPersonWithHttpInfo(PersonCreateDto personCreateDto,) async { Future<Response> createPersonWithHttpInfo(PersonCreateDto personCreateDto,) async {
// ignore: prefer_const_declarations // ignore: prefer_const_declarations
final path = r'/person'; final path = r'/people';
// ignore: prefer_final_locals // ignore: prefer_final_locals
Object? postBody = personCreateDto; Object? postBody = personCreateDto;
@@ -63,13 +63,13 @@ class PersonApi {
return null; return null;
} }
/// Performs an HTTP 'GET /person' operation and returns the [Response]. /// Performs an HTTP 'GET /people' operation and returns the [Response].
/// Parameters: /// Parameters:
/// ///
/// * [bool] withHidden: /// * [bool] withHidden:
Future<Response> getAllPeopleWithHttpInfo({ bool? withHidden, }) async { Future<Response> getAllPeopleWithHttpInfo({ bool? withHidden, }) async {
// ignore: prefer_const_declarations // ignore: prefer_const_declarations
final path = r'/person'; final path = r'/people';
// ignore: prefer_final_locals // ignore: prefer_final_locals
Object? postBody; Object? postBody;
@@ -114,13 +114,13 @@ class PersonApi {
return null; return null;
} }
/// Performs an HTTP 'GET /person/{id}' operation and returns the [Response]. /// Performs an HTTP 'GET /people/{id}' operation and returns the [Response].
/// Parameters: /// Parameters:
/// ///
/// * [String] id (required): /// * [String] id (required):
Future<Response> getPersonWithHttpInfo(String id,) async { Future<Response> getPersonWithHttpInfo(String id,) async {
// ignore: prefer_const_declarations // ignore: prefer_const_declarations
final path = r'/person/{id}' final path = r'/people/{id}'
.replaceAll('{id}', id); .replaceAll('{id}', id);
// ignore: prefer_final_locals // ignore: prefer_final_locals
@@ -162,13 +162,13 @@ class PersonApi {
return null; return null;
} }
/// Performs an HTTP 'GET /person/{id}/assets' operation and returns the [Response]. /// Performs an HTTP 'GET /people/{id}/assets' operation and returns the [Response].
/// Parameters: /// Parameters:
/// ///
/// * [String] id (required): /// * [String] id (required):
Future<Response> getPersonAssetsWithHttpInfo(String id,) async { Future<Response> getPersonAssetsWithHttpInfo(String id,) async {
// ignore: prefer_const_declarations // ignore: prefer_const_declarations
final path = r'/person/{id}/assets' final path = r'/people/{id}/assets'
.replaceAll('{id}', id); .replaceAll('{id}', id);
// ignore: prefer_final_locals // ignore: prefer_final_locals
@@ -213,13 +213,13 @@ class PersonApi {
return null; return null;
} }
/// Performs an HTTP 'GET /person/{id}/statistics' operation and returns the [Response]. /// Performs an HTTP 'GET /people/{id}/statistics' operation and returns the [Response].
/// Parameters: /// Parameters:
/// ///
/// * [String] id (required): /// * [String] id (required):
Future<Response> getPersonStatisticsWithHttpInfo(String id,) async { Future<Response> getPersonStatisticsWithHttpInfo(String id,) async {
// ignore: prefer_const_declarations // ignore: prefer_const_declarations
final path = r'/person/{id}/statistics' final path = r'/people/{id}/statistics'
.replaceAll('{id}', id); .replaceAll('{id}', id);
// ignore: prefer_final_locals // ignore: prefer_final_locals
@@ -261,13 +261,13 @@ class PersonApi {
return null; return null;
} }
/// Performs an HTTP 'GET /person/{id}/thumbnail' operation and returns the [Response]. /// Performs an HTTP 'GET /people/{id}/thumbnail' operation and returns the [Response].
/// Parameters: /// Parameters:
/// ///
/// * [String] id (required): /// * [String] id (required):
Future<Response> getPersonThumbnailWithHttpInfo(String id,) async { Future<Response> getPersonThumbnailWithHttpInfo(String id,) async {
// ignore: prefer_const_declarations // ignore: prefer_const_declarations
final path = r'/person/{id}/thumbnail' final path = r'/people/{id}/thumbnail'
.replaceAll('{id}', id); .replaceAll('{id}', id);
// ignore: prefer_final_locals // ignore: prefer_final_locals
@@ -309,7 +309,7 @@ class PersonApi {
return null; return null;
} }
/// Performs an HTTP 'POST /person/{id}/merge' operation and returns the [Response]. /// Performs an HTTP 'POST /people/{id}/merge' operation and returns the [Response].
/// Parameters: /// Parameters:
/// ///
/// * [String] id (required): /// * [String] id (required):
@@ -317,7 +317,7 @@ class PersonApi {
/// * [MergePersonDto] mergePersonDto (required): /// * [MergePersonDto] mergePersonDto (required):
Future<Response> mergePersonWithHttpInfo(String id, MergePersonDto mergePersonDto,) async { Future<Response> mergePersonWithHttpInfo(String id, MergePersonDto mergePersonDto,) async {
// ignore: prefer_const_declarations // ignore: prefer_const_declarations
final path = r'/person/{id}/merge' final path = r'/people/{id}/merge'
.replaceAll('{id}', id); .replaceAll('{id}', id);
// ignore: prefer_final_locals // ignore: prefer_final_locals
@@ -364,7 +364,7 @@ class PersonApi {
return null; return null;
} }
/// Performs an HTTP 'PUT /person/{id}/reassign' operation and returns the [Response]. /// Performs an HTTP 'PUT /people/{id}/reassign' operation and returns the [Response].
/// Parameters: /// Parameters:
/// ///
/// * [String] id (required): /// * [String] id (required):
@@ -372,7 +372,7 @@ class PersonApi {
/// * [AssetFaceUpdateDto] assetFaceUpdateDto (required): /// * [AssetFaceUpdateDto] assetFaceUpdateDto (required):
Future<Response> reassignFacesWithHttpInfo(String id, AssetFaceUpdateDto assetFaceUpdateDto,) async { Future<Response> reassignFacesWithHttpInfo(String id, AssetFaceUpdateDto assetFaceUpdateDto,) async {
// ignore: prefer_const_declarations // ignore: prefer_const_declarations
final path = r'/person/{id}/reassign' final path = r'/people/{id}/reassign'
.replaceAll('{id}', id); .replaceAll('{id}', id);
// ignore: prefer_final_locals // ignore: prefer_final_locals
@@ -419,13 +419,63 @@ class PersonApi {
return null; return null;
} }
/// Performs an HTTP 'PUT /person' operation and returns the [Response]. /// Performs an HTTP 'DELETE /people' operation and returns the [Response].
/// Parameters:
///
/// * [AssetFaceUpdateDto] assetFaceUpdateDto (required):
Future<Response> unassignFacesWithHttpInfo(AssetFaceUpdateDto assetFaceUpdateDto,) async {
// ignore: prefer_const_declarations
final path = r'/people';
// ignore: prefer_final_locals
Object? postBody = assetFaceUpdateDto;
final queryParams = <QueryParam>[];
final headerParams = <String, String>{};
final formParams = <String, String>{};
const contentTypes = <String>['application/json'];
return apiClient.invokeAPI(
path,
'DELETE',
queryParams,
postBody,
headerParams,
formParams,
contentTypes.isEmpty ? null : contentTypes.first,
);
}
/// Parameters:
///
/// * [AssetFaceUpdateDto] assetFaceUpdateDto (required):
Future<List<BulkIdResponseDto>?> unassignFaces(AssetFaceUpdateDto assetFaceUpdateDto,) async {
final response = await unassignFacesWithHttpInfo(assetFaceUpdateDto,);
if (response.statusCode >= HttpStatus.badRequest) {
throw ApiException(response.statusCode, await _decodeBodyBytes(response));
}
// When a remote server returns no body with a status of 204, we shall not decode it.
// At the time of writing this, `dart:convert` will throw an "Unexpected end of input"
// FormatException when trying to decode an empty string.
if (response.body.isNotEmpty && response.statusCode != HttpStatus.noContent) {
final responseBody = await _decodeBodyBytes(response);
return (await apiClient.deserializeAsync(responseBody, 'List<BulkIdResponseDto>') as List)
.cast<BulkIdResponseDto>()
.toList(growable: false);
}
return null;
}
/// Performs an HTTP 'PUT /people' operation and returns the [Response].
/// Parameters: /// Parameters:
/// ///
/// * [PeopleUpdateDto] peopleUpdateDto (required): /// * [PeopleUpdateDto] peopleUpdateDto (required):
Future<Response> updatePeopleWithHttpInfo(PeopleUpdateDto peopleUpdateDto,) async { Future<Response> updatePeopleWithHttpInfo(PeopleUpdateDto peopleUpdateDto,) async {
// ignore: prefer_const_declarations // ignore: prefer_const_declarations
final path = r'/person'; final path = r'/people';
// ignore: prefer_final_locals // ignore: prefer_final_locals
Object? postBody = peopleUpdateDto; Object? postBody = peopleUpdateDto;
@@ -469,7 +519,7 @@ class PersonApi {
return null; return null;
} }
/// Performs an HTTP 'PUT /person/{id}' operation and returns the [Response]. /// Performs an HTTP 'PUT /people/{id}' operation and returns the [Response].
/// Parameters: /// Parameters:
/// ///
/// * [String] id (required): /// * [String] id (required):
@@ -477,7 +527,7 @@ class PersonApi {
/// * [PersonUpdateDto] personUpdateDto (required): /// * [PersonUpdateDto] personUpdateDto (required):
Future<Response> updatePersonWithHttpInfo(String id, PersonUpdateDto personUpdateDto,) async { Future<Response> updatePersonWithHttpInfo(String id, PersonUpdateDto personUpdateDto,) async {
// ignore: prefer_const_declarations // ignore: prefer_const_declarations
final path = r'/person/{id}' final path = r'/people/{id}'
.replaceAll('{id}', id); .replaceAll('{id}', id);
// ignore: prefer_final_locals // ignore: prefer_final_locals

View File

@@ -98,47 +98,6 @@ class ServerInfoApi {
return null; return null;
} }
/// Performs an HTTP 'GET /server-info' operation and returns the [Response].
Future<Response> getServerInfoWithHttpInfo() async {
// ignore: prefer_const_declarations
final path = r'/server-info';
// ignore: prefer_final_locals
Object? postBody;
final queryParams = <QueryParam>[];
final headerParams = <String, String>{};
final formParams = <String, String>{};
const contentTypes = <String>[];
return apiClient.invokeAPI(
path,
'GET',
queryParams,
postBody,
headerParams,
formParams,
contentTypes.isEmpty ? null : contentTypes.first,
);
}
Future<ServerInfoResponseDto?> getServerInfo() async {
final response = await getServerInfoWithHttpInfo();
if (response.statusCode >= HttpStatus.badRequest) {
throw ApiException(response.statusCode, await _decodeBodyBytes(response));
}
// When a remote server returns no body with a status of 204, we shall not decode it.
// At the time of writing this, `dart:convert` will throw an "Unexpected end of input"
// FormatException when trying to decode an empty string.
if (response.body.isNotEmpty && response.statusCode != HttpStatus.noContent) {
return await apiClient.deserializeAsync(await _decodeBodyBytes(response), 'ServerInfoResponseDto',) as ServerInfoResponseDto;
}
return null;
}
/// Performs an HTTP 'GET /server-info/statistics' operation and returns the [Response]. /// Performs an HTTP 'GET /server-info/statistics' operation and returns the [Response].
Future<Response> getServerStatisticsWithHttpInfo() async { Future<Response> getServerStatisticsWithHttpInfo() async {
// ignore: prefer_const_declarations // ignore: prefer_const_declarations
@@ -221,6 +180,47 @@ class ServerInfoApi {
return null; return null;
} }
/// Performs an HTTP 'GET /server-info/storage' operation and returns the [Response].
Future<Response> getStorageWithHttpInfo() async {
// ignore: prefer_const_declarations
final path = r'/server-info/storage';
// ignore: prefer_final_locals
Object? postBody;
final queryParams = <QueryParam>[];
final headerParams = <String, String>{};
final formParams = <String, String>{};
const contentTypes = <String>[];
return apiClient.invokeAPI(
path,
'GET',
queryParams,
postBody,
headerParams,
formParams,
contentTypes.isEmpty ? null : contentTypes.first,
);
}
Future<ServerStorageResponseDto?> getStorage() async {
final response = await getStorageWithHttpInfo();
if (response.statusCode >= HttpStatus.badRequest) {
throw ApiException(response.statusCode, await _decodeBodyBytes(response));
}
// When a remote server returns no body with a status of 204, we shall not decode it.
// At the time of writing this, `dart:convert` will throw an "Unexpected end of input"
// FormatException when trying to decode an empty string.
if (response.body.isNotEmpty && response.statusCode != HttpStatus.noContent) {
return await apiClient.deserializeAsync(await _decodeBodyBytes(response), 'ServerStorageResponseDto',) as ServerStorageResponseDto;
}
return null;
}
/// Performs an HTTP 'GET /server-info/media-types' operation and returns the [Response]. /// Performs an HTTP 'GET /server-info/media-types' operation and returns the [Response].
Future<Response> getSupportedMediaTypesWithHttpInfo() async { Future<Response> getSupportedMediaTypesWithHttpInfo() async {
// ignore: prefer_const_declarations // ignore: prefer_const_declarations

View File

@@ -16,7 +16,7 @@ class SharedLinkApi {
final ApiClient apiClient; final ApiClient apiClient;
/// Performs an HTTP 'PUT /shared-link/{id}/assets' operation and returns the [Response]. /// Performs an HTTP 'PUT /shared-links/{id}/assets' operation and returns the [Response].
/// Parameters: /// Parameters:
/// ///
/// * [String] id (required): /// * [String] id (required):
@@ -26,7 +26,7 @@ class SharedLinkApi {
/// * [String] key: /// * [String] key:
Future<Response> addSharedLinkAssetsWithHttpInfo(String id, AssetIdsDto assetIdsDto, { String? key, }) async { Future<Response> addSharedLinkAssetsWithHttpInfo(String id, AssetIdsDto assetIdsDto, { String? key, }) async {
// ignore: prefer_const_declarations // ignore: prefer_const_declarations
final path = r'/shared-link/{id}/assets' final path = r'/shared-links/{id}/assets'
.replaceAll('{id}', id); .replaceAll('{id}', id);
// ignore: prefer_final_locals // ignore: prefer_final_locals
@@ -79,13 +79,13 @@ class SharedLinkApi {
return null; return null;
} }
/// Performs an HTTP 'POST /shared-link' operation and returns the [Response]. /// Performs an HTTP 'POST /shared-links' operation and returns the [Response].
/// Parameters: /// Parameters:
/// ///
/// * [SharedLinkCreateDto] sharedLinkCreateDto (required): /// * [SharedLinkCreateDto] sharedLinkCreateDto (required):
Future<Response> createSharedLinkWithHttpInfo(SharedLinkCreateDto sharedLinkCreateDto,) async { Future<Response> createSharedLinkWithHttpInfo(SharedLinkCreateDto sharedLinkCreateDto,) async {
// ignore: prefer_const_declarations // ignore: prefer_const_declarations
final path = r'/shared-link'; final path = r'/shared-links';
// ignore: prefer_final_locals // ignore: prefer_final_locals
Object? postBody = sharedLinkCreateDto; Object? postBody = sharedLinkCreateDto;
@@ -126,10 +126,10 @@ class SharedLinkApi {
return null; return null;
} }
/// Performs an HTTP 'GET /shared-link' operation and returns the [Response]. /// Performs an HTTP 'GET /shared-links' operation and returns the [Response].
Future<Response> getAllSharedLinksWithHttpInfo() async { Future<Response> getAllSharedLinksWithHttpInfo() async {
// ignore: prefer_const_declarations // ignore: prefer_const_declarations
final path = r'/shared-link'; final path = r'/shared-links';
// ignore: prefer_final_locals // ignore: prefer_final_locals
Object? postBody; Object? postBody;
@@ -170,7 +170,7 @@ class SharedLinkApi {
return null; return null;
} }
/// Performs an HTTP 'GET /shared-link/me' operation and returns the [Response]. /// Performs an HTTP 'GET /shared-links/me' operation and returns the [Response].
/// Parameters: /// Parameters:
/// ///
/// * [String] key: /// * [String] key:
@@ -180,7 +180,7 @@ class SharedLinkApi {
/// * [String] token: /// * [String] token:
Future<Response> getMySharedLinkWithHttpInfo({ String? key, String? password, String? token, }) async { Future<Response> getMySharedLinkWithHttpInfo({ String? key, String? password, String? token, }) async {
// ignore: prefer_const_declarations // ignore: prefer_const_declarations
final path = r'/shared-link/me'; final path = r'/shared-links/me';
// ignore: prefer_final_locals // ignore: prefer_final_locals
Object? postBody; Object? postBody;
@@ -235,13 +235,13 @@ class SharedLinkApi {
return null; return null;
} }
/// Performs an HTTP 'GET /shared-link/{id}' operation and returns the [Response]. /// Performs an HTTP 'GET /shared-links/{id}' operation and returns the [Response].
/// Parameters: /// Parameters:
/// ///
/// * [String] id (required): /// * [String] id (required):
Future<Response> getSharedLinkByIdWithHttpInfo(String id,) async { Future<Response> getSharedLinkByIdWithHttpInfo(String id,) async {
// ignore: prefer_const_declarations // ignore: prefer_const_declarations
final path = r'/shared-link/{id}' final path = r'/shared-links/{id}'
.replaceAll('{id}', id); .replaceAll('{id}', id);
// ignore: prefer_final_locals // ignore: prefer_final_locals
@@ -283,13 +283,13 @@ class SharedLinkApi {
return null; return null;
} }
/// Performs an HTTP 'DELETE /shared-link/{id}' operation and returns the [Response]. /// Performs an HTTP 'DELETE /shared-links/{id}' operation and returns the [Response].
/// Parameters: /// Parameters:
/// ///
/// * [String] id (required): /// * [String] id (required):
Future<Response> removeSharedLinkWithHttpInfo(String id,) async { Future<Response> removeSharedLinkWithHttpInfo(String id,) async {
// ignore: prefer_const_declarations // ignore: prefer_const_declarations
final path = r'/shared-link/{id}' final path = r'/shared-links/{id}'
.replaceAll('{id}', id); .replaceAll('{id}', id);
// ignore: prefer_final_locals // ignore: prefer_final_locals
@@ -323,7 +323,7 @@ class SharedLinkApi {
} }
} }
/// Performs an HTTP 'DELETE /shared-link/{id}/assets' operation and returns the [Response]. /// Performs an HTTP 'DELETE /shared-links/{id}/assets' operation and returns the [Response].
/// Parameters: /// Parameters:
/// ///
/// * [String] id (required): /// * [String] id (required):
@@ -333,7 +333,7 @@ class SharedLinkApi {
/// * [String] key: /// * [String] key:
Future<Response> removeSharedLinkAssetsWithHttpInfo(String id, AssetIdsDto assetIdsDto, { String? key, }) async { Future<Response> removeSharedLinkAssetsWithHttpInfo(String id, AssetIdsDto assetIdsDto, { String? key, }) async {
// ignore: prefer_const_declarations // ignore: prefer_const_declarations
final path = r'/shared-link/{id}/assets' final path = r'/shared-links/{id}/assets'
.replaceAll('{id}', id); .replaceAll('{id}', id);
// ignore: prefer_final_locals // ignore: prefer_final_locals
@@ -386,7 +386,7 @@ class SharedLinkApi {
return null; return null;
} }
/// Performs an HTTP 'PATCH /shared-link/{id}' operation and returns the [Response]. /// Performs an HTTP 'PATCH /shared-links/{id}' operation and returns the [Response].
/// Parameters: /// Parameters:
/// ///
/// * [String] id (required): /// * [String] id (required):
@@ -394,7 +394,7 @@ class SharedLinkApi {
/// * [SharedLinkEditDto] sharedLinkEditDto (required): /// * [SharedLinkEditDto] sharedLinkEditDto (required):
Future<Response> updateSharedLinkWithHttpInfo(String id, SharedLinkEditDto sharedLinkEditDto,) async { Future<Response> updateSharedLinkWithHttpInfo(String id, SharedLinkEditDto sharedLinkEditDto,) async {
// ignore: prefer_const_declarations // ignore: prefer_const_declarations
final path = r'/shared-link/{id}' final path = r'/shared-links/{id}'
.replaceAll('{id}', id); .replaceAll('{id}', id);
// ignore: prefer_final_locals // ignore: prefer_final_locals

View File

@@ -16,13 +16,13 @@ class TagApi {
final ApiClient apiClient; final ApiClient apiClient;
/// Performs an HTTP 'POST /tag' operation and returns the [Response]. /// Performs an HTTP 'POST /tags' operation and returns the [Response].
/// Parameters: /// Parameters:
/// ///
/// * [CreateTagDto] createTagDto (required): /// * [CreateTagDto] createTagDto (required):
Future<Response> createTagWithHttpInfo(CreateTagDto createTagDto,) async { Future<Response> createTagWithHttpInfo(CreateTagDto createTagDto,) async {
// ignore: prefer_const_declarations // ignore: prefer_const_declarations
final path = r'/tag'; final path = r'/tags';
// ignore: prefer_final_locals // ignore: prefer_final_locals
Object? postBody = createTagDto; Object? postBody = createTagDto;
@@ -63,13 +63,13 @@ class TagApi {
return null; return null;
} }
/// Performs an HTTP 'DELETE /tag/{id}' operation and returns the [Response]. /// Performs an HTTP 'DELETE /tags/{id}' operation and returns the [Response].
/// Parameters: /// Parameters:
/// ///
/// * [String] id (required): /// * [String] id (required):
Future<Response> deleteTagWithHttpInfo(String id,) async { Future<Response> deleteTagWithHttpInfo(String id,) async {
// ignore: prefer_const_declarations // ignore: prefer_const_declarations
final path = r'/tag/{id}' final path = r'/tags/{id}'
.replaceAll('{id}', id); .replaceAll('{id}', id);
// ignore: prefer_final_locals // ignore: prefer_final_locals
@@ -103,10 +103,10 @@ class TagApi {
} }
} }
/// Performs an HTTP 'GET /tag' operation and returns the [Response]. /// Performs an HTTP 'GET /tags' operation and returns the [Response].
Future<Response> getAllTagsWithHttpInfo() async { Future<Response> getAllTagsWithHttpInfo() async {
// ignore: prefer_const_declarations // ignore: prefer_const_declarations
final path = r'/tag'; final path = r'/tags';
// ignore: prefer_final_locals // ignore: prefer_final_locals
Object? postBody; Object? postBody;
@@ -147,13 +147,13 @@ class TagApi {
return null; return null;
} }
/// Performs an HTTP 'GET /tag/{id}/assets' operation and returns the [Response]. /// Performs an HTTP 'GET /tags/{id}/assets' operation and returns the [Response].
/// Parameters: /// Parameters:
/// ///
/// * [String] id (required): /// * [String] id (required):
Future<Response> getTagAssetsWithHttpInfo(String id,) async { Future<Response> getTagAssetsWithHttpInfo(String id,) async {
// ignore: prefer_const_declarations // ignore: prefer_const_declarations
final path = r'/tag/{id}/assets' final path = r'/tags/{id}/assets'
.replaceAll('{id}', id); .replaceAll('{id}', id);
// ignore: prefer_final_locals // ignore: prefer_final_locals
@@ -198,13 +198,13 @@ class TagApi {
return null; return null;
} }
/// Performs an HTTP 'GET /tag/{id}' operation and returns the [Response]. /// Performs an HTTP 'GET /tags/{id}' operation and returns the [Response].
/// Parameters: /// Parameters:
/// ///
/// * [String] id (required): /// * [String] id (required):
Future<Response> getTagByIdWithHttpInfo(String id,) async { Future<Response> getTagByIdWithHttpInfo(String id,) async {
// ignore: prefer_const_declarations // ignore: prefer_const_declarations
final path = r'/tag/{id}' final path = r'/tags/{id}'
.replaceAll('{id}', id); .replaceAll('{id}', id);
// ignore: prefer_final_locals // ignore: prefer_final_locals
@@ -246,7 +246,7 @@ class TagApi {
return null; return null;
} }
/// Performs an HTTP 'PUT /tag/{id}/assets' operation and returns the [Response]. /// Performs an HTTP 'PUT /tags/{id}/assets' operation and returns the [Response].
/// Parameters: /// Parameters:
/// ///
/// * [String] id (required): /// * [String] id (required):
@@ -254,7 +254,7 @@ class TagApi {
/// * [AssetIdsDto] assetIdsDto (required): /// * [AssetIdsDto] assetIdsDto (required):
Future<Response> tagAssetsWithHttpInfo(String id, AssetIdsDto assetIdsDto,) async { Future<Response> tagAssetsWithHttpInfo(String id, AssetIdsDto assetIdsDto,) async {
// ignore: prefer_const_declarations // ignore: prefer_const_declarations
final path = r'/tag/{id}/assets' final path = r'/tags/{id}/assets'
.replaceAll('{id}', id); .replaceAll('{id}', id);
// ignore: prefer_final_locals // ignore: prefer_final_locals
@@ -301,7 +301,7 @@ class TagApi {
return null; return null;
} }
/// Performs an HTTP 'DELETE /tag/{id}/assets' operation and returns the [Response]. /// Performs an HTTP 'DELETE /tags/{id}/assets' operation and returns the [Response].
/// Parameters: /// Parameters:
/// ///
/// * [String] id (required): /// * [String] id (required):
@@ -309,7 +309,7 @@ class TagApi {
/// * [AssetIdsDto] assetIdsDto (required): /// * [AssetIdsDto] assetIdsDto (required):
Future<Response> untagAssetsWithHttpInfo(String id, AssetIdsDto assetIdsDto,) async { Future<Response> untagAssetsWithHttpInfo(String id, AssetIdsDto assetIdsDto,) async {
// ignore: prefer_const_declarations // ignore: prefer_const_declarations
final path = r'/tag/{id}/assets' final path = r'/tags/{id}/assets'
.replaceAll('{id}', id); .replaceAll('{id}', id);
// ignore: prefer_final_locals // ignore: prefer_final_locals
@@ -356,7 +356,7 @@ class TagApi {
return null; return null;
} }
/// Performs an HTTP 'PATCH /tag/{id}' operation and returns the [Response]. /// Performs an HTTP 'PATCH /tags/{id}' operation and returns the [Response].
/// Parameters: /// Parameters:
/// ///
/// * [String] id (required): /// * [String] id (required):
@@ -364,7 +364,7 @@ class TagApi {
/// * [UpdateTagDto] updateTagDto (required): /// * [UpdateTagDto] updateTagDto (required):
Future<Response> updateTagWithHttpInfo(String id, UpdateTagDto updateTagDto,) async { Future<Response> updateTagWithHttpInfo(String id, UpdateTagDto updateTagDto,) async {
// ignore: prefer_const_declarations // ignore: prefer_const_declarations
final path = r'/tag/{id}' final path = r'/tags/{id}'
.replaceAll('{id}', id); .replaceAll('{id}', id);
// ignore: prefer_final_locals // ignore: prefer_final_locals

View File

@@ -16,13 +16,13 @@ class UserApi {
final ApiClient apiClient; final ApiClient apiClient;
/// Performs an HTTP 'POST /user/profile-image' operation and returns the [Response]. /// Performs an HTTP 'POST /users/profile-image' operation and returns the [Response].
/// Parameters: /// Parameters:
/// ///
/// * [MultipartFile] file (required): /// * [MultipartFile] file (required):
Future<Response> createProfileImageWithHttpInfo(MultipartFile file,) async { Future<Response> createProfileImageWithHttpInfo(MultipartFile file,) async {
// ignore: prefer_const_declarations // ignore: prefer_const_declarations
final path = r'/user/profile-image'; final path = r'/users/profile-image';
// ignore: prefer_final_locals // ignore: prefer_final_locals
Object? postBody; Object? postBody;
@@ -73,16 +73,16 @@ class UserApi {
return null; return null;
} }
/// Performs an HTTP 'POST /user' operation and returns the [Response]. /// Performs an HTTP 'POST /admin/users' operation and returns the [Response].
/// Parameters: /// Parameters:
/// ///
/// * [CreateUserDto] createUserDto (required): /// * [UserAdminCreateDto] userAdminCreateDto (required):
Future<Response> createUserWithHttpInfo(CreateUserDto createUserDto,) async { Future<Response> createUserAdminWithHttpInfo(UserAdminCreateDto userAdminCreateDto,) async {
// ignore: prefer_const_declarations // ignore: prefer_const_declarations
final path = r'/user'; final path = r'/admin/users';
// ignore: prefer_final_locals // ignore: prefer_final_locals
Object? postBody = createUserDto; Object? postBody = userAdminCreateDto;
final queryParams = <QueryParam>[]; final queryParams = <QueryParam>[];
final headerParams = <String, String>{}; final headerParams = <String, String>{};
@@ -104,9 +104,9 @@ class UserApi {
/// Parameters: /// Parameters:
/// ///
/// * [CreateUserDto] createUserDto (required): /// * [UserAdminCreateDto] userAdminCreateDto (required):
Future<UserResponseDto?> createUser(CreateUserDto createUserDto,) async { Future<UserAdminResponseDto?> createUserAdmin(UserAdminCreateDto userAdminCreateDto,) async {
final response = await createUserWithHttpInfo(createUserDto,); final response = await createUserAdminWithHttpInfo(userAdminCreateDto,);
if (response.statusCode >= HttpStatus.badRequest) { if (response.statusCode >= HttpStatus.badRequest) {
throw ApiException(response.statusCode, await _decodeBodyBytes(response)); throw ApiException(response.statusCode, await _decodeBodyBytes(response));
} }
@@ -114,16 +114,16 @@ class UserApi {
// At the time of writing this, `dart:convert` will throw an "Unexpected end of input" // At the time of writing this, `dart:convert` will throw an "Unexpected end of input"
// FormatException when trying to decode an empty string. // FormatException when trying to decode an empty string.
if (response.body.isNotEmpty && response.statusCode != HttpStatus.noContent) { if (response.body.isNotEmpty && response.statusCode != HttpStatus.noContent) {
return await apiClient.deserializeAsync(await _decodeBodyBytes(response), 'UserResponseDto',) as UserResponseDto; return await apiClient.deserializeAsync(await _decodeBodyBytes(response), 'UserAdminResponseDto',) as UserAdminResponseDto;
} }
return null; return null;
} }
/// Performs an HTTP 'DELETE /user/profile-image' operation and returns the [Response]. /// Performs an HTTP 'DELETE /users/profile-image' operation and returns the [Response].
Future<Response> deleteProfileImageWithHttpInfo() async { Future<Response> deleteProfileImageWithHttpInfo() async {
// ignore: prefer_const_declarations // ignore: prefer_const_declarations
final path = r'/user/profile-image'; final path = r'/users/profile-image';
// ignore: prefer_final_locals // ignore: prefer_final_locals
Object? postBody; Object? postBody;
@@ -153,19 +153,19 @@ class UserApi {
} }
} }
/// Performs an HTTP 'DELETE /user/{id}' operation and returns the [Response]. /// Performs an HTTP 'DELETE /admin/users/{id}' operation and returns the [Response].
/// Parameters: /// Parameters:
/// ///
/// * [String] id (required): /// * [String] id (required):
/// ///
/// * [DeleteUserDto] deleteUserDto (required): /// * [UserAdminDeleteDto] userAdminDeleteDto (required):
Future<Response> deleteUserWithHttpInfo(String id, DeleteUserDto deleteUserDto,) async { Future<Response> deleteUserAdminWithHttpInfo(String id, UserAdminDeleteDto userAdminDeleteDto,) async {
// ignore: prefer_const_declarations // ignore: prefer_const_declarations
final path = r'/user/{id}' final path = r'/admin/users/{id}'
.replaceAll('{id}', id); .replaceAll('{id}', id);
// ignore: prefer_final_locals // ignore: prefer_final_locals
Object? postBody = deleteUserDto; Object? postBody = userAdminDeleteDto;
final queryParams = <QueryParam>[]; final queryParams = <QueryParam>[];
final headerParams = <String, String>{}; final headerParams = <String, String>{};
@@ -189,9 +189,9 @@ class UserApi {
/// ///
/// * [String] id (required): /// * [String] id (required):
/// ///
/// * [DeleteUserDto] deleteUserDto (required): /// * [UserAdminDeleteDto] userAdminDeleteDto (required):
Future<UserResponseDto?> deleteUser(String id, DeleteUserDto deleteUserDto,) async { Future<UserAdminResponseDto?> deleteUserAdmin(String id, UserAdminDeleteDto userAdminDeleteDto,) async {
final response = await deleteUserWithHttpInfo(id, deleteUserDto,); final response = await deleteUserAdminWithHttpInfo(id, userAdminDeleteDto,);
if (response.statusCode >= HttpStatus.badRequest) { if (response.statusCode >= HttpStatus.badRequest) {
throw ApiException(response.statusCode, await _decodeBodyBytes(response)); throw ApiException(response.statusCode, await _decodeBodyBytes(response));
} }
@@ -199,68 +199,16 @@ class UserApi {
// At the time of writing this, `dart:convert` will throw an "Unexpected end of input" // At the time of writing this, `dart:convert` will throw an "Unexpected end of input"
// FormatException when trying to decode an empty string. // FormatException when trying to decode an empty string.
if (response.body.isNotEmpty && response.statusCode != HttpStatus.noContent) { if (response.body.isNotEmpty && response.statusCode != HttpStatus.noContent) {
return await apiClient.deserializeAsync(await _decodeBodyBytes(response), 'UserResponseDto',) as UserResponseDto; return await apiClient.deserializeAsync(await _decodeBodyBytes(response), 'UserAdminResponseDto',) as UserAdminResponseDto;
} }
return null; return null;
} }
/// Performs an HTTP 'GET /user' operation and returns the [Response]. /// Performs an HTTP 'GET /users/me' operation and returns the [Response].
/// Parameters: Future<Response> getMyUserWithHttpInfo() async {
///
/// * [bool] isAll (required):
Future<Response> getAllUsersWithHttpInfo(bool isAll,) async {
// ignore: prefer_const_declarations // ignore: prefer_const_declarations
final path = r'/user'; final path = r'/users/me';
// ignore: prefer_final_locals
Object? postBody;
final queryParams = <QueryParam>[];
final headerParams = <String, String>{};
final formParams = <String, String>{};
queryParams.addAll(_queryParams('', 'isAll', isAll));
const contentTypes = <String>[];
return apiClient.invokeAPI(
path,
'GET',
queryParams,
postBody,
headerParams,
formParams,
contentTypes.isEmpty ? null : contentTypes.first,
);
}
/// Parameters:
///
/// * [bool] isAll (required):
Future<List<UserResponseDto>?> getAllUsers(bool isAll,) async {
final response = await getAllUsersWithHttpInfo(isAll,);
if (response.statusCode >= HttpStatus.badRequest) {
throw ApiException(response.statusCode, await _decodeBodyBytes(response));
}
// When a remote server returns no body with a status of 204, we shall not decode it.
// At the time of writing this, `dart:convert` will throw an "Unexpected end of input"
// FormatException when trying to decode an empty string.
if (response.body.isNotEmpty && response.statusCode != HttpStatus.noContent) {
final responseBody = await _decodeBodyBytes(response);
return (await apiClient.deserializeAsync(responseBody, 'List<UserResponseDto>') as List)
.cast<UserResponseDto>()
.toList(growable: false);
}
return null;
}
/// Performs an HTTP 'GET /user/me' operation and returns the [Response].
Future<Response> getMyUserInfoWithHttpInfo() async {
// ignore: prefer_const_declarations
final path = r'/user/me';
// ignore: prefer_final_locals // ignore: prefer_final_locals
Object? postBody; Object? postBody;
@@ -283,8 +231,8 @@ class UserApi {
); );
} }
Future<UserResponseDto?> getMyUserInfo() async { Future<UserAdminResponseDto?> getMyUser() async {
final response = await getMyUserInfoWithHttpInfo(); final response = await getMyUserWithHttpInfo();
if (response.statusCode >= HttpStatus.badRequest) { if (response.statusCode >= HttpStatus.badRequest) {
throw ApiException(response.statusCode, await _decodeBodyBytes(response)); throw ApiException(response.statusCode, await _decodeBodyBytes(response));
} }
@@ -292,19 +240,19 @@ class UserApi {
// At the time of writing this, `dart:convert` will throw an "Unexpected end of input" // At the time of writing this, `dart:convert` will throw an "Unexpected end of input"
// FormatException when trying to decode an empty string. // FormatException when trying to decode an empty string.
if (response.body.isNotEmpty && response.statusCode != HttpStatus.noContent) { if (response.body.isNotEmpty && response.statusCode != HttpStatus.noContent) {
return await apiClient.deserializeAsync(await _decodeBodyBytes(response), 'UserResponseDto',) as UserResponseDto; return await apiClient.deserializeAsync(await _decodeBodyBytes(response), 'UserAdminResponseDto',) as UserAdminResponseDto;
} }
return null; return null;
} }
/// Performs an HTTP 'GET /user/profile-image/{id}' operation and returns the [Response]. /// Performs an HTTP 'GET /users/{id}/profile-image' operation and returns the [Response].
/// Parameters: /// Parameters:
/// ///
/// * [String] id (required): /// * [String] id (required):
Future<Response> getProfileImageWithHttpInfo(String id,) async { Future<Response> getProfileImageWithHttpInfo(String id,) async {
// ignore: prefer_const_declarations // ignore: prefer_const_declarations
final path = r'/user/profile-image/{id}' final path = r'/users/{id}/profile-image'
.replaceAll('{id}', id); .replaceAll('{id}', id);
// ignore: prefer_final_locals // ignore: prefer_final_locals
@@ -346,13 +294,13 @@ class UserApi {
return null; return null;
} }
/// Performs an HTTP 'GET /user/info/{id}' operation and returns the [Response]. /// Performs an HTTP 'GET /users/{id}' operation and returns the [Response].
/// Parameters: /// Parameters:
/// ///
/// * [String] id (required): /// * [String] id (required):
Future<Response> getUserByIdWithHttpInfo(String id,) async { Future<Response> getUserWithHttpInfo(String id,) async {
// ignore: prefer_const_declarations // ignore: prefer_const_declarations
final path = r'/user/info/{id}' final path = r'/users/{id}'
.replaceAll('{id}', id); .replaceAll('{id}', id);
// ignore: prefer_final_locals // ignore: prefer_final_locals
@@ -379,8 +327,8 @@ class UserApi {
/// Parameters: /// Parameters:
/// ///
/// * [String] id (required): /// * [String] id (required):
Future<UserResponseDto?> getUserById(String id,) async { Future<UserResponseDto?> getUser(String id,) async {
final response = await getUserByIdWithHttpInfo(id,); final response = await getUserWithHttpInfo(id,);
if (response.statusCode >= HttpStatus.badRequest) { if (response.statusCode >= HttpStatus.badRequest) {
throw ApiException(response.statusCode, await _decodeBodyBytes(response)); throw ApiException(response.statusCode, await _decodeBodyBytes(response));
} }
@@ -394,13 +342,61 @@ class UserApi {
return null; return null;
} }
/// Performs an HTTP 'POST /user/{id}/restore' operation and returns the [Response]. /// Performs an HTTP 'GET /admin/users/{id}' operation and returns the [Response].
/// Parameters: /// Parameters:
/// ///
/// * [String] id (required): /// * [String] id (required):
Future<Response> restoreUserWithHttpInfo(String id,) async { Future<Response> getUserAdminWithHttpInfo(String id,) async {
// ignore: prefer_const_declarations // ignore: prefer_const_declarations
final path = r'/user/{id}/restore' final path = r'/admin/users/{id}'
.replaceAll('{id}', id);
// ignore: prefer_final_locals
Object? postBody;
final queryParams = <QueryParam>[];
final headerParams = <String, String>{};
final formParams = <String, String>{};
const contentTypes = <String>[];
return apiClient.invokeAPI(
path,
'GET',
queryParams,
postBody,
headerParams,
formParams,
contentTypes.isEmpty ? null : contentTypes.first,
);
}
/// Parameters:
///
/// * [String] id (required):
Future<UserAdminResponseDto?> getUserAdmin(String id,) async {
final response = await getUserAdminWithHttpInfo(id,);
if (response.statusCode >= HttpStatus.badRequest) {
throw ApiException(response.statusCode, await _decodeBodyBytes(response));
}
// When a remote server returns no body with a status of 204, we shall not decode it.
// At the time of writing this, `dart:convert` will throw an "Unexpected end of input"
// FormatException when trying to decode an empty string.
if (response.body.isNotEmpty && response.statusCode != HttpStatus.noContent) {
return await apiClient.deserializeAsync(await _decodeBodyBytes(response), 'UserAdminResponseDto',) as UserAdminResponseDto;
}
return null;
}
/// Performs an HTTP 'POST /admin/users/{id}/restore' operation and returns the [Response].
/// Parameters:
///
/// * [String] id (required):
Future<Response> restoreUserAdminWithHttpInfo(String id,) async {
// ignore: prefer_const_declarations
final path = r'/admin/users/{id}/restore'
.replaceAll('{id}', id); .replaceAll('{id}', id);
// ignore: prefer_final_locals // ignore: prefer_final_locals
@@ -427,8 +423,8 @@ class UserApi {
/// Parameters: /// Parameters:
/// ///
/// * [String] id (required): /// * [String] id (required):
Future<UserResponseDto?> restoreUser(String id,) async { Future<UserAdminResponseDto?> restoreUserAdmin(String id,) async {
final response = await restoreUserWithHttpInfo(id,); final response = await restoreUserAdminWithHttpInfo(id,);
if (response.statusCode >= HttpStatus.badRequest) { if (response.statusCode >= HttpStatus.badRequest) {
throw ApiException(response.statusCode, await _decodeBodyBytes(response)); throw ApiException(response.statusCode, await _decodeBodyBytes(response));
} }
@@ -436,22 +432,120 @@ class UserApi {
// At the time of writing this, `dart:convert` will throw an "Unexpected end of input" // At the time of writing this, `dart:convert` will throw an "Unexpected end of input"
// FormatException when trying to decode an empty string. // FormatException when trying to decode an empty string.
if (response.body.isNotEmpty && response.statusCode != HttpStatus.noContent) { if (response.body.isNotEmpty && response.statusCode != HttpStatus.noContent) {
return await apiClient.deserializeAsync(await _decodeBodyBytes(response), 'UserResponseDto',) as UserResponseDto; return await apiClient.deserializeAsync(await _decodeBodyBytes(response), 'UserAdminResponseDto',) as UserAdminResponseDto;
} }
return null; return null;
} }
/// Performs an HTTP 'PUT /user' operation and returns the [Response]. /// Performs an HTTP 'GET /users' operation and returns the [Response].
/// Parameters: Future<Response> searchUsersWithHttpInfo() async {
///
/// * [UpdateUserDto] updateUserDto (required):
Future<Response> updateUserWithHttpInfo(UpdateUserDto updateUserDto,) async {
// ignore: prefer_const_declarations // ignore: prefer_const_declarations
final path = r'/user'; final path = r'/users';
// ignore: prefer_final_locals // ignore: prefer_final_locals
Object? postBody = updateUserDto; Object? postBody;
final queryParams = <QueryParam>[];
final headerParams = <String, String>{};
final formParams = <String, String>{};
const contentTypes = <String>[];
return apiClient.invokeAPI(
path,
'GET',
queryParams,
postBody,
headerParams,
formParams,
contentTypes.isEmpty ? null : contentTypes.first,
);
}
Future<List<UserResponseDto>?> searchUsers() async {
final response = await searchUsersWithHttpInfo();
if (response.statusCode >= HttpStatus.badRequest) {
throw ApiException(response.statusCode, await _decodeBodyBytes(response));
}
// When a remote server returns no body with a status of 204, we shall not decode it.
// At the time of writing this, `dart:convert` will throw an "Unexpected end of input"
// FormatException when trying to decode an empty string.
if (response.body.isNotEmpty && response.statusCode != HttpStatus.noContent) {
final responseBody = await _decodeBodyBytes(response);
return (await apiClient.deserializeAsync(responseBody, 'List<UserResponseDto>') as List)
.cast<UserResponseDto>()
.toList(growable: false);
}
return null;
}
/// Performs an HTTP 'GET /admin/users' operation and returns the [Response].
/// Parameters:
///
/// * [bool] withDeleted:
Future<Response> searchUsersAdminWithHttpInfo({ bool? withDeleted, }) async {
// ignore: prefer_const_declarations
final path = r'/admin/users';
// ignore: prefer_final_locals
Object? postBody;
final queryParams = <QueryParam>[];
final headerParams = <String, String>{};
final formParams = <String, String>{};
if (withDeleted != null) {
queryParams.addAll(_queryParams('', 'withDeleted', withDeleted));
}
const contentTypes = <String>[];
return apiClient.invokeAPI(
path,
'GET',
queryParams,
postBody,
headerParams,
formParams,
contentTypes.isEmpty ? null : contentTypes.first,
);
}
/// Parameters:
///
/// * [bool] withDeleted:
Future<List<UserAdminResponseDto>?> searchUsersAdmin({ bool? withDeleted, }) async {
final response = await searchUsersAdminWithHttpInfo( withDeleted: withDeleted, );
if (response.statusCode >= HttpStatus.badRequest) {
throw ApiException(response.statusCode, await _decodeBodyBytes(response));
}
// When a remote server returns no body with a status of 204, we shall not decode it.
// At the time of writing this, `dart:convert` will throw an "Unexpected end of input"
// FormatException when trying to decode an empty string.
if (response.body.isNotEmpty && response.statusCode != HttpStatus.noContent) {
final responseBody = await _decodeBodyBytes(response);
return (await apiClient.deserializeAsync(responseBody, 'List<UserAdminResponseDto>') as List)
.cast<UserAdminResponseDto>()
.toList(growable: false);
}
return null;
}
/// Performs an HTTP 'PUT /users/me' operation and returns the [Response].
/// Parameters:
///
/// * [UserUpdateMeDto] userUpdateMeDto (required):
Future<Response> updateMyUserWithHttpInfo(UserUpdateMeDto userUpdateMeDto,) async {
// ignore: prefer_const_declarations
final path = r'/users/me';
// ignore: prefer_final_locals
Object? postBody = userUpdateMeDto;
final queryParams = <QueryParam>[]; final queryParams = <QueryParam>[];
final headerParams = <String, String>{}; final headerParams = <String, String>{};
@@ -473,9 +567,9 @@ class UserApi {
/// Parameters: /// Parameters:
/// ///
/// * [UpdateUserDto] updateUserDto (required): /// * [UserUpdateMeDto] userUpdateMeDto (required):
Future<UserResponseDto?> updateUser(UpdateUserDto updateUserDto,) async { Future<UserAdminResponseDto?> updateMyUser(UserUpdateMeDto userUpdateMeDto,) async {
final response = await updateUserWithHttpInfo(updateUserDto,); final response = await updateMyUserWithHttpInfo(userUpdateMeDto,);
if (response.statusCode >= HttpStatus.badRequest) { if (response.statusCode >= HttpStatus.badRequest) {
throw ApiException(response.statusCode, await _decodeBodyBytes(response)); throw ApiException(response.statusCode, await _decodeBodyBytes(response));
} }
@@ -483,7 +577,59 @@ class UserApi {
// At the time of writing this, `dart:convert` will throw an "Unexpected end of input" // At the time of writing this, `dart:convert` will throw an "Unexpected end of input"
// FormatException when trying to decode an empty string. // FormatException when trying to decode an empty string.
if (response.body.isNotEmpty && response.statusCode != HttpStatus.noContent) { if (response.body.isNotEmpty && response.statusCode != HttpStatus.noContent) {
return await apiClient.deserializeAsync(await _decodeBodyBytes(response), 'UserResponseDto',) as UserResponseDto; return await apiClient.deserializeAsync(await _decodeBodyBytes(response), 'UserAdminResponseDto',) as UserAdminResponseDto;
}
return null;
}
/// Performs an HTTP 'PUT /admin/users/{id}' operation and returns the [Response].
/// Parameters:
///
/// * [String] id (required):
///
/// * [UserAdminUpdateDto] userAdminUpdateDto (required):
Future<Response> updateUserAdminWithHttpInfo(String id, UserAdminUpdateDto userAdminUpdateDto,) async {
// ignore: prefer_const_declarations
final path = r'/admin/users/{id}'
.replaceAll('{id}', id);
// ignore: prefer_final_locals
Object? postBody = userAdminUpdateDto;
final queryParams = <QueryParam>[];
final headerParams = <String, String>{};
final formParams = <String, String>{};
const contentTypes = <String>['application/json'];
return apiClient.invokeAPI(
path,
'PUT',
queryParams,
postBody,
headerParams,
formParams,
contentTypes.isEmpty ? null : contentTypes.first,
);
}
/// Parameters:
///
/// * [String] id (required):
///
/// * [UserAdminUpdateDto] userAdminUpdateDto (required):
Future<UserAdminResponseDto?> updateUserAdmin(String id, UserAdminUpdateDto userAdminUpdateDto,) async {
final response = await updateUserAdminWithHttpInfo(id, userAdminUpdateDto,);
if (response.statusCode >= HttpStatus.badRequest) {
throw ApiException(response.statusCode, await _decodeBodyBytes(response));
}
// When a remote server returns no body with a status of 204, we shall not decode it.
// At the time of writing this, `dart:convert` will throw an "Unexpected end of input"
// FormatException when trying to decode an empty string.
if (response.body.isNotEmpty && response.statusCode != HttpStatus.noContent) {
return await apiClient.deserializeAsync(await _decodeBodyBytes(response), 'UserAdminResponseDto',) as UserAdminResponseDto;
} }
return null; return null;

View File

@@ -250,6 +250,10 @@ class ApiClient {
return AssetJobNameTypeTransformer().decode(value); return AssetJobNameTypeTransformer().decode(value);
case 'AssetJobsDto': case 'AssetJobsDto':
return AssetJobsDto.fromJson(value); return AssetJobsDto.fromJson(value);
case 'AssetMediaResponseDto':
return AssetMediaResponseDto.fromJson(value);
case 'AssetMediaStatus':
return AssetMediaStatusTypeTransformer().decode(value);
case 'AssetOrder': case 'AssetOrder':
return AssetOrderTypeTransformer().decode(value); return AssetOrderTypeTransformer().decode(value);
case 'AssetResponseDto': case 'AssetResponseDto':
@@ -288,10 +292,6 @@ class ApiClient {
return CreateProfileImageResponseDto.fromJson(value); return CreateProfileImageResponseDto.fromJson(value);
case 'CreateTagDto': case 'CreateTagDto':
return CreateTagDto.fromJson(value); return CreateTagDto.fromJson(value);
case 'CreateUserDto':
return CreateUserDto.fromJson(value);
case 'DeleteUserDto':
return DeleteUserDto.fromJson(value);
case 'DownloadArchiveInfo': case 'DownloadArchiveInfo':
return DownloadArchiveInfo.fromJson(value); return DownloadArchiveInfo.fromJson(value);
case 'DownloadInfoDto': case 'DownloadInfoDto':
@@ -384,6 +384,8 @@ class ApiClient {
return PeopleUpdateDto.fromJson(value); return PeopleUpdateDto.fromJson(value);
case 'PeopleUpdateItem': case 'PeopleUpdateItem':
return PeopleUpdateItem.fromJson(value); return PeopleUpdateItem.fromJson(value);
case 'PeopleWithFacesResponseDto':
return PeopleWithFacesResponseDto.fromJson(value);
case 'PersonCreateDto': case 'PersonCreateDto':
return PersonCreateDto.fromJson(value); return PersonCreateDto.fromJson(value);
case 'PersonResponseDto': case 'PersonResponseDto':
@@ -428,14 +430,14 @@ class ApiClient {
return ServerConfigDto.fromJson(value); return ServerConfigDto.fromJson(value);
case 'ServerFeaturesDto': case 'ServerFeaturesDto':
return ServerFeaturesDto.fromJson(value); return ServerFeaturesDto.fromJson(value);
case 'ServerInfoResponseDto':
return ServerInfoResponseDto.fromJson(value);
case 'ServerMediaTypesResponseDto': case 'ServerMediaTypesResponseDto':
return ServerMediaTypesResponseDto.fromJson(value); return ServerMediaTypesResponseDto.fromJson(value);
case 'ServerPingResponse': case 'ServerPingResponse':
return ServerPingResponse.fromJson(value); return ServerPingResponse.fromJson(value);
case 'ServerStatsResponseDto': case 'ServerStatsResponseDto':
return ServerStatsResponseDto.fromJson(value); return ServerStatsResponseDto.fromJson(value);
case 'ServerStorageResponseDto':
return ServerStorageResponseDto.fromJson(value);
case 'ServerThemeDto': case 'ServerThemeDto':
return ServerThemeDto.fromJson(value); return ServerThemeDto.fromJson(value);
case 'ServerVersionResponseDto': case 'ServerVersionResponseDto':
@@ -532,18 +534,24 @@ class ApiClient {
return UpdateStackParentDto.fromJson(value); return UpdateStackParentDto.fromJson(value);
case 'UpdateTagDto': case 'UpdateTagDto':
return UpdateTagDto.fromJson(value); return UpdateTagDto.fromJson(value);
case 'UpdateUserDto':
return UpdateUserDto.fromJson(value);
case 'UsageByUserDto': case 'UsageByUserDto':
return UsageByUserDto.fromJson(value); return UsageByUserDto.fromJson(value);
case 'UserAdminCreateDto':
return UserAdminCreateDto.fromJson(value);
case 'UserAdminDeleteDto':
return UserAdminDeleteDto.fromJson(value);
case 'UserAdminResponseDto':
return UserAdminResponseDto.fromJson(value);
case 'UserAdminUpdateDto':
return UserAdminUpdateDto.fromJson(value);
case 'UserAvatarColor': case 'UserAvatarColor':
return UserAvatarColorTypeTransformer().decode(value); return UserAvatarColorTypeTransformer().decode(value);
case 'UserDto':
return UserDto.fromJson(value);
case 'UserResponseDto': case 'UserResponseDto':
return UserResponseDto.fromJson(value); return UserResponseDto.fromJson(value);
case 'UserStatus': case 'UserStatus':
return UserStatusTypeTransformer().decode(value); return UserStatusTypeTransformer().decode(value);
case 'UserUpdateMeDto':
return UserUpdateMeDto.fromJson(value);
case 'ValidateAccessTokenResponseDto': case 'ValidateAccessTokenResponseDto':
return ValidateAccessTokenResponseDto.fromJson(value); return ValidateAccessTokenResponseDto.fromJson(value);
case 'ValidateLibraryDto': case 'ValidateLibraryDto':

View File

@@ -61,6 +61,9 @@ String parameterToString(dynamic value) {
if (value is AssetJobName) { if (value is AssetJobName) {
return AssetJobNameTypeTransformer().encode(value).toString(); return AssetJobNameTypeTransformer().encode(value).toString();
} }
if (value is AssetMediaStatus) {
return AssetMediaStatusTypeTransformer().encode(value).toString();
}
if (value is AssetOrder) { if (value is AssetOrder) {
return AssetOrderTypeTransformer().encode(value).toString(); return AssetOrderTypeTransformer().encode(value).toString();
} }

View File

@@ -31,7 +31,7 @@ class ActivityResponseDto {
ActivityResponseDtoTypeEnum type; ActivityResponseDtoTypeEnum type;
UserDto user; UserResponseDto user;
@override @override
bool operator ==(Object other) => identical(this, other) || other is ActivityResponseDto && bool operator ==(Object other) => identical(this, other) || other is ActivityResponseDto &&
@@ -87,7 +87,7 @@ class ActivityResponseDto {
createdAt: mapDateTime(json, r'createdAt', r'')!, createdAt: mapDateTime(json, r'createdAt', r'')!,
id: mapValueOfType<String>(json, r'id')!, id: mapValueOfType<String>(json, r'id')!,
type: ActivityResponseDtoTypeEnum.fromJson(json[r'type'])!, type: ActivityResponseDtoTypeEnum.fromJson(json[r'type'])!,
user: UserDto.fromJson(json[r'user'])!, user: UserResponseDto.fromJson(json[r'user'])!,
); );
} }
return null; return null;

View File

@@ -14,32 +14,25 @@ class AddUsersDto {
/// Returns a new [AddUsersDto] instance. /// Returns a new [AddUsersDto] instance.
AddUsersDto({ AddUsersDto({
this.albumUsers = const [], this.albumUsers = const [],
this.sharedUserIds = const [],
}); });
List<AlbumUserAddDto> albumUsers; List<AlbumUserAddDto> albumUsers;
/// This property was deprecated in v1.102.0
List<String> sharedUserIds;
@override @override
bool operator ==(Object other) => identical(this, other) || other is AddUsersDto && bool operator ==(Object other) => identical(this, other) || other is AddUsersDto &&
_deepEquality.equals(other.albumUsers, albumUsers) && _deepEquality.equals(other.albumUsers, albumUsers);
_deepEquality.equals(other.sharedUserIds, sharedUserIds);
@override @override
int get hashCode => int get hashCode =>
// ignore: unnecessary_parenthesis // ignore: unnecessary_parenthesis
(albumUsers.hashCode) + (albumUsers.hashCode);
(sharedUserIds.hashCode);
@override @override
String toString() => 'AddUsersDto[albumUsers=$albumUsers, sharedUserIds=$sharedUserIds]'; String toString() => 'AddUsersDto[albumUsers=$albumUsers]';
Map<String, dynamic> toJson() { Map<String, dynamic> toJson() {
final json = <String, dynamic>{}; final json = <String, dynamic>{};
json[r'albumUsers'] = this.albumUsers; json[r'albumUsers'] = this.albumUsers;
json[r'sharedUserIds'] = this.sharedUserIds;
return json; return json;
} }
@@ -52,9 +45,6 @@ class AddUsersDto {
return AddUsersDto( return AddUsersDto(
albumUsers: AlbumUserAddDto.listFromJson(json[r'albumUsers']), albumUsers: AlbumUserAddDto.listFromJson(json[r'albumUsers']),
sharedUserIds: json[r'sharedUserIds'] is Iterable
? (json[r'sharedUserIds'] as Iterable).cast<String>().toList(growable: false)
: const [],
); );
} }
return null; return null;

View File

@@ -29,7 +29,6 @@ class AlbumResponseDto {
required this.owner, required this.owner,
required this.ownerId, required this.ownerId,
required this.shared, required this.shared,
this.sharedUsers = const [],
this.startDate, this.startDate,
required this.updatedAt, required this.updatedAt,
}); });
@@ -84,9 +83,6 @@ class AlbumResponseDto {
bool shared; bool shared;
/// This property was deprecated in v1.102.0
List<UserResponseDto> sharedUsers;
/// ///
/// Please note: This property should have been non-nullable! Since the specification file /// Please note: This property should have been non-nullable! Since the specification file
/// does not include a default value (using the "default:" property), however, the generated /// does not include a default value (using the "default:" property), however, the generated
@@ -115,7 +111,6 @@ class AlbumResponseDto {
other.owner == owner && other.owner == owner &&
other.ownerId == ownerId && other.ownerId == ownerId &&
other.shared == shared && other.shared == shared &&
_deepEquality.equals(other.sharedUsers, sharedUsers) &&
other.startDate == startDate && other.startDate == startDate &&
other.updatedAt == updatedAt; other.updatedAt == updatedAt;
@@ -138,12 +133,11 @@ class AlbumResponseDto {
(owner.hashCode) + (owner.hashCode) +
(ownerId.hashCode) + (ownerId.hashCode) +
(shared.hashCode) + (shared.hashCode) +
(sharedUsers.hashCode) +
(startDate == null ? 0 : startDate!.hashCode) + (startDate == null ? 0 : startDate!.hashCode) +
(updatedAt.hashCode); (updatedAt.hashCode);
@override @override
String toString() => 'AlbumResponseDto[albumName=$albumName, albumThumbnailAssetId=$albumThumbnailAssetId, albumUsers=$albumUsers, assetCount=$assetCount, assets=$assets, createdAt=$createdAt, description=$description, endDate=$endDate, hasSharedLink=$hasSharedLink, id=$id, isActivityEnabled=$isActivityEnabled, lastModifiedAssetTimestamp=$lastModifiedAssetTimestamp, order=$order, owner=$owner, ownerId=$ownerId, shared=$shared, sharedUsers=$sharedUsers, startDate=$startDate, updatedAt=$updatedAt]'; String toString() => 'AlbumResponseDto[albumName=$albumName, albumThumbnailAssetId=$albumThumbnailAssetId, albumUsers=$albumUsers, assetCount=$assetCount, assets=$assets, createdAt=$createdAt, description=$description, endDate=$endDate, hasSharedLink=$hasSharedLink, id=$id, isActivityEnabled=$isActivityEnabled, lastModifiedAssetTimestamp=$lastModifiedAssetTimestamp, order=$order, owner=$owner, ownerId=$ownerId, shared=$shared, startDate=$startDate, updatedAt=$updatedAt]';
Map<String, dynamic> toJson() { Map<String, dynamic> toJson() {
final json = <String, dynamic>{}; final json = <String, dynamic>{};
@@ -179,7 +173,6 @@ class AlbumResponseDto {
json[r'owner'] = this.owner; json[r'owner'] = this.owner;
json[r'ownerId'] = this.ownerId; json[r'ownerId'] = this.ownerId;
json[r'shared'] = this.shared; json[r'shared'] = this.shared;
json[r'sharedUsers'] = this.sharedUsers;
if (this.startDate != null) { if (this.startDate != null) {
json[r'startDate'] = this.startDate!.toUtc().toIso8601String(); json[r'startDate'] = this.startDate!.toUtc().toIso8601String();
} else { } else {
@@ -213,7 +206,6 @@ class AlbumResponseDto {
owner: UserResponseDto.fromJson(json[r'owner'])!, owner: UserResponseDto.fromJson(json[r'owner'])!,
ownerId: mapValueOfType<String>(json, r'ownerId')!, ownerId: mapValueOfType<String>(json, r'ownerId')!,
shared: mapValueOfType<bool>(json, r'shared')!, shared: mapValueOfType<bool>(json, r'shared')!,
sharedUsers: UserResponseDto.listFromJson(json[r'sharedUsers']),
startDate: mapDateTime(json, r'startDate', r''), startDate: mapDateTime(json, r'startDate', r''),
updatedAt: mapDateTime(json, r'updatedAt', r'')!, updatedAt: mapDateTime(json, r'updatedAt', r'')!,
); );
@@ -276,7 +268,6 @@ class AlbumResponseDto {
'owner', 'owner',
'ownerId', 'ownerId',
'shared', 'shared',
'sharedUsers',
'updatedAt', 'updatedAt',
}; };
} }

View File

@@ -14,6 +14,7 @@ class AssetBulkUpdateDto {
/// Returns a new [AssetBulkUpdateDto] instance. /// Returns a new [AssetBulkUpdateDto] instance.
AssetBulkUpdateDto({ AssetBulkUpdateDto({
this.dateTimeOriginal, this.dateTimeOriginal,
this.duplicateId,
this.ids = const [], this.ids = const [],
this.isArchived, this.isArchived,
this.isFavorite, this.isFavorite,
@@ -31,6 +32,8 @@ class AssetBulkUpdateDto {
/// ///
String? dateTimeOriginal; String? dateTimeOriginal;
String? duplicateId;
List<String> ids; List<String> ids;
/// ///
@@ -84,6 +87,7 @@ class AssetBulkUpdateDto {
@override @override
bool operator ==(Object other) => identical(this, other) || other is AssetBulkUpdateDto && bool operator ==(Object other) => identical(this, other) || other is AssetBulkUpdateDto &&
other.dateTimeOriginal == dateTimeOriginal && other.dateTimeOriginal == dateTimeOriginal &&
other.duplicateId == duplicateId &&
_deepEquality.equals(other.ids, ids) && _deepEquality.equals(other.ids, ids) &&
other.isArchived == isArchived && other.isArchived == isArchived &&
other.isFavorite == isFavorite && other.isFavorite == isFavorite &&
@@ -96,6 +100,7 @@ class AssetBulkUpdateDto {
int get hashCode => int get hashCode =>
// ignore: unnecessary_parenthesis // ignore: unnecessary_parenthesis
(dateTimeOriginal == null ? 0 : dateTimeOriginal!.hashCode) + (dateTimeOriginal == null ? 0 : dateTimeOriginal!.hashCode) +
(duplicateId == null ? 0 : duplicateId!.hashCode) +
(ids.hashCode) + (ids.hashCode) +
(isArchived == null ? 0 : isArchived!.hashCode) + (isArchived == null ? 0 : isArchived!.hashCode) +
(isFavorite == null ? 0 : isFavorite!.hashCode) + (isFavorite == null ? 0 : isFavorite!.hashCode) +
@@ -105,7 +110,7 @@ class AssetBulkUpdateDto {
(stackParentId == null ? 0 : stackParentId!.hashCode); (stackParentId == null ? 0 : stackParentId!.hashCode);
@override @override
String toString() => 'AssetBulkUpdateDto[dateTimeOriginal=$dateTimeOriginal, ids=$ids, isArchived=$isArchived, isFavorite=$isFavorite, latitude=$latitude, longitude=$longitude, removeParent=$removeParent, stackParentId=$stackParentId]'; String toString() => 'AssetBulkUpdateDto[dateTimeOriginal=$dateTimeOriginal, duplicateId=$duplicateId, ids=$ids, isArchived=$isArchived, isFavorite=$isFavorite, latitude=$latitude, longitude=$longitude, removeParent=$removeParent, stackParentId=$stackParentId]';
Map<String, dynamic> toJson() { Map<String, dynamic> toJson() {
final json = <String, dynamic>{}; final json = <String, dynamic>{};
@@ -113,6 +118,11 @@ class AssetBulkUpdateDto {
json[r'dateTimeOriginal'] = this.dateTimeOriginal; json[r'dateTimeOriginal'] = this.dateTimeOriginal;
} else { } else {
// json[r'dateTimeOriginal'] = null; // json[r'dateTimeOriginal'] = null;
}
if (this.duplicateId != null) {
json[r'duplicateId'] = this.duplicateId;
} else {
// json[r'duplicateId'] = null;
} }
json[r'ids'] = this.ids; json[r'ids'] = this.ids;
if (this.isArchived != null) { if (this.isArchived != null) {
@@ -157,6 +167,7 @@ class AssetBulkUpdateDto {
return AssetBulkUpdateDto( return AssetBulkUpdateDto(
dateTimeOriginal: mapValueOfType<String>(json, r'dateTimeOriginal'), dateTimeOriginal: mapValueOfType<String>(json, r'dateTimeOriginal'),
duplicateId: mapValueOfType<String>(json, r'duplicateId'),
ids: json[r'ids'] is Iterable ids: json[r'ids'] is Iterable
? (json[r'ids'] as Iterable).cast<String>().toList(growable: false) ? (json[r'ids'] as Iterable).cast<String>().toList(growable: false)
: const [], : const [],

View File

@@ -0,0 +1,106 @@
//
// AUTO-GENERATED FILE, DO NOT MODIFY!
//
// @dart=2.18
// ignore_for_file: unused_element, unused_import
// ignore_for_file: always_put_required_named_parameters_first
// ignore_for_file: constant_identifier_names
// ignore_for_file: lines_longer_than_80_chars
part of openapi.api;
class AssetMediaResponseDto {
/// Returns a new [AssetMediaResponseDto] instance.
AssetMediaResponseDto({
required this.id,
required this.status,
});
String id;
AssetMediaStatus status;
@override
bool operator ==(Object other) => identical(this, other) || other is AssetMediaResponseDto &&
other.id == id &&
other.status == status;
@override
int get hashCode =>
// ignore: unnecessary_parenthesis
(id.hashCode) +
(status.hashCode);
@override
String toString() => 'AssetMediaResponseDto[id=$id, status=$status]';
Map<String, dynamic> toJson() {
final json = <String, dynamic>{};
json[r'id'] = this.id;
json[r'status'] = this.status;
return json;
}
/// Returns a new [AssetMediaResponseDto] instance and imports its values from
/// [value] if it's a [Map], null otherwise.
// ignore: prefer_constructors_over_static_methods
static AssetMediaResponseDto? fromJson(dynamic value) {
if (value is Map) {
final json = value.cast<String, dynamic>();
return AssetMediaResponseDto(
id: mapValueOfType<String>(json, r'id')!,
status: AssetMediaStatus.fromJson(json[r'status'])!,
);
}
return null;
}
static List<AssetMediaResponseDto> listFromJson(dynamic json, {bool growable = false,}) {
final result = <AssetMediaResponseDto>[];
if (json is List && json.isNotEmpty) {
for (final row in json) {
final value = AssetMediaResponseDto.fromJson(row);
if (value != null) {
result.add(value);
}
}
}
return result.toList(growable: growable);
}
static Map<String, AssetMediaResponseDto> mapFromJson(dynamic json) {
final map = <String, AssetMediaResponseDto>{};
if (json is Map && json.isNotEmpty) {
json = json.cast<String, dynamic>(); // ignore: parameter_assignments
for (final entry in json.entries) {
final value = AssetMediaResponseDto.fromJson(entry.value);
if (value != null) {
map[entry.key] = value;
}
}
}
return map;
}
// maps a json object with a list of AssetMediaResponseDto-objects as value to a dart map
static Map<String, List<AssetMediaResponseDto>> mapListFromJson(dynamic json, {bool growable = false,}) {
final map = <String, List<AssetMediaResponseDto>>{};
if (json is Map && json.isNotEmpty) {
// ignore: parameter_assignments
json = json.cast<String, dynamic>();
for (final entry in json.entries) {
map[entry.key] = AssetMediaResponseDto.listFromJson(entry.value, growable: growable,);
}
}
return map;
}
/// The list of required keys that must be present in a JSON.
static const requiredKeys = <String>{
'id',
'status',
};
}

View File

@@ -0,0 +1,85 @@
//
// AUTO-GENERATED FILE, DO NOT MODIFY!
//
// @dart=2.18
// ignore_for_file: unused_element, unused_import
// ignore_for_file: always_put_required_named_parameters_first
// ignore_for_file: constant_identifier_names
// ignore_for_file: lines_longer_than_80_chars
part of openapi.api;
class AssetMediaStatus {
/// Instantiate a new enum with the provided [value].
const AssetMediaStatus._(this.value);
/// The underlying value of this enum member.
final String value;
@override
String toString() => value;
String toJson() => value;
static const replaced = AssetMediaStatus._(r'replaced');
static const duplicate = AssetMediaStatus._(r'duplicate');
/// List of all possible values in this [enum][AssetMediaStatus].
static const values = <AssetMediaStatus>[
replaced,
duplicate,
];
static AssetMediaStatus? fromJson(dynamic value) => AssetMediaStatusTypeTransformer().decode(value);
static List<AssetMediaStatus> listFromJson(dynamic json, {bool growable = false,}) {
final result = <AssetMediaStatus>[];
if (json is List && json.isNotEmpty) {
for (final row in json) {
final value = AssetMediaStatus.fromJson(row);
if (value != null) {
result.add(value);
}
}
}
return result.toList(growable: growable);
}
}
/// Transformation class that can [encode] an instance of [AssetMediaStatus] to String,
/// and [decode] dynamic data back to [AssetMediaStatus].
class AssetMediaStatusTypeTransformer {
factory AssetMediaStatusTypeTransformer() => _instance ??= const AssetMediaStatusTypeTransformer._();
const AssetMediaStatusTypeTransformer._();
String encode(AssetMediaStatus data) => data.value;
/// Decodes a [dynamic value][data] to a AssetMediaStatus.
///
/// If [allowNull] is true and the [dynamic value][data] cannot be decoded successfully,
/// then null is returned. However, if [allowNull] is false and the [dynamic value][data]
/// cannot be decoded successfully, then an [UnimplementedError] is thrown.
///
/// The [allowNull] is very handy when an API changes and a new enum value is added or removed,
/// and users are still using an old app with the old code.
AssetMediaStatus? decode(dynamic data, {bool allowNull = true}) {
if (data != null) {
switch (data) {
case r'replaced': return AssetMediaStatus.replaced;
case r'duplicate': return AssetMediaStatus.duplicate;
default:
if (!allowNull) {
throw ArgumentError('Unknown enum value to decode: $data');
}
}
}
return null;
}
/// Singleton [AssetMediaStatusTypeTransformer] instance.
static AssetMediaStatusTypeTransformer? _instance;
}

View File

@@ -24,10 +24,8 @@ class AssetResponseDto {
required this.hasMetadata, required this.hasMetadata,
required this.id, required this.id,
required this.isArchived, required this.isArchived,
this.isExternal,
required this.isFavorite, required this.isFavorite,
required this.isOffline, required this.isOffline,
this.isReadOnly,
required this.isTrashed, required this.isTrashed,
this.libraryId, this.libraryId,
this.livePhotoVideoId, this.livePhotoVideoId,
@@ -36,7 +34,7 @@ class AssetResponseDto {
required this.originalPath, required this.originalPath,
this.owner, this.owner,
required this.ownerId, required this.ownerId,
this.people = const [], this.people,
required this.resized, required this.resized,
this.smartInfo, this.smartInfo,
this.stack = const [], this.stack = const [],
@@ -77,28 +75,10 @@ class AssetResponseDto {
bool isArchived; bool isArchived;
/// This property was deprecated in v1.104.0
///
/// Please note: This property should have been non-nullable! Since the specification file
/// does not include a default value (using the "default:" property), however, the generated
/// source code must fall back to having a nullable type.
/// Consider adding a "default:" property in the specification file to hide this note.
///
bool? isExternal;
bool isFavorite; bool isFavorite;
bool isOffline; bool isOffline;
/// This property was deprecated in v1.104.0
///
/// Please note: This property should have been non-nullable! Since the specification file
/// does not include a default value (using the "default:" property), however, the generated
/// source code must fall back to having a nullable type.
/// Consider adding a "default:" property in the specification file to hide this note.
///
bool? isReadOnly;
bool isTrashed; bool isTrashed;
/// This property was deprecated in v1.106.0 /// This property was deprecated in v1.106.0
@@ -122,7 +102,13 @@ class AssetResponseDto {
String ownerId; String ownerId;
List<PersonWithFacesResponseDto> people; ///
/// Please note: This property should have been non-nullable! Since the specification file
/// does not include a default value (using the "default:" property), however, the generated
/// source code must fall back to having a nullable type.
/// Consider adding a "default:" property in the specification file to hide this note.
///
PeopleWithFacesResponseDto? people;
bool resized; bool resized;
@@ -161,10 +147,8 @@ class AssetResponseDto {
other.hasMetadata == hasMetadata && other.hasMetadata == hasMetadata &&
other.id == id && other.id == id &&
other.isArchived == isArchived && other.isArchived == isArchived &&
other.isExternal == isExternal &&
other.isFavorite == isFavorite && other.isFavorite == isFavorite &&
other.isOffline == isOffline && other.isOffline == isOffline &&
other.isReadOnly == isReadOnly &&
other.isTrashed == isTrashed && other.isTrashed == isTrashed &&
other.libraryId == libraryId && other.libraryId == libraryId &&
other.livePhotoVideoId == livePhotoVideoId && other.livePhotoVideoId == livePhotoVideoId &&
@@ -173,7 +157,7 @@ class AssetResponseDto {
other.originalPath == originalPath && other.originalPath == originalPath &&
other.owner == owner && other.owner == owner &&
other.ownerId == ownerId && other.ownerId == ownerId &&
_deepEquality.equals(other.people, people) && other.people == people &&
other.resized == resized && other.resized == resized &&
other.smartInfo == smartInfo && other.smartInfo == smartInfo &&
_deepEquality.equals(other.stack, stack) && _deepEquality.equals(other.stack, stack) &&
@@ -198,10 +182,8 @@ class AssetResponseDto {
(hasMetadata.hashCode) + (hasMetadata.hashCode) +
(id.hashCode) + (id.hashCode) +
(isArchived.hashCode) + (isArchived.hashCode) +
(isExternal == null ? 0 : isExternal!.hashCode) +
(isFavorite.hashCode) + (isFavorite.hashCode) +
(isOffline.hashCode) + (isOffline.hashCode) +
(isReadOnly == null ? 0 : isReadOnly!.hashCode) +
(isTrashed.hashCode) + (isTrashed.hashCode) +
(libraryId == null ? 0 : libraryId!.hashCode) + (libraryId == null ? 0 : libraryId!.hashCode) +
(livePhotoVideoId == null ? 0 : livePhotoVideoId!.hashCode) + (livePhotoVideoId == null ? 0 : livePhotoVideoId!.hashCode) +
@@ -210,7 +192,7 @@ class AssetResponseDto {
(originalPath.hashCode) + (originalPath.hashCode) +
(owner == null ? 0 : owner!.hashCode) + (owner == null ? 0 : owner!.hashCode) +
(ownerId.hashCode) + (ownerId.hashCode) +
(people.hashCode) + (people == null ? 0 : people!.hashCode) +
(resized.hashCode) + (resized.hashCode) +
(smartInfo == null ? 0 : smartInfo!.hashCode) + (smartInfo == null ? 0 : smartInfo!.hashCode) +
(stack.hashCode) + (stack.hashCode) +
@@ -222,7 +204,7 @@ class AssetResponseDto {
(updatedAt.hashCode); (updatedAt.hashCode);
@override @override
String toString() => 'AssetResponseDto[checksum=$checksum, deviceAssetId=$deviceAssetId, deviceId=$deviceId, duplicateId=$duplicateId, duration=$duration, exifInfo=$exifInfo, fileCreatedAt=$fileCreatedAt, fileModifiedAt=$fileModifiedAt, hasMetadata=$hasMetadata, id=$id, isArchived=$isArchived, isExternal=$isExternal, isFavorite=$isFavorite, isOffline=$isOffline, isReadOnly=$isReadOnly, isTrashed=$isTrashed, libraryId=$libraryId, livePhotoVideoId=$livePhotoVideoId, localDateTime=$localDateTime, originalFileName=$originalFileName, originalPath=$originalPath, owner=$owner, ownerId=$ownerId, people=$people, resized=$resized, smartInfo=$smartInfo, stack=$stack, stackCount=$stackCount, stackParentId=$stackParentId, tags=$tags, thumbhash=$thumbhash, type=$type, updatedAt=$updatedAt]'; String toString() => 'AssetResponseDto[checksum=$checksum, deviceAssetId=$deviceAssetId, deviceId=$deviceId, duplicateId=$duplicateId, duration=$duration, exifInfo=$exifInfo, fileCreatedAt=$fileCreatedAt, fileModifiedAt=$fileModifiedAt, hasMetadata=$hasMetadata, id=$id, isArchived=$isArchived, isFavorite=$isFavorite, isOffline=$isOffline, isTrashed=$isTrashed, libraryId=$libraryId, livePhotoVideoId=$livePhotoVideoId, localDateTime=$localDateTime, originalFileName=$originalFileName, originalPath=$originalPath, owner=$owner, ownerId=$ownerId, people=$people, resized=$resized, smartInfo=$smartInfo, stack=$stack, stackCount=$stackCount, stackParentId=$stackParentId, tags=$tags, thumbhash=$thumbhash, type=$type, updatedAt=$updatedAt]';
Map<String, dynamic> toJson() { Map<String, dynamic> toJson() {
final json = <String, dynamic>{}; final json = <String, dynamic>{};
@@ -245,18 +227,8 @@ class AssetResponseDto {
json[r'hasMetadata'] = this.hasMetadata; json[r'hasMetadata'] = this.hasMetadata;
json[r'id'] = this.id; json[r'id'] = this.id;
json[r'isArchived'] = this.isArchived; json[r'isArchived'] = this.isArchived;
if (this.isExternal != null) {
json[r'isExternal'] = this.isExternal;
} else {
// json[r'isExternal'] = null;
}
json[r'isFavorite'] = this.isFavorite; json[r'isFavorite'] = this.isFavorite;
json[r'isOffline'] = this.isOffline; json[r'isOffline'] = this.isOffline;
if (this.isReadOnly != null) {
json[r'isReadOnly'] = this.isReadOnly;
} else {
// json[r'isReadOnly'] = null;
}
json[r'isTrashed'] = this.isTrashed; json[r'isTrashed'] = this.isTrashed;
if (this.libraryId != null) { if (this.libraryId != null) {
json[r'libraryId'] = this.libraryId; json[r'libraryId'] = this.libraryId;
@@ -277,7 +249,11 @@ class AssetResponseDto {
// json[r'owner'] = null; // json[r'owner'] = null;
} }
json[r'ownerId'] = this.ownerId; json[r'ownerId'] = this.ownerId;
if (this.people != null) {
json[r'people'] = this.people; json[r'people'] = this.people;
} else {
// json[r'people'] = null;
}
json[r'resized'] = this.resized; json[r'resized'] = this.resized;
if (this.smartInfo != null) { if (this.smartInfo != null) {
json[r'smartInfo'] = this.smartInfo; json[r'smartInfo'] = this.smartInfo;
@@ -325,10 +301,8 @@ class AssetResponseDto {
hasMetadata: mapValueOfType<bool>(json, r'hasMetadata')!, hasMetadata: mapValueOfType<bool>(json, r'hasMetadata')!,
id: mapValueOfType<String>(json, r'id')!, id: mapValueOfType<String>(json, r'id')!,
isArchived: mapValueOfType<bool>(json, r'isArchived')!, isArchived: mapValueOfType<bool>(json, r'isArchived')!,
isExternal: mapValueOfType<bool>(json, r'isExternal'),
isFavorite: mapValueOfType<bool>(json, r'isFavorite')!, isFavorite: mapValueOfType<bool>(json, r'isFavorite')!,
isOffline: mapValueOfType<bool>(json, r'isOffline')!, isOffline: mapValueOfType<bool>(json, r'isOffline')!,
isReadOnly: mapValueOfType<bool>(json, r'isReadOnly'),
isTrashed: mapValueOfType<bool>(json, r'isTrashed')!, isTrashed: mapValueOfType<bool>(json, r'isTrashed')!,
libraryId: mapValueOfType<String>(json, r'libraryId'), libraryId: mapValueOfType<String>(json, r'libraryId'),
livePhotoVideoId: mapValueOfType<String>(json, r'livePhotoVideoId'), livePhotoVideoId: mapValueOfType<String>(json, r'livePhotoVideoId'),
@@ -337,7 +311,7 @@ class AssetResponseDto {
originalPath: mapValueOfType<String>(json, r'originalPath')!, originalPath: mapValueOfType<String>(json, r'originalPath')!,
owner: UserResponseDto.fromJson(json[r'owner']), owner: UserResponseDto.fromJson(json[r'owner']),
ownerId: mapValueOfType<String>(json, r'ownerId')!, ownerId: mapValueOfType<String>(json, r'ownerId')!,
people: PersonWithFacesResponseDto.listFromJson(json[r'people']), people: PeopleWithFacesResponseDto.fromJson(json[r'people']),
resized: mapValueOfType<bool>(json, r'resized')!, resized: mapValueOfType<bool>(json, r'resized')!,
smartInfo: SmartInfoResponseDto.fromJson(json[r'smartInfo']), smartInfo: SmartInfoResponseDto.fromJson(json[r'smartInfo']),
stack: AssetResponseDto.listFromJson(json[r'stack']), stack: AssetResponseDto.listFromJson(json[r'stack']),

View File

@@ -17,12 +17,10 @@ class CreateAlbumDto {
this.albumUsers = const [], this.albumUsers = const [],
this.assetIds = const [], this.assetIds = const [],
this.description, this.description,
this.sharedWithUserIds = const [],
}); });
String albumName; String albumName;
/// This property was added in v1.104.0
List<AlbumUserCreateDto> albumUsers; List<AlbumUserCreateDto> albumUsers;
List<String> assetIds; List<String> assetIds;
@@ -35,16 +33,12 @@ class CreateAlbumDto {
/// ///
String? description; String? description;
/// This property was deprecated in v1.104.0
List<String> sharedWithUserIds;
@override @override
bool operator ==(Object other) => identical(this, other) || other is CreateAlbumDto && bool operator ==(Object other) => identical(this, other) || other is CreateAlbumDto &&
other.albumName == albumName && other.albumName == albumName &&
_deepEquality.equals(other.albumUsers, albumUsers) && _deepEquality.equals(other.albumUsers, albumUsers) &&
_deepEquality.equals(other.assetIds, assetIds) && _deepEquality.equals(other.assetIds, assetIds) &&
other.description == description && other.description == description;
_deepEquality.equals(other.sharedWithUserIds, sharedWithUserIds);
@override @override
int get hashCode => int get hashCode =>
@@ -52,11 +46,10 @@ class CreateAlbumDto {
(albumName.hashCode) + (albumName.hashCode) +
(albumUsers.hashCode) + (albumUsers.hashCode) +
(assetIds.hashCode) + (assetIds.hashCode) +
(description == null ? 0 : description!.hashCode) + (description == null ? 0 : description!.hashCode);
(sharedWithUserIds.hashCode);
@override @override
String toString() => 'CreateAlbumDto[albumName=$albumName, albumUsers=$albumUsers, assetIds=$assetIds, description=$description, sharedWithUserIds=$sharedWithUserIds]'; String toString() => 'CreateAlbumDto[albumName=$albumName, albumUsers=$albumUsers, assetIds=$assetIds, description=$description]';
Map<String, dynamic> toJson() { Map<String, dynamic> toJson() {
final json = <String, dynamic>{}; final json = <String, dynamic>{};
@@ -68,7 +61,6 @@ class CreateAlbumDto {
} else { } else {
// json[r'description'] = null; // json[r'description'] = null;
} }
json[r'sharedWithUserIds'] = this.sharedWithUserIds;
return json; return json;
} }
@@ -86,9 +78,6 @@ class CreateAlbumDto {
? (json[r'assetIds'] as Iterable).cast<String>().toList(growable: false) ? (json[r'assetIds'] as Iterable).cast<String>().toList(growable: false)
: const [], : const [],
description: mapValueOfType<String>(json, r'description'), description: mapValueOfType<String>(json, r'description'),
sharedWithUserIds: json[r'sharedWithUserIds'] is Iterable
? (json[r'sharedWithUserIds'] as Iterable).cast<String>().toList(growable: false)
: const [],
); );
} }
return null; return null;

View File

@@ -14,37 +14,30 @@ class MemoryLaneResponseDto {
/// Returns a new [MemoryLaneResponseDto] instance. /// Returns a new [MemoryLaneResponseDto] instance.
MemoryLaneResponseDto({ MemoryLaneResponseDto({
this.assets = const [], this.assets = const [],
required this.title,
required this.yearsAgo, required this.yearsAgo,
}); });
List<AssetResponseDto> assets; List<AssetResponseDto> assets;
/// This property was deprecated in v1.100.0
String title;
int yearsAgo; int yearsAgo;
@override @override
bool operator ==(Object other) => identical(this, other) || other is MemoryLaneResponseDto && bool operator ==(Object other) => identical(this, other) || other is MemoryLaneResponseDto &&
_deepEquality.equals(other.assets, assets) && _deepEquality.equals(other.assets, assets) &&
other.title == title &&
other.yearsAgo == yearsAgo; other.yearsAgo == yearsAgo;
@override @override
int get hashCode => int get hashCode =>
// ignore: unnecessary_parenthesis // ignore: unnecessary_parenthesis
(assets.hashCode) + (assets.hashCode) +
(title.hashCode) +
(yearsAgo.hashCode); (yearsAgo.hashCode);
@override @override
String toString() => 'MemoryLaneResponseDto[assets=$assets, title=$title, yearsAgo=$yearsAgo]'; String toString() => 'MemoryLaneResponseDto[assets=$assets, yearsAgo=$yearsAgo]';
Map<String, dynamic> toJson() { Map<String, dynamic> toJson() {
final json = <String, dynamic>{}; final json = <String, dynamic>{};
json[r'assets'] = this.assets; json[r'assets'] = this.assets;
json[r'title'] = this.title;
json[r'yearsAgo'] = this.yearsAgo; json[r'yearsAgo'] = this.yearsAgo;
return json; return json;
} }
@@ -58,7 +51,6 @@ class MemoryLaneResponseDto {
return MemoryLaneResponseDto( return MemoryLaneResponseDto(
assets: AssetResponseDto.listFromJson(json[r'assets']), assets: AssetResponseDto.listFromJson(json[r'assets']),
title: mapValueOfType<String>(json, r'title')!,
yearsAgo: mapValueOfType<int>(json, r'yearsAgo')!, yearsAgo: mapValueOfType<int>(json, r'yearsAgo')!,
); );
} }
@@ -108,7 +100,6 @@ class MemoryLaneResponseDto {
/// The list of required keys that must be present in a JSON. /// The list of required keys that must be present in a JSON.
static const requiredKeys = <String>{ static const requiredKeys = <String>{
'assets', 'assets',
'title',
'yearsAgo', 'yearsAgo',
}; };
} }

View File

@@ -39,7 +39,6 @@ class MetadataSearchDto {
this.page, this.page,
this.personIds = const [], this.personIds = const [],
this.previewPath, this.previewPath,
this.resizePath,
this.size, this.size,
this.state, this.state,
this.takenAfter, this.takenAfter,
@@ -50,7 +49,6 @@ class MetadataSearchDto {
this.type, this.type,
this.updatedAfter, this.updatedAfter,
this.updatedBefore, this.updatedBefore,
this.webpPath,
this.withArchived = false, this.withArchived = false,
this.withDeleted, this.withDeleted,
this.withExif, this.withExif,
@@ -261,15 +259,6 @@ class MetadataSearchDto {
/// ///
String? previewPath; String? previewPath;
/// This property was deprecated in v1.100.0
///
/// Please note: This property should have been non-nullable! Since the specification file
/// does not include a default value (using the "default:" property), however, the generated
/// source code must fall back to having a nullable type.
/// Consider adding a "default:" property in the specification file to hide this note.
///
String? resizePath;
/// Minimum value: 1 /// Minimum value: 1
/// Maximum value: 1000 /// Maximum value: 1000
/// ///
@@ -352,15 +341,6 @@ class MetadataSearchDto {
/// ///
DateTime? updatedBefore; DateTime? updatedBefore;
/// This property was deprecated in v1.100.0
///
/// Please note: This property should have been non-nullable! Since the specification file
/// does not include a default value (using the "default:" property), however, the generated
/// source code must fall back to having a nullable type.
/// Consider adding a "default:" property in the specification file to hide this note.
///
String? webpPath;
bool withArchived; bool withArchived;
/// ///
@@ -423,7 +403,6 @@ class MetadataSearchDto {
other.page == page && other.page == page &&
_deepEquality.equals(other.personIds, personIds) && _deepEquality.equals(other.personIds, personIds) &&
other.previewPath == previewPath && other.previewPath == previewPath &&
other.resizePath == resizePath &&
other.size == size && other.size == size &&
other.state == state && other.state == state &&
other.takenAfter == takenAfter && other.takenAfter == takenAfter &&
@@ -434,7 +413,6 @@ class MetadataSearchDto {
other.type == type && other.type == type &&
other.updatedAfter == updatedAfter && other.updatedAfter == updatedAfter &&
other.updatedBefore == updatedBefore && other.updatedBefore == updatedBefore &&
other.webpPath == webpPath &&
other.withArchived == withArchived && other.withArchived == withArchived &&
other.withDeleted == withDeleted && other.withDeleted == withDeleted &&
other.withExif == withExif && other.withExif == withExif &&
@@ -470,7 +448,6 @@ class MetadataSearchDto {
(page == null ? 0 : page!.hashCode) + (page == null ? 0 : page!.hashCode) +
(personIds.hashCode) + (personIds.hashCode) +
(previewPath == null ? 0 : previewPath!.hashCode) + (previewPath == null ? 0 : previewPath!.hashCode) +
(resizePath == null ? 0 : resizePath!.hashCode) +
(size == null ? 0 : size!.hashCode) + (size == null ? 0 : size!.hashCode) +
(state == null ? 0 : state!.hashCode) + (state == null ? 0 : state!.hashCode) +
(takenAfter == null ? 0 : takenAfter!.hashCode) + (takenAfter == null ? 0 : takenAfter!.hashCode) +
@@ -481,7 +458,6 @@ class MetadataSearchDto {
(type == null ? 0 : type!.hashCode) + (type == null ? 0 : type!.hashCode) +
(updatedAfter == null ? 0 : updatedAfter!.hashCode) + (updatedAfter == null ? 0 : updatedAfter!.hashCode) +
(updatedBefore == null ? 0 : updatedBefore!.hashCode) + (updatedBefore == null ? 0 : updatedBefore!.hashCode) +
(webpPath == null ? 0 : webpPath!.hashCode) +
(withArchived.hashCode) + (withArchived.hashCode) +
(withDeleted == null ? 0 : withDeleted!.hashCode) + (withDeleted == null ? 0 : withDeleted!.hashCode) +
(withExif == null ? 0 : withExif!.hashCode) + (withExif == null ? 0 : withExif!.hashCode) +
@@ -489,7 +465,7 @@ class MetadataSearchDto {
(withStacked == null ? 0 : withStacked!.hashCode); (withStacked == null ? 0 : withStacked!.hashCode);
@override @override
String toString() => 'MetadataSearchDto[checksum=$checksum, city=$city, country=$country, createdAfter=$createdAfter, createdBefore=$createdBefore, deviceAssetId=$deviceAssetId, deviceId=$deviceId, encodedVideoPath=$encodedVideoPath, id=$id, isArchived=$isArchived, isEncoded=$isEncoded, isFavorite=$isFavorite, isMotion=$isMotion, isNotInAlbum=$isNotInAlbum, isOffline=$isOffline, isVisible=$isVisible, lensModel=$lensModel, libraryId=$libraryId, make=$make, model=$model, order=$order, originalFileName=$originalFileName, originalPath=$originalPath, page=$page, personIds=$personIds, previewPath=$previewPath, resizePath=$resizePath, size=$size, state=$state, takenAfter=$takenAfter, takenBefore=$takenBefore, thumbnailPath=$thumbnailPath, trashedAfter=$trashedAfter, trashedBefore=$trashedBefore, type=$type, updatedAfter=$updatedAfter, updatedBefore=$updatedBefore, webpPath=$webpPath, withArchived=$withArchived, withDeleted=$withDeleted, withExif=$withExif, withPeople=$withPeople, withStacked=$withStacked]'; String toString() => 'MetadataSearchDto[checksum=$checksum, city=$city, country=$country, createdAfter=$createdAfter, createdBefore=$createdBefore, deviceAssetId=$deviceAssetId, deviceId=$deviceId, encodedVideoPath=$encodedVideoPath, id=$id, isArchived=$isArchived, isEncoded=$isEncoded, isFavorite=$isFavorite, isMotion=$isMotion, isNotInAlbum=$isNotInAlbum, isOffline=$isOffline, isVisible=$isVisible, lensModel=$lensModel, libraryId=$libraryId, make=$make, model=$model, order=$order, originalFileName=$originalFileName, originalPath=$originalPath, page=$page, personIds=$personIds, previewPath=$previewPath, size=$size, state=$state, takenAfter=$takenAfter, takenBefore=$takenBefore, thumbnailPath=$thumbnailPath, trashedAfter=$trashedAfter, trashedBefore=$trashedBefore, type=$type, updatedAfter=$updatedAfter, updatedBefore=$updatedBefore, withArchived=$withArchived, withDeleted=$withDeleted, withExif=$withExif, withPeople=$withPeople, withStacked=$withStacked]';
Map<String, dynamic> toJson() { Map<String, dynamic> toJson() {
final json = <String, dynamic>{}; final json = <String, dynamic>{};
@@ -619,11 +595,6 @@ class MetadataSearchDto {
} else { } else {
// json[r'previewPath'] = null; // json[r'previewPath'] = null;
} }
if (this.resizePath != null) {
json[r'resizePath'] = this.resizePath;
} else {
// json[r'resizePath'] = null;
}
if (this.size != null) { if (this.size != null) {
json[r'size'] = this.size; json[r'size'] = this.size;
} else { } else {
@@ -673,11 +644,6 @@ class MetadataSearchDto {
json[r'updatedBefore'] = this.updatedBefore!.toUtc().toIso8601String(); json[r'updatedBefore'] = this.updatedBefore!.toUtc().toIso8601String();
} else { } else {
// json[r'updatedBefore'] = null; // json[r'updatedBefore'] = null;
}
if (this.webpPath != null) {
json[r'webpPath'] = this.webpPath;
} else {
// json[r'webpPath'] = null;
} }
json[r'withArchived'] = this.withArchived; json[r'withArchived'] = this.withArchived;
if (this.withDeleted != null) { if (this.withDeleted != null) {
@@ -739,7 +705,6 @@ class MetadataSearchDto {
? (json[r'personIds'] as Iterable).cast<String>().toList(growable: false) ? (json[r'personIds'] as Iterable).cast<String>().toList(growable: false)
: const [], : const [],
previewPath: mapValueOfType<String>(json, r'previewPath'), previewPath: mapValueOfType<String>(json, r'previewPath'),
resizePath: mapValueOfType<String>(json, r'resizePath'),
size: num.parse('${json[r'size']}'), size: num.parse('${json[r'size']}'),
state: mapValueOfType<String>(json, r'state'), state: mapValueOfType<String>(json, r'state'),
takenAfter: mapDateTime(json, r'takenAfter', r''), takenAfter: mapDateTime(json, r'takenAfter', r''),
@@ -750,7 +715,6 @@ class MetadataSearchDto {
type: AssetTypeEnum.fromJson(json[r'type']), type: AssetTypeEnum.fromJson(json[r'type']),
updatedAfter: mapDateTime(json, r'updatedAfter', r''), updatedAfter: mapDateTime(json, r'updatedAfter', r''),
updatedBefore: mapDateTime(json, r'updatedBefore', r''), updatedBefore: mapDateTime(json, r'updatedBefore', r''),
webpPath: mapValueOfType<String>(json, r'webpPath'),
withArchived: mapValueOfType<bool>(json, r'withArchived') ?? false, withArchived: mapValueOfType<bool>(json, r'withArchived') ?? false,
withDeleted: mapValueOfType<bool>(json, r'withDeleted'), withDeleted: mapValueOfType<bool>(json, r'withDeleted'),
withExif: mapValueOfType<bool>(json, r'withExif'), withExif: mapValueOfType<bool>(json, r'withExif'),

View File

@@ -14,30 +14,15 @@ class PartnerResponseDto {
/// Returns a new [PartnerResponseDto] instance. /// Returns a new [PartnerResponseDto] instance.
PartnerResponseDto({ PartnerResponseDto({
required this.avatarColor, required this.avatarColor,
required this.createdAt,
required this.deletedAt,
required this.email, required this.email,
required this.id, required this.id,
this.inTimeline, this.inTimeline,
required this.isAdmin,
this.memoriesEnabled,
required this.name, required this.name,
required this.oauthId,
required this.profileImagePath, required this.profileImagePath,
required this.quotaSizeInBytes,
required this.quotaUsageInBytes,
required this.shouldChangePassword,
required this.status,
required this.storageLabel,
required this.updatedAt,
}); });
UserAvatarColor avatarColor; UserAvatarColor avatarColor;
DateTime createdAt;
DateTime? deletedAt;
String email; String email;
String id; String id;
@@ -50,121 +35,44 @@ class PartnerResponseDto {
/// ///
bool? inTimeline; bool? inTimeline;
bool isAdmin;
///
/// Please note: This property should have been non-nullable! Since the specification file
/// does not include a default value (using the "default:" property), however, the generated
/// source code must fall back to having a nullable type.
/// Consider adding a "default:" property in the specification file to hide this note.
///
bool? memoriesEnabled;
String name; String name;
String oauthId;
String profileImagePath; String profileImagePath;
int? quotaSizeInBytes;
int? quotaUsageInBytes;
bool shouldChangePassword;
UserStatus status;
String? storageLabel;
DateTime updatedAt;
@override @override
bool operator ==(Object other) => identical(this, other) || other is PartnerResponseDto && bool operator ==(Object other) => identical(this, other) || other is PartnerResponseDto &&
other.avatarColor == avatarColor && other.avatarColor == avatarColor &&
other.createdAt == createdAt &&
other.deletedAt == deletedAt &&
other.email == email && other.email == email &&
other.id == id && other.id == id &&
other.inTimeline == inTimeline && other.inTimeline == inTimeline &&
other.isAdmin == isAdmin &&
other.memoriesEnabled == memoriesEnabled &&
other.name == name && other.name == name &&
other.oauthId == oauthId && other.profileImagePath == profileImagePath;
other.profileImagePath == profileImagePath &&
other.quotaSizeInBytes == quotaSizeInBytes &&
other.quotaUsageInBytes == quotaUsageInBytes &&
other.shouldChangePassword == shouldChangePassword &&
other.status == status &&
other.storageLabel == storageLabel &&
other.updatedAt == updatedAt;
@override @override
int get hashCode => int get hashCode =>
// ignore: unnecessary_parenthesis // ignore: unnecessary_parenthesis
(avatarColor.hashCode) + (avatarColor.hashCode) +
(createdAt.hashCode) +
(deletedAt == null ? 0 : deletedAt!.hashCode) +
(email.hashCode) + (email.hashCode) +
(id.hashCode) + (id.hashCode) +
(inTimeline == null ? 0 : inTimeline!.hashCode) + (inTimeline == null ? 0 : inTimeline!.hashCode) +
(isAdmin.hashCode) +
(memoriesEnabled == null ? 0 : memoriesEnabled!.hashCode) +
(name.hashCode) + (name.hashCode) +
(oauthId.hashCode) + (profileImagePath.hashCode);
(profileImagePath.hashCode) +
(quotaSizeInBytes == null ? 0 : quotaSizeInBytes!.hashCode) +
(quotaUsageInBytes == null ? 0 : quotaUsageInBytes!.hashCode) +
(shouldChangePassword.hashCode) +
(status.hashCode) +
(storageLabel == null ? 0 : storageLabel!.hashCode) +
(updatedAt.hashCode);
@override @override
String toString() => 'PartnerResponseDto[avatarColor=$avatarColor, createdAt=$createdAt, deletedAt=$deletedAt, email=$email, id=$id, inTimeline=$inTimeline, isAdmin=$isAdmin, memoriesEnabled=$memoriesEnabled, name=$name, oauthId=$oauthId, profileImagePath=$profileImagePath, quotaSizeInBytes=$quotaSizeInBytes, quotaUsageInBytes=$quotaUsageInBytes, shouldChangePassword=$shouldChangePassword, status=$status, storageLabel=$storageLabel, updatedAt=$updatedAt]'; String toString() => 'PartnerResponseDto[avatarColor=$avatarColor, email=$email, id=$id, inTimeline=$inTimeline, name=$name, profileImagePath=$profileImagePath]';
Map<String, dynamic> toJson() { Map<String, dynamic> toJson() {
final json = <String, dynamic>{}; final json = <String, dynamic>{};
json[r'avatarColor'] = this.avatarColor; json[r'avatarColor'] = this.avatarColor;
json[r'createdAt'] = this.createdAt.toUtc().toIso8601String();
if (this.deletedAt != null) {
json[r'deletedAt'] = this.deletedAt!.toUtc().toIso8601String();
} else {
// json[r'deletedAt'] = null;
}
json[r'email'] = this.email; json[r'email'] = this.email;
json[r'id'] = this.id; json[r'id'] = this.id;
if (this.inTimeline != null) { if (this.inTimeline != null) {
json[r'inTimeline'] = this.inTimeline; json[r'inTimeline'] = this.inTimeline;
} else { } else {
// json[r'inTimeline'] = null; // json[r'inTimeline'] = null;
}
json[r'isAdmin'] = this.isAdmin;
if (this.memoriesEnabled != null) {
json[r'memoriesEnabled'] = this.memoriesEnabled;
} else {
// json[r'memoriesEnabled'] = null;
} }
json[r'name'] = this.name; json[r'name'] = this.name;
json[r'oauthId'] = this.oauthId;
json[r'profileImagePath'] = this.profileImagePath; json[r'profileImagePath'] = this.profileImagePath;
if (this.quotaSizeInBytes != null) {
json[r'quotaSizeInBytes'] = this.quotaSizeInBytes;
} else {
// json[r'quotaSizeInBytes'] = null;
}
if (this.quotaUsageInBytes != null) {
json[r'quotaUsageInBytes'] = this.quotaUsageInBytes;
} else {
// json[r'quotaUsageInBytes'] = null;
}
json[r'shouldChangePassword'] = this.shouldChangePassword;
json[r'status'] = this.status;
if (this.storageLabel != null) {
json[r'storageLabel'] = this.storageLabel;
} else {
// json[r'storageLabel'] = null;
}
json[r'updatedAt'] = this.updatedAt.toUtc().toIso8601String();
return json; return json;
} }
@@ -177,22 +85,11 @@ class PartnerResponseDto {
return PartnerResponseDto( return PartnerResponseDto(
avatarColor: UserAvatarColor.fromJson(json[r'avatarColor'])!, avatarColor: UserAvatarColor.fromJson(json[r'avatarColor'])!,
createdAt: mapDateTime(json, r'createdAt', r'')!,
deletedAt: mapDateTime(json, r'deletedAt', r''),
email: mapValueOfType<String>(json, r'email')!, email: mapValueOfType<String>(json, r'email')!,
id: mapValueOfType<String>(json, r'id')!, id: mapValueOfType<String>(json, r'id')!,
inTimeline: mapValueOfType<bool>(json, r'inTimeline'), inTimeline: mapValueOfType<bool>(json, r'inTimeline'),
isAdmin: mapValueOfType<bool>(json, r'isAdmin')!,
memoriesEnabled: mapValueOfType<bool>(json, r'memoriesEnabled'),
name: mapValueOfType<String>(json, r'name')!, name: mapValueOfType<String>(json, r'name')!,
oauthId: mapValueOfType<String>(json, r'oauthId')!,
profileImagePath: mapValueOfType<String>(json, r'profileImagePath')!, profileImagePath: mapValueOfType<String>(json, r'profileImagePath')!,
quotaSizeInBytes: mapValueOfType<int>(json, r'quotaSizeInBytes'),
quotaUsageInBytes: mapValueOfType<int>(json, r'quotaUsageInBytes'),
shouldChangePassword: mapValueOfType<bool>(json, r'shouldChangePassword')!,
status: UserStatus.fromJson(json[r'status'])!,
storageLabel: mapValueOfType<String>(json, r'storageLabel'),
updatedAt: mapDateTime(json, r'updatedAt', r'')!,
); );
} }
return null; return null;
@@ -241,20 +138,10 @@ class PartnerResponseDto {
/// The list of required keys that must be present in a JSON. /// The list of required keys that must be present in a JSON.
static const requiredKeys = <String>{ static const requiredKeys = <String>{
'avatarColor', 'avatarColor',
'createdAt',
'deletedAt',
'email', 'email',
'id', 'id',
'isAdmin',
'name', 'name',
'oauthId',
'profileImagePath', 'profileImagePath',
'quotaSizeInBytes',
'quotaUsageInBytes',
'shouldChangePassword',
'status',
'storageLabel',
'updatedAt',
}; };
} }

View File

@@ -0,0 +1,106 @@
//
// AUTO-GENERATED FILE, DO NOT MODIFY!
//
// @dart=2.18
// ignore_for_file: unused_element, unused_import
// ignore_for_file: always_put_required_named_parameters_first
// ignore_for_file: constant_identifier_names
// ignore_for_file: lines_longer_than_80_chars
part of openapi.api;
class PeopleWithFacesResponseDto {
/// Returns a new [PeopleWithFacesResponseDto] instance.
PeopleWithFacesResponseDto({
required this.numberOfFaces,
this.visiblePeople = const [],
});
int numberOfFaces;
List<PersonWithFacesResponseDto> visiblePeople;
@override
bool operator ==(Object other) => identical(this, other) || other is PeopleWithFacesResponseDto &&
other.numberOfFaces == numberOfFaces &&
_deepEquality.equals(other.visiblePeople, visiblePeople);
@override
int get hashCode =>
// ignore: unnecessary_parenthesis
(numberOfFaces.hashCode) +
(visiblePeople.hashCode);
@override
String toString() => 'PeopleWithFacesResponseDto[numberOfFaces=$numberOfFaces, visiblePeople=$visiblePeople]';
Map<String, dynamic> toJson() {
final json = <String, dynamic>{};
json[r'numberOfFaces'] = this.numberOfFaces;
json[r'visiblePeople'] = this.visiblePeople;
return json;
}
/// Returns a new [PeopleWithFacesResponseDto] instance and imports its values from
/// [value] if it's a [Map], null otherwise.
// ignore: prefer_constructors_over_static_methods
static PeopleWithFacesResponseDto? fromJson(dynamic value) {
if (value is Map) {
final json = value.cast<String, dynamic>();
return PeopleWithFacesResponseDto(
numberOfFaces: mapValueOfType<int>(json, r'numberOfFaces')!,
visiblePeople: PersonWithFacesResponseDto.listFromJson(json[r'visiblePeople']),
);
}
return null;
}
static List<PeopleWithFacesResponseDto> listFromJson(dynamic json, {bool growable = false,}) {
final result = <PeopleWithFacesResponseDto>[];
if (json is List && json.isNotEmpty) {
for (final row in json) {
final value = PeopleWithFacesResponseDto.fromJson(row);
if (value != null) {
result.add(value);
}
}
}
return result.toList(growable: growable);
}
static Map<String, PeopleWithFacesResponseDto> mapFromJson(dynamic json) {
final map = <String, PeopleWithFacesResponseDto>{};
if (json is Map && json.isNotEmpty) {
json = json.cast<String, dynamic>(); // ignore: parameter_assignments
for (final entry in json.entries) {
final value = PeopleWithFacesResponseDto.fromJson(entry.value);
if (value != null) {
map[entry.key] = value;
}
}
}
return map;
}
// maps a json object with a list of PeopleWithFacesResponseDto-objects as value to a dart map
static Map<String, List<PeopleWithFacesResponseDto>> mapListFromJson(dynamic json, {bool growable = false,}) {
final map = <String, List<PeopleWithFacesResponseDto>>{};
if (json is Map && json.isNotEmpty) {
// ignore: parameter_assignments
json = json.cast<String, dynamic>();
for (final entry in json.entries) {
map[entry.key] = PeopleWithFacesResponseDto.listFromJson(entry.value, growable: growable,);
}
}
return map;
}
/// The list of required keys that must be present in a JSON.
static const requiredKeys = <String>{
'numberOfFaces',
'visiblePeople',
};
}

View File

@@ -10,9 +10,9 @@
part of openapi.api; part of openapi.api;
class ServerInfoResponseDto { class ServerStorageResponseDto {
/// Returns a new [ServerInfoResponseDto] instance. /// Returns a new [ServerStorageResponseDto] instance.
ServerInfoResponseDto({ ServerStorageResponseDto({
required this.diskAvailable, required this.diskAvailable,
required this.diskAvailableRaw, required this.diskAvailableRaw,
required this.diskSize, required this.diskSize,
@@ -37,7 +37,7 @@ class ServerInfoResponseDto {
int diskUseRaw; int diskUseRaw;
@override @override
bool operator ==(Object other) => identical(this, other) || other is ServerInfoResponseDto && bool operator ==(Object other) => identical(this, other) || other is ServerStorageResponseDto &&
other.diskAvailable == diskAvailable && other.diskAvailable == diskAvailable &&
other.diskAvailableRaw == diskAvailableRaw && other.diskAvailableRaw == diskAvailableRaw &&
other.diskSize == diskSize && other.diskSize == diskSize &&
@@ -58,7 +58,7 @@ class ServerInfoResponseDto {
(diskUseRaw.hashCode); (diskUseRaw.hashCode);
@override @override
String toString() => 'ServerInfoResponseDto[diskAvailable=$diskAvailable, diskAvailableRaw=$diskAvailableRaw, diskSize=$diskSize, diskSizeRaw=$diskSizeRaw, diskUsagePercentage=$diskUsagePercentage, diskUse=$diskUse, diskUseRaw=$diskUseRaw]'; String toString() => 'ServerStorageResponseDto[diskAvailable=$diskAvailable, diskAvailableRaw=$diskAvailableRaw, diskSize=$diskSize, diskSizeRaw=$diskSizeRaw, diskUsagePercentage=$diskUsagePercentage, diskUse=$diskUse, diskUseRaw=$diskUseRaw]';
Map<String, dynamic> toJson() { Map<String, dynamic> toJson() {
final json = <String, dynamic>{}; final json = <String, dynamic>{};
@@ -72,14 +72,14 @@ class ServerInfoResponseDto {
return json; return json;
} }
/// Returns a new [ServerInfoResponseDto] instance and imports its values from /// Returns a new [ServerStorageResponseDto] instance and imports its values from
/// [value] if it's a [Map], null otherwise. /// [value] if it's a [Map], null otherwise.
// ignore: prefer_constructors_over_static_methods // ignore: prefer_constructors_over_static_methods
static ServerInfoResponseDto? fromJson(dynamic value) { static ServerStorageResponseDto? fromJson(dynamic value) {
if (value is Map) { if (value is Map) {
final json = value.cast<String, dynamic>(); final json = value.cast<String, dynamic>();
return ServerInfoResponseDto( return ServerStorageResponseDto(
diskAvailable: mapValueOfType<String>(json, r'diskAvailable')!, diskAvailable: mapValueOfType<String>(json, r'diskAvailable')!,
diskAvailableRaw: mapValueOfType<int>(json, r'diskAvailableRaw')!, diskAvailableRaw: mapValueOfType<int>(json, r'diskAvailableRaw')!,
diskSize: mapValueOfType<String>(json, r'diskSize')!, diskSize: mapValueOfType<String>(json, r'diskSize')!,
@@ -92,11 +92,11 @@ class ServerInfoResponseDto {
return null; return null;
} }
static List<ServerInfoResponseDto> listFromJson(dynamic json, {bool growable = false,}) { static List<ServerStorageResponseDto> listFromJson(dynamic json, {bool growable = false,}) {
final result = <ServerInfoResponseDto>[]; final result = <ServerStorageResponseDto>[];
if (json is List && json.isNotEmpty) { if (json is List && json.isNotEmpty) {
for (final row in json) { for (final row in json) {
final value = ServerInfoResponseDto.fromJson(row); final value = ServerStorageResponseDto.fromJson(row);
if (value != null) { if (value != null) {
result.add(value); result.add(value);
} }
@@ -105,12 +105,12 @@ class ServerInfoResponseDto {
return result.toList(growable: growable); return result.toList(growable: growable);
} }
static Map<String, ServerInfoResponseDto> mapFromJson(dynamic json) { static Map<String, ServerStorageResponseDto> mapFromJson(dynamic json) {
final map = <String, ServerInfoResponseDto>{}; final map = <String, ServerStorageResponseDto>{};
if (json is Map && json.isNotEmpty) { if (json is Map && json.isNotEmpty) {
json = json.cast<String, dynamic>(); // ignore: parameter_assignments json = json.cast<String, dynamic>(); // ignore: parameter_assignments
for (final entry in json.entries) { for (final entry in json.entries) {
final value = ServerInfoResponseDto.fromJson(entry.value); final value = ServerStorageResponseDto.fromJson(entry.value);
if (value != null) { if (value != null) {
map[entry.key] = value; map[entry.key] = value;
} }
@@ -119,14 +119,14 @@ class ServerInfoResponseDto {
return map; return map;
} }
// maps a json object with a list of ServerInfoResponseDto-objects as value to a dart map // maps a json object with a list of ServerStorageResponseDto-objects as value to a dart map
static Map<String, List<ServerInfoResponseDto>> mapListFromJson(dynamic json, {bool growable = false,}) { static Map<String, List<ServerStorageResponseDto>> mapListFromJson(dynamic json, {bool growable = false,}) {
final map = <String, List<ServerInfoResponseDto>>{}; final map = <String, List<ServerStorageResponseDto>>{};
if (json is Map && json.isNotEmpty) { if (json is Map && json.isNotEmpty) {
// ignore: parameter_assignments // ignore: parameter_assignments
json = json.cast<String, dynamic>(); json = json.cast<String, dynamic>();
for (final entry in json.entries) { for (final entry in json.entries) {
map[entry.key] = ServerInfoResponseDto.listFromJson(entry.value, growable: growable,); map[entry.key] = ServerStorageResponseDto.listFromJson(entry.value, growable: growable,);
} }
} }
return map; return map;

View File

@@ -10,9 +10,9 @@
part of openapi.api; part of openapi.api;
class CreateUserDto { class UserAdminCreateDto {
/// Returns a new [CreateUserDto] instance. /// Returns a new [UserAdminCreateDto] instance.
CreateUserDto({ UserAdminCreateDto({
required this.email, required this.email,
this.memoriesEnabled, this.memoriesEnabled,
required this.name, required this.name,
@@ -59,7 +59,7 @@ class CreateUserDto {
String? storageLabel; String? storageLabel;
@override @override
bool operator ==(Object other) => identical(this, other) || other is CreateUserDto && bool operator ==(Object other) => identical(this, other) || other is UserAdminCreateDto &&
other.email == email && other.email == email &&
other.memoriesEnabled == memoriesEnabled && other.memoriesEnabled == memoriesEnabled &&
other.name == name && other.name == name &&
@@ -82,7 +82,7 @@ class CreateUserDto {
(storageLabel == null ? 0 : storageLabel!.hashCode); (storageLabel == null ? 0 : storageLabel!.hashCode);
@override @override
String toString() => 'CreateUserDto[email=$email, memoriesEnabled=$memoriesEnabled, name=$name, notify=$notify, password=$password, quotaSizeInBytes=$quotaSizeInBytes, shouldChangePassword=$shouldChangePassword, storageLabel=$storageLabel]'; String toString() => 'UserAdminCreateDto[email=$email, memoriesEnabled=$memoriesEnabled, name=$name, notify=$notify, password=$password, quotaSizeInBytes=$quotaSizeInBytes, shouldChangePassword=$shouldChangePassword, storageLabel=$storageLabel]';
Map<String, dynamic> toJson() { Map<String, dynamic> toJson() {
final json = <String, dynamic>{}; final json = <String, dynamic>{};
@@ -117,14 +117,14 @@ class CreateUserDto {
return json; return json;
} }
/// Returns a new [CreateUserDto] instance and imports its values from /// Returns a new [UserAdminCreateDto] instance and imports its values from
/// [value] if it's a [Map], null otherwise. /// [value] if it's a [Map], null otherwise.
// ignore: prefer_constructors_over_static_methods // ignore: prefer_constructors_over_static_methods
static CreateUserDto? fromJson(dynamic value) { static UserAdminCreateDto? fromJson(dynamic value) {
if (value is Map) { if (value is Map) {
final json = value.cast<String, dynamic>(); final json = value.cast<String, dynamic>();
return CreateUserDto( return UserAdminCreateDto(
email: mapValueOfType<String>(json, r'email')!, email: mapValueOfType<String>(json, r'email')!,
memoriesEnabled: mapValueOfType<bool>(json, r'memoriesEnabled'), memoriesEnabled: mapValueOfType<bool>(json, r'memoriesEnabled'),
name: mapValueOfType<String>(json, r'name')!, name: mapValueOfType<String>(json, r'name')!,
@@ -138,11 +138,11 @@ class CreateUserDto {
return null; return null;
} }
static List<CreateUserDto> listFromJson(dynamic json, {bool growable = false,}) { static List<UserAdminCreateDto> listFromJson(dynamic json, {bool growable = false,}) {
final result = <CreateUserDto>[]; final result = <UserAdminCreateDto>[];
if (json is List && json.isNotEmpty) { if (json is List && json.isNotEmpty) {
for (final row in json) { for (final row in json) {
final value = CreateUserDto.fromJson(row); final value = UserAdminCreateDto.fromJson(row);
if (value != null) { if (value != null) {
result.add(value); result.add(value);
} }
@@ -151,12 +151,12 @@ class CreateUserDto {
return result.toList(growable: growable); return result.toList(growable: growable);
} }
static Map<String, CreateUserDto> mapFromJson(dynamic json) { static Map<String, UserAdminCreateDto> mapFromJson(dynamic json) {
final map = <String, CreateUserDto>{}; final map = <String, UserAdminCreateDto>{};
if (json is Map && json.isNotEmpty) { if (json is Map && json.isNotEmpty) {
json = json.cast<String, dynamic>(); // ignore: parameter_assignments json = json.cast<String, dynamic>(); // ignore: parameter_assignments
for (final entry in json.entries) { for (final entry in json.entries) {
final value = CreateUserDto.fromJson(entry.value); final value = UserAdminCreateDto.fromJson(entry.value);
if (value != null) { if (value != null) {
map[entry.key] = value; map[entry.key] = value;
} }
@@ -165,14 +165,14 @@ class CreateUserDto {
return map; return map;
} }
// maps a json object with a list of CreateUserDto-objects as value to a dart map // maps a json object with a list of UserAdminCreateDto-objects as value to a dart map
static Map<String, List<CreateUserDto>> mapListFromJson(dynamic json, {bool growable = false,}) { static Map<String, List<UserAdminCreateDto>> mapListFromJson(dynamic json, {bool growable = false,}) {
final map = <String, List<CreateUserDto>>{}; final map = <String, List<UserAdminCreateDto>>{};
if (json is Map && json.isNotEmpty) { if (json is Map && json.isNotEmpty) {
// ignore: parameter_assignments // ignore: parameter_assignments
json = json.cast<String, dynamic>(); json = json.cast<String, dynamic>();
for (final entry in json.entries) { for (final entry in json.entries) {
map[entry.key] = CreateUserDto.listFromJson(entry.value, growable: growable,); map[entry.key] = UserAdminCreateDto.listFromJson(entry.value, growable: growable,);
} }
} }
return map; return map;

View File

@@ -10,9 +10,9 @@
part of openapi.api; part of openapi.api;
class DeleteUserDto { class UserAdminDeleteDto {
/// Returns a new [DeleteUserDto] instance. /// Returns a new [UserAdminDeleteDto] instance.
DeleteUserDto({ UserAdminDeleteDto({
this.force, this.force,
}); });
@@ -25,7 +25,7 @@ class DeleteUserDto {
bool? force; bool? force;
@override @override
bool operator ==(Object other) => identical(this, other) || other is DeleteUserDto && bool operator ==(Object other) => identical(this, other) || other is UserAdminDeleteDto &&
other.force == force; other.force == force;
@override @override
@@ -34,7 +34,7 @@ class DeleteUserDto {
(force == null ? 0 : force!.hashCode); (force == null ? 0 : force!.hashCode);
@override @override
String toString() => 'DeleteUserDto[force=$force]'; String toString() => 'UserAdminDeleteDto[force=$force]';
Map<String, dynamic> toJson() { Map<String, dynamic> toJson() {
final json = <String, dynamic>{}; final json = <String, dynamic>{};
@@ -46,25 +46,25 @@ class DeleteUserDto {
return json; return json;
} }
/// Returns a new [DeleteUserDto] instance and imports its values from /// Returns a new [UserAdminDeleteDto] instance and imports its values from
/// [value] if it's a [Map], null otherwise. /// [value] if it's a [Map], null otherwise.
// ignore: prefer_constructors_over_static_methods // ignore: prefer_constructors_over_static_methods
static DeleteUserDto? fromJson(dynamic value) { static UserAdminDeleteDto? fromJson(dynamic value) {
if (value is Map) { if (value is Map) {
final json = value.cast<String, dynamic>(); final json = value.cast<String, dynamic>();
return DeleteUserDto( return UserAdminDeleteDto(
force: mapValueOfType<bool>(json, r'force'), force: mapValueOfType<bool>(json, r'force'),
); );
} }
return null; return null;
} }
static List<DeleteUserDto> listFromJson(dynamic json, {bool growable = false,}) { static List<UserAdminDeleteDto> listFromJson(dynamic json, {bool growable = false,}) {
final result = <DeleteUserDto>[]; final result = <UserAdminDeleteDto>[];
if (json is List && json.isNotEmpty) { if (json is List && json.isNotEmpty) {
for (final row in json) { for (final row in json) {
final value = DeleteUserDto.fromJson(row); final value = UserAdminDeleteDto.fromJson(row);
if (value != null) { if (value != null) {
result.add(value); result.add(value);
} }
@@ -73,12 +73,12 @@ class DeleteUserDto {
return result.toList(growable: growable); return result.toList(growable: growable);
} }
static Map<String, DeleteUserDto> mapFromJson(dynamic json) { static Map<String, UserAdminDeleteDto> mapFromJson(dynamic json) {
final map = <String, DeleteUserDto>{}; final map = <String, UserAdminDeleteDto>{};
if (json is Map && json.isNotEmpty) { if (json is Map && json.isNotEmpty) {
json = json.cast<String, dynamic>(); // ignore: parameter_assignments json = json.cast<String, dynamic>(); // ignore: parameter_assignments
for (final entry in json.entries) { for (final entry in json.entries) {
final value = DeleteUserDto.fromJson(entry.value); final value = UserAdminDeleteDto.fromJson(entry.value);
if (value != null) { if (value != null) {
map[entry.key] = value; map[entry.key] = value;
} }
@@ -87,14 +87,14 @@ class DeleteUserDto {
return map; return map;
} }
// maps a json object with a list of DeleteUserDto-objects as value to a dart map // maps a json object with a list of UserAdminDeleteDto-objects as value to a dart map
static Map<String, List<DeleteUserDto>> mapListFromJson(dynamic json, {bool growable = false,}) { static Map<String, List<UserAdminDeleteDto>> mapListFromJson(dynamic json, {bool growable = false,}) {
final map = <String, List<DeleteUserDto>>{}; final map = <String, List<UserAdminDeleteDto>>{};
if (json is Map && json.isNotEmpty) { if (json is Map && json.isNotEmpty) {
// ignore: parameter_assignments // ignore: parameter_assignments
json = json.cast<String, dynamic>(); json = json.cast<String, dynamic>();
for (final entry in json.entries) { for (final entry in json.entries) {
map[entry.key] = DeleteUserDto.listFromJson(entry.value, growable: growable,); map[entry.key] = UserAdminDeleteDto.listFromJson(entry.value, growable: growable,);
} }
} }
return map; return map;

View File

@@ -0,0 +1,243 @@
//
// AUTO-GENERATED FILE, DO NOT MODIFY!
//
// @dart=2.18
// ignore_for_file: unused_element, unused_import
// ignore_for_file: always_put_required_named_parameters_first
// ignore_for_file: constant_identifier_names
// ignore_for_file: lines_longer_than_80_chars
part of openapi.api;
class UserAdminResponseDto {
/// Returns a new [UserAdminResponseDto] instance.
UserAdminResponseDto({
required this.avatarColor,
required this.createdAt,
required this.deletedAt,
required this.email,
required this.id,
required this.isAdmin,
this.memoriesEnabled,
required this.name,
required this.oauthId,
required this.profileImagePath,
required this.quotaSizeInBytes,
required this.quotaUsageInBytes,
required this.shouldChangePassword,
required this.status,
required this.storageLabel,
required this.updatedAt,
});
UserAvatarColor avatarColor;
DateTime createdAt;
DateTime? deletedAt;
String email;
String id;
bool isAdmin;
///
/// Please note: This property should have been non-nullable! Since the specification file
/// does not include a default value (using the "default:" property), however, the generated
/// source code must fall back to having a nullable type.
/// Consider adding a "default:" property in the specification file to hide this note.
///
bool? memoriesEnabled;
String name;
String oauthId;
String profileImagePath;
int? quotaSizeInBytes;
int? quotaUsageInBytes;
bool shouldChangePassword;
UserStatus status;
String? storageLabel;
DateTime updatedAt;
@override
bool operator ==(Object other) => identical(this, other) || other is UserAdminResponseDto &&
other.avatarColor == avatarColor &&
other.createdAt == createdAt &&
other.deletedAt == deletedAt &&
other.email == email &&
other.id == id &&
other.isAdmin == isAdmin &&
other.memoriesEnabled == memoriesEnabled &&
other.name == name &&
other.oauthId == oauthId &&
other.profileImagePath == profileImagePath &&
other.quotaSizeInBytes == quotaSizeInBytes &&
other.quotaUsageInBytes == quotaUsageInBytes &&
other.shouldChangePassword == shouldChangePassword &&
other.status == status &&
other.storageLabel == storageLabel &&
other.updatedAt == updatedAt;
@override
int get hashCode =>
// ignore: unnecessary_parenthesis
(avatarColor.hashCode) +
(createdAt.hashCode) +
(deletedAt == null ? 0 : deletedAt!.hashCode) +
(email.hashCode) +
(id.hashCode) +
(isAdmin.hashCode) +
(memoriesEnabled == null ? 0 : memoriesEnabled!.hashCode) +
(name.hashCode) +
(oauthId.hashCode) +
(profileImagePath.hashCode) +
(quotaSizeInBytes == null ? 0 : quotaSizeInBytes!.hashCode) +
(quotaUsageInBytes == null ? 0 : quotaUsageInBytes!.hashCode) +
(shouldChangePassword.hashCode) +
(status.hashCode) +
(storageLabel == null ? 0 : storageLabel!.hashCode) +
(updatedAt.hashCode);
@override
String toString() => 'UserAdminResponseDto[avatarColor=$avatarColor, createdAt=$createdAt, deletedAt=$deletedAt, email=$email, id=$id, isAdmin=$isAdmin, memoriesEnabled=$memoriesEnabled, name=$name, oauthId=$oauthId, profileImagePath=$profileImagePath, quotaSizeInBytes=$quotaSizeInBytes, quotaUsageInBytes=$quotaUsageInBytes, shouldChangePassword=$shouldChangePassword, status=$status, storageLabel=$storageLabel, updatedAt=$updatedAt]';
Map<String, dynamic> toJson() {
final json = <String, dynamic>{};
json[r'avatarColor'] = this.avatarColor;
json[r'createdAt'] = this.createdAt.toUtc().toIso8601String();
if (this.deletedAt != null) {
json[r'deletedAt'] = this.deletedAt!.toUtc().toIso8601String();
} else {
// json[r'deletedAt'] = null;
}
json[r'email'] = this.email;
json[r'id'] = this.id;
json[r'isAdmin'] = this.isAdmin;
if (this.memoriesEnabled != null) {
json[r'memoriesEnabled'] = this.memoriesEnabled;
} else {
// json[r'memoriesEnabled'] = null;
}
json[r'name'] = this.name;
json[r'oauthId'] = this.oauthId;
json[r'profileImagePath'] = this.profileImagePath;
if (this.quotaSizeInBytes != null) {
json[r'quotaSizeInBytes'] = this.quotaSizeInBytes;
} else {
// json[r'quotaSizeInBytes'] = null;
}
if (this.quotaUsageInBytes != null) {
json[r'quotaUsageInBytes'] = this.quotaUsageInBytes;
} else {
// json[r'quotaUsageInBytes'] = null;
}
json[r'shouldChangePassword'] = this.shouldChangePassword;
json[r'status'] = this.status;
if (this.storageLabel != null) {
json[r'storageLabel'] = this.storageLabel;
} else {
// json[r'storageLabel'] = null;
}
json[r'updatedAt'] = this.updatedAt.toUtc().toIso8601String();
return json;
}
/// Returns a new [UserAdminResponseDto] instance and imports its values from
/// [value] if it's a [Map], null otherwise.
// ignore: prefer_constructors_over_static_methods
static UserAdminResponseDto? fromJson(dynamic value) {
if (value is Map) {
final json = value.cast<String, dynamic>();
return UserAdminResponseDto(
avatarColor: UserAvatarColor.fromJson(json[r'avatarColor'])!,
createdAt: mapDateTime(json, r'createdAt', r'')!,
deletedAt: mapDateTime(json, r'deletedAt', r''),
email: mapValueOfType<String>(json, r'email')!,
id: mapValueOfType<String>(json, r'id')!,
isAdmin: mapValueOfType<bool>(json, r'isAdmin')!,
memoriesEnabled: mapValueOfType<bool>(json, r'memoriesEnabled'),
name: mapValueOfType<String>(json, r'name')!,
oauthId: mapValueOfType<String>(json, r'oauthId')!,
profileImagePath: mapValueOfType<String>(json, r'profileImagePath')!,
quotaSizeInBytes: mapValueOfType<int>(json, r'quotaSizeInBytes'),
quotaUsageInBytes: mapValueOfType<int>(json, r'quotaUsageInBytes'),
shouldChangePassword: mapValueOfType<bool>(json, r'shouldChangePassword')!,
status: UserStatus.fromJson(json[r'status'])!,
storageLabel: mapValueOfType<String>(json, r'storageLabel'),
updatedAt: mapDateTime(json, r'updatedAt', r'')!,
);
}
return null;
}
static List<UserAdminResponseDto> listFromJson(dynamic json, {bool growable = false,}) {
final result = <UserAdminResponseDto>[];
if (json is List && json.isNotEmpty) {
for (final row in json) {
final value = UserAdminResponseDto.fromJson(row);
if (value != null) {
result.add(value);
}
}
}
return result.toList(growable: growable);
}
static Map<String, UserAdminResponseDto> mapFromJson(dynamic json) {
final map = <String, UserAdminResponseDto>{};
if (json is Map && json.isNotEmpty) {
json = json.cast<String, dynamic>(); // ignore: parameter_assignments
for (final entry in json.entries) {
final value = UserAdminResponseDto.fromJson(entry.value);
if (value != null) {
map[entry.key] = value;
}
}
}
return map;
}
// maps a json object with a list of UserAdminResponseDto-objects as value to a dart map
static Map<String, List<UserAdminResponseDto>> mapListFromJson(dynamic json, {bool growable = false,}) {
final map = <String, List<UserAdminResponseDto>>{};
if (json is Map && json.isNotEmpty) {
// ignore: parameter_assignments
json = json.cast<String, dynamic>();
for (final entry in json.entries) {
map[entry.key] = UserAdminResponseDto.listFromJson(entry.value, growable: growable,);
}
}
return map;
}
/// The list of required keys that must be present in a JSON.
static const requiredKeys = <String>{
'avatarColor',
'createdAt',
'deletedAt',
'email',
'id',
'isAdmin',
'name',
'oauthId',
'profileImagePath',
'quotaSizeInBytes',
'quotaUsageInBytes',
'shouldChangePassword',
'status',
'storageLabel',
'updatedAt',
};
}

View File

@@ -10,13 +10,11 @@
part of openapi.api; part of openapi.api;
class UpdateUserDto { class UserAdminUpdateDto {
/// Returns a new [UpdateUserDto] instance. /// Returns a new [UserAdminUpdateDto] instance.
UpdateUserDto({ UserAdminUpdateDto({
this.avatarColor, this.avatarColor,
this.email, this.email,
required this.id,
this.isAdmin,
this.memoriesEnabled, this.memoriesEnabled,
this.name, this.name,
this.password, this.password,
@@ -41,16 +39,6 @@ class UpdateUserDto {
/// ///
String? email; String? email;
String id;
///
/// Please note: This property should have been non-nullable! Since the specification file
/// does not include a default value (using the "default:" property), however, the generated
/// source code must fall back to having a nullable type.
/// Consider adding a "default:" property in the specification file to hide this note.
///
bool? isAdmin;
/// ///
/// Please note: This property should have been non-nullable! Since the specification file /// Please note: This property should have been non-nullable! Since the specification file
/// does not include a default value (using the "default:" property), however, the generated /// does not include a default value (using the "default:" property), however, the generated
@@ -86,20 +74,12 @@ class UpdateUserDto {
/// ///
bool? shouldChangePassword; bool? shouldChangePassword;
///
/// Please note: This property should have been non-nullable! Since the specification file
/// does not include a default value (using the "default:" property), however, the generated
/// source code must fall back to having a nullable type.
/// Consider adding a "default:" property in the specification file to hide this note.
///
String? storageLabel; String? storageLabel;
@override @override
bool operator ==(Object other) => identical(this, other) || other is UpdateUserDto && bool operator ==(Object other) => identical(this, other) || other is UserAdminUpdateDto &&
other.avatarColor == avatarColor && other.avatarColor == avatarColor &&
other.email == email && other.email == email &&
other.id == id &&
other.isAdmin == isAdmin &&
other.memoriesEnabled == memoriesEnabled && other.memoriesEnabled == memoriesEnabled &&
other.name == name && other.name == name &&
other.password == password && other.password == password &&
@@ -112,8 +92,6 @@ class UpdateUserDto {
// ignore: unnecessary_parenthesis // ignore: unnecessary_parenthesis
(avatarColor == null ? 0 : avatarColor!.hashCode) + (avatarColor == null ? 0 : avatarColor!.hashCode) +
(email == null ? 0 : email!.hashCode) + (email == null ? 0 : email!.hashCode) +
(id.hashCode) +
(isAdmin == null ? 0 : isAdmin!.hashCode) +
(memoriesEnabled == null ? 0 : memoriesEnabled!.hashCode) + (memoriesEnabled == null ? 0 : memoriesEnabled!.hashCode) +
(name == null ? 0 : name!.hashCode) + (name == null ? 0 : name!.hashCode) +
(password == null ? 0 : password!.hashCode) + (password == null ? 0 : password!.hashCode) +
@@ -122,7 +100,7 @@ class UpdateUserDto {
(storageLabel == null ? 0 : storageLabel!.hashCode); (storageLabel == null ? 0 : storageLabel!.hashCode);
@override @override
String toString() => 'UpdateUserDto[avatarColor=$avatarColor, email=$email, id=$id, isAdmin=$isAdmin, memoriesEnabled=$memoriesEnabled, name=$name, password=$password, quotaSizeInBytes=$quotaSizeInBytes, shouldChangePassword=$shouldChangePassword, storageLabel=$storageLabel]'; String toString() => 'UserAdminUpdateDto[avatarColor=$avatarColor, email=$email, memoriesEnabled=$memoriesEnabled, name=$name, password=$password, quotaSizeInBytes=$quotaSizeInBytes, shouldChangePassword=$shouldChangePassword, storageLabel=$storageLabel]';
Map<String, dynamic> toJson() { Map<String, dynamic> toJson() {
final json = <String, dynamic>{}; final json = <String, dynamic>{};
@@ -135,12 +113,6 @@ class UpdateUserDto {
json[r'email'] = this.email; json[r'email'] = this.email;
} else { } else {
// json[r'email'] = null; // json[r'email'] = null;
}
json[r'id'] = this.id;
if (this.isAdmin != null) {
json[r'isAdmin'] = this.isAdmin;
} else {
// json[r'isAdmin'] = null;
} }
if (this.memoriesEnabled != null) { if (this.memoriesEnabled != null) {
json[r'memoriesEnabled'] = this.memoriesEnabled; json[r'memoriesEnabled'] = this.memoriesEnabled;
@@ -175,18 +147,16 @@ class UpdateUserDto {
return json; return json;
} }
/// Returns a new [UpdateUserDto] instance and imports its values from /// Returns a new [UserAdminUpdateDto] instance and imports its values from
/// [value] if it's a [Map], null otherwise. /// [value] if it's a [Map], null otherwise.
// ignore: prefer_constructors_over_static_methods // ignore: prefer_constructors_over_static_methods
static UpdateUserDto? fromJson(dynamic value) { static UserAdminUpdateDto? fromJson(dynamic value) {
if (value is Map) { if (value is Map) {
final json = value.cast<String, dynamic>(); final json = value.cast<String, dynamic>();
return UpdateUserDto( return UserAdminUpdateDto(
avatarColor: UserAvatarColor.fromJson(json[r'avatarColor']), avatarColor: UserAvatarColor.fromJson(json[r'avatarColor']),
email: mapValueOfType<String>(json, r'email'), email: mapValueOfType<String>(json, r'email'),
id: mapValueOfType<String>(json, r'id')!,
isAdmin: mapValueOfType<bool>(json, r'isAdmin'),
memoriesEnabled: mapValueOfType<bool>(json, r'memoriesEnabled'), memoriesEnabled: mapValueOfType<bool>(json, r'memoriesEnabled'),
name: mapValueOfType<String>(json, r'name'), name: mapValueOfType<String>(json, r'name'),
password: mapValueOfType<String>(json, r'password'), password: mapValueOfType<String>(json, r'password'),
@@ -198,11 +168,11 @@ class UpdateUserDto {
return null; return null;
} }
static List<UpdateUserDto> listFromJson(dynamic json, {bool growable = false,}) { static List<UserAdminUpdateDto> listFromJson(dynamic json, {bool growable = false,}) {
final result = <UpdateUserDto>[]; final result = <UserAdminUpdateDto>[];
if (json is List && json.isNotEmpty) { if (json is List && json.isNotEmpty) {
for (final row in json) { for (final row in json) {
final value = UpdateUserDto.fromJson(row); final value = UserAdminUpdateDto.fromJson(row);
if (value != null) { if (value != null) {
result.add(value); result.add(value);
} }
@@ -211,12 +181,12 @@ class UpdateUserDto {
return result.toList(growable: growable); return result.toList(growable: growable);
} }
static Map<String, UpdateUserDto> mapFromJson(dynamic json) { static Map<String, UserAdminUpdateDto> mapFromJson(dynamic json) {
final map = <String, UpdateUserDto>{}; final map = <String, UserAdminUpdateDto>{};
if (json is Map && json.isNotEmpty) { if (json is Map && json.isNotEmpty) {
json = json.cast<String, dynamic>(); // ignore: parameter_assignments json = json.cast<String, dynamic>(); // ignore: parameter_assignments
for (final entry in json.entries) { for (final entry in json.entries) {
final value = UpdateUserDto.fromJson(entry.value); final value = UserAdminUpdateDto.fromJson(entry.value);
if (value != null) { if (value != null) {
map[entry.key] = value; map[entry.key] = value;
} }
@@ -225,14 +195,14 @@ class UpdateUserDto {
return map; return map;
} }
// maps a json object with a list of UpdateUserDto-objects as value to a dart map // maps a json object with a list of UserAdminUpdateDto-objects as value to a dart map
static Map<String, List<UpdateUserDto>> mapListFromJson(dynamic json, {bool growable = false,}) { static Map<String, List<UserAdminUpdateDto>> mapListFromJson(dynamic json, {bool growable = false,}) {
final map = <String, List<UpdateUserDto>>{}; final map = <String, List<UserAdminUpdateDto>>{};
if (json is Map && json.isNotEmpty) { if (json is Map && json.isNotEmpty) {
// ignore: parameter_assignments // ignore: parameter_assignments
json = json.cast<String, dynamic>(); json = json.cast<String, dynamic>();
for (final entry in json.entries) { for (final entry in json.entries) {
map[entry.key] = UpdateUserDto.listFromJson(entry.value, growable: growable,); map[entry.key] = UserAdminUpdateDto.listFromJson(entry.value, growable: growable,);
} }
} }
return map; return map;
@@ -240,7 +210,6 @@ class UpdateUserDto {
/// The list of required keys that must be present in a JSON. /// The list of required keys that must be present in a JSON.
static const requiredKeys = <String>{ static const requiredKeys = <String>{
'id',
}; };
} }

View File

@@ -1,130 +0,0 @@
//
// AUTO-GENERATED FILE, DO NOT MODIFY!
//
// @dart=2.18
// ignore_for_file: unused_element, unused_import
// ignore_for_file: always_put_required_named_parameters_first
// ignore_for_file: constant_identifier_names
// ignore_for_file: lines_longer_than_80_chars
part of openapi.api;
class UserDto {
/// Returns a new [UserDto] instance.
UserDto({
required this.avatarColor,
required this.email,
required this.id,
required this.name,
required this.profileImagePath,
});
UserAvatarColor avatarColor;
String email;
String id;
String name;
String profileImagePath;
@override
bool operator ==(Object other) => identical(this, other) || other is UserDto &&
other.avatarColor == avatarColor &&
other.email == email &&
other.id == id &&
other.name == name &&
other.profileImagePath == profileImagePath;
@override
int get hashCode =>
// ignore: unnecessary_parenthesis
(avatarColor.hashCode) +
(email.hashCode) +
(id.hashCode) +
(name.hashCode) +
(profileImagePath.hashCode);
@override
String toString() => 'UserDto[avatarColor=$avatarColor, email=$email, id=$id, name=$name, profileImagePath=$profileImagePath]';
Map<String, dynamic> toJson() {
final json = <String, dynamic>{};
json[r'avatarColor'] = this.avatarColor;
json[r'email'] = this.email;
json[r'id'] = this.id;
json[r'name'] = this.name;
json[r'profileImagePath'] = this.profileImagePath;
return json;
}
/// Returns a new [UserDto] instance and imports its values from
/// [value] if it's a [Map], null otherwise.
// ignore: prefer_constructors_over_static_methods
static UserDto? fromJson(dynamic value) {
if (value is Map) {
final json = value.cast<String, dynamic>();
return UserDto(
avatarColor: UserAvatarColor.fromJson(json[r'avatarColor'])!,
email: mapValueOfType<String>(json, r'email')!,
id: mapValueOfType<String>(json, r'id')!,
name: mapValueOfType<String>(json, r'name')!,
profileImagePath: mapValueOfType<String>(json, r'profileImagePath')!,
);
}
return null;
}
static List<UserDto> listFromJson(dynamic json, {bool growable = false,}) {
final result = <UserDto>[];
if (json is List && json.isNotEmpty) {
for (final row in json) {
final value = UserDto.fromJson(row);
if (value != null) {
result.add(value);
}
}
}
return result.toList(growable: growable);
}
static Map<String, UserDto> mapFromJson(dynamic json) {
final map = <String, UserDto>{};
if (json is Map && json.isNotEmpty) {
json = json.cast<String, dynamic>(); // ignore: parameter_assignments
for (final entry in json.entries) {
final value = UserDto.fromJson(entry.value);
if (value != null) {
map[entry.key] = value;
}
}
}
return map;
}
// maps a json object with a list of UserDto-objects as value to a dart map
static Map<String, List<UserDto>> mapListFromJson(dynamic json, {bool growable = false,}) {
final map = <String, List<UserDto>>{};
if (json is Map && json.isNotEmpty) {
// ignore: parameter_assignments
json = json.cast<String, dynamic>();
for (final entry in json.entries) {
map[entry.key] = UserDto.listFromJson(entry.value, growable: growable,);
}
}
return map;
}
/// The list of required keys that must be present in a JSON.
static const requiredKeys = <String>{
'avatarColor',
'email',
'id',
'name',
'profileImagePath',
};
}

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