Skip to content

feat(server): lighter buckets #17831

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 93 commits into from
May 19, 2025
Merged
Show file tree
Hide file tree
Changes from 87 commits
Commits
Show all changes
93 commits
Select commit Hold shift + click to select a range
5a8f9f3
feat(web): lighter timeline buckets
midzelis Apr 19, 2025
3b9490e
Merge remote-tracking branch 'origin/main' into lighter_buckets_web
midzelis Apr 19, 2025
c1e699e
GalleryViewer
midzelis Apr 20, 2025
9b7e9bc
weird ssr
midzelis Apr 20, 2025
f3fe043
Remove generics from AssetInteraction
midzelis Apr 20, 2025
9f6120a
ensure keys on getAssetInfo, alt-text
midzelis Apr 20, 2025
6cb7fff
empty - trigger ci
midzelis Apr 20, 2025
0795f8a
re-add alt-text
midzelis Apr 23, 2025
d76c50f
Merge remote-tracking branch 'origin/main' into lighter_buckets_web
midzelis Apr 23, 2025
a5eaadd
test fix
midzelis Apr 23, 2025
683a10f
Merge branch 'main' into lighter_buckets_web
midzelis Apr 23, 2025
89bfa69
update tests
midzelis Apr 23, 2025
77121a0
tests
midzelis Apr 24, 2025
50cfc46
missing import
midzelis Apr 24, 2025
bfefa36
feat(server): lighter buckets
midzelis Apr 24, 2025
6308ae7
fix: flappy e2e test
midzelis Apr 24, 2025
7f93458
lint
midzelis Apr 24, 2025
cd8806e
revert settings
midzelis Apr 28, 2025
236973e
unneeded cast
midzelis Apr 29, 2025
ffda736
Merge remote-tracking branch 'origin/main' into lighter_buckets_web
midzelis Apr 29, 2025
580a011
fix after merge
midzelis Apr 29, 2025
077703a
Merge branch 'lighter_buckets_web' into lighter_buckets_server
midzelis Apr 29, 2025
bc5d4b4
Adapt web client to consume new server response format
midzelis Apr 29, 2025
15d5460
test
midzelis Apr 29, 2025
c16348e
Merge remote-tracking branch 'origin/main' into lighter_buckets_web
midzelis May 2, 2025
1d3a546
missing import
midzelis May 2, 2025
9d527b3
Merge branch 'main' into lighter_buckets_web
midzelis May 2, 2025
0ed2a2f
Merge remote-tracking branch 'origin/lighter_buckets_web' into lighte…
midzelis May 2, 2025
8011605
lint
midzelis May 2, 2025
aea2c95
Use nulls, make-sql
midzelis May 3, 2025
6e8993c
Merge branch 'lighter_buckets_web' into lighter_buckets_server
midzelis May 3, 2025
bf0be6a
openapi battle
midzelis May 3, 2025
73cd236
date->string
midzelis May 3, 2025
f7fd213
tests
midzelis May 3, 2025
ee08fd0
tests
midzelis May 3, 2025
5520db1
lint/tests
midzelis May 3, 2025
5a3e32f
lint
midzelis May 3, 2025
07c03b8
test
midzelis May 3, 2025
97cc9e2
push aggregation to query
mertalev May 4, 2025
8837f5b
openapi
mertalev May 4, 2025
a3a2ced
stack as tuple
mertalev May 5, 2025
ef92454
openapi
mertalev May 5, 2025
1d885c1
update references to description
mertalev May 5, 2025
b20440e
update alt text tests
mertalev May 5, 2025
f7712c3
update sql
mertalev May 5, 2025
85359bf
update sql
mertalev May 5, 2025
3ace02b
update timeline tests
mertalev May 5, 2025
21bbf2f
linting, fix expected response
mertalev May 5, 2025
71cc045
string tuple
mertalev May 5, 2025
606d4b6
fix spec
mertalev May 5, 2025
4174575
fix
mertalev May 6, 2025
35d91aa
silly generator
mertalev May 6, 2025
c9728a1
rename patch
mertalev May 6, 2025
fa216b5
minimize sorting
mertalev May 7, 2025
3d673b2
Merge remote-tracking branch 'origin/main' into lighter_buckets_web
midzelis May 12, 2025
55bb1d9
review
midzelis May 12, 2025
f81b8bb
Merge remote-tracking branch 'origin/main' into lighter_buckets_web
midzelis May 13, 2025
9147f39
lint
midzelis May 13, 2025
50ad889
Merge remote-tracking branch 'origin/main' into lighter_buckets_server
midzelis May 13, 2025
fe9ce66
Merge remote-tracking branch 'origin/lighter_buckets_web' into lighte…
midzelis May 13, 2025
505a5c9
lint
midzelis May 13, 2025
0ec7fa8
sql
midzelis May 13, 2025
c3e2010
test
midzelis May 13, 2025
3e4ccc8
Merge remote-tracking branch 'origin/main' into lighter_buckets_web
midzelis May 15, 2025
eb468e0
avoid abbreviations
midzelis May 15, 2025
a502a84
review comment - type safety in test
midzelis May 15, 2025
2dd042f
merge conflicts
midzelis May 15, 2025
ce4346e
lint
midzelis May 16, 2025
0920ee9
lint/abbreviations
midzelis May 16, 2025
7ffca14
remove unncessary code
midzelis May 16, 2025
7c4b1de
review comments
midzelis May 16, 2025
30a3aec
Merge branch 'lighter_buckets_web' into lighter_buckets_server
midzelis May 16, 2025
4472020
sql
midzelis May 15, 2025
220124c
re-add package-lock
midzelis May 16, 2025
b1f19a0
use booleans, fix visibility in openapi spec, less cursed controller
mertalev May 16, 2025
cbe7f0f
update sql
mertalev May 16, 2025
e4568e1
no need to use sql template
mertalev May 16, 2025
b262164
array access actually doesn't seem to matter
mertalev May 16, 2025
dd8beaa
remove redundant code
mertalev May 16, 2025
3220f9a
re-add sql decorator
mertalev May 16, 2025
9f041a9
unused type
mertalev May 16, 2025
dc06925
remove null assertions
mertalev May 16, 2025
9cc3f52
Merge remote-tracking branch 'origin/main' into lighter_buckets_server
midzelis May 18, 2025
59b3bbf
bad merge
midzelis May 18, 2025
54106ba
Fix test
midzelis May 18, 2025
12f6e7e
Merge branch 'main' into lighter_buckets_server
alextran1502 May 19, 2025
45cea7b
shave
mertalev May 19, 2025
7e8539e
extra clean shave
mertalev May 19, 2025
82c1de5
use decorator for content type
mertalev May 19, 2025
d383bc8
redundant types
mertalev May 19, 2025
2b9a108
redundant comment
mertalev May 19, 2025
75e6d70
update comment
mertalev May 19, 2025
a580047
unnecessary res
mertalev May 19, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
78 changes: 46 additions & 32 deletions e2e/src/api/specs/timeline.e2e-spec.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { AssetMediaResponseDto, AssetVisibility, LoginResponseDto, SharedLinkType, TimeBucketSize } from '@immich/sdk';
import { AssetMediaResponseDto, AssetVisibility, LoginResponseDto, SharedLinkType } from '@immich/sdk';
import { DateTime } from 'luxon';
import { createUserDto } from 'src/fixtures';
import { errorDto } from 'src/responses';
Expand Down Expand Up @@ -52,16 +52,15 @@ describe('/timeline', () => {

describe('GET /timeline/buckets', () => {
it('should require authentication', async () => {
const { status, body } = await request(app).get('/timeline/buckets').query({ size: TimeBucketSize.Month });
const { status, body } = await request(app).get('/timeline/buckets');
expect(status).toBe(401);
expect(body).toEqual(errorDto.unauthorized);
});

it('should get time buckets by month', async () => {
const { status, body } = await request(app)
.get('/timeline/buckets')
.set('Authorization', `Bearer ${timeBucketUser.accessToken}`)
.query({ size: TimeBucketSize.Month });
.set('Authorization', `Bearer ${timeBucketUser.accessToken}`);

expect(status).toBe(200);
expect(body).toEqual(
Expand All @@ -78,41 +77,25 @@ describe('/timeline', () => {
assetIds: userAssets.map(({ id }) => id),
});

const { status, body } = await request(app)
.get('/timeline/buckets')
.query({ key: sharedLink.key, size: TimeBucketSize.Month });
const { status, body } = await request(app).get('/timeline/buckets').query({ key: sharedLink.key });

expect(status).toBe(400);
expect(body).toEqual(errorDto.noPermission);
});

it('should get time buckets by day', async () => {
const { status, body } = await request(app)
.get('/timeline/buckets')
.set('Authorization', `Bearer ${timeBucketUser.accessToken}`)
.query({ size: TimeBucketSize.Day });

expect(status).toBe(200);
expect(body).toEqual([
{ count: 2, timeBucket: '1970-02-11T00:00:00.000Z' },
{ count: 1, timeBucket: '1970-02-10T00:00:00.000Z' },
{ count: 1, timeBucket: '1970-01-01T00:00:00.000Z' },
]);
});

it('should return error if time bucket is requested with partners asset and archived', async () => {
const req1 = await request(app)
.get('/timeline/buckets')
.set('Authorization', `Bearer ${timeBucketUser.accessToken}`)
.query({ size: TimeBucketSize.Month, withPartners: true, visibility: AssetVisibility.Archive });
.query({ withPartners: true, visibility: AssetVisibility.Archive });

expect(req1.status).toBe(400);
expect(req1.body).toEqual(errorDto.badRequest());

const req2 = await request(app)
.get('/timeline/buckets')
.set('Authorization', `Bearer ${user.accessToken}`)
.query({ size: TimeBucketSize.Month, withPartners: true, visibility: undefined });
.query({ withPartners: true, visibility: undefined });

expect(req2.status).toBe(400);
expect(req2.body).toEqual(errorDto.badRequest());
Expand All @@ -122,15 +105,15 @@ describe('/timeline', () => {
const req1 = await request(app)
.get('/timeline/buckets')
.set('Authorization', `Bearer ${timeBucketUser.accessToken}`)
.query({ size: TimeBucketSize.Month, withPartners: true, isFavorite: true });
.query({ withPartners: true, isFavorite: true });

expect(req1.status).toBe(400);
expect(req1.body).toEqual(errorDto.badRequest());

const req2 = await request(app)
.get('/timeline/buckets')
.set('Authorization', `Bearer ${timeBucketUser.accessToken}`)
.query({ size: TimeBucketSize.Month, withPartners: true, isFavorite: false });
.query({ withPartners: true, isFavorite: false });

expect(req2.status).toBe(400);
expect(req2.body).toEqual(errorDto.badRequest());
Expand All @@ -140,7 +123,7 @@ describe('/timeline', () => {
const req = await request(app)
.get('/timeline/buckets')
.set('Authorization', `Bearer ${user.accessToken}`)
.query({ size: TimeBucketSize.Month, withPartners: true, isTrashed: true });
.query({ withPartners: true, isTrashed: true });

expect(req.status).toBe(400);
expect(req.body).toEqual(errorDto.badRequest());
Expand All @@ -150,7 +133,6 @@ describe('/timeline', () => {
describe('GET /timeline/bucket', () => {
it('should require authentication', async () => {
const { status, body } = await request(app).get('/timeline/bucket').query({
size: TimeBucketSize.Month,
timeBucket: '1900-01-01',
});

Expand All @@ -161,19 +143,35 @@ describe('/timeline', () => {
it('should handle 5 digit years', async () => {
const { status, body } = await request(app)
.get('/timeline/bucket')
.query({ size: TimeBucketSize.Month, timeBucket: '012345-01-01' })
.query({ timeBucket: '012345-01-01' })
.set('Authorization', `Bearer ${timeBucketUser.accessToken}`);

expect(status).toBe(200);
expect(body).toEqual([]);
expect(body).toEqual({
city: [],
country: [],
duration: [],
id: [],
visibility: [],
isFavorite: [],
isImage: [],
isTrashed: [],
livePhotoVideoId: [],
localDateTime: [],
ownerId: [],
projectionType: [],
ratio: [],
status: [],
thumbhash: [],
});
});

// TODO enable date string validation while still accepting 5 digit years
// it('should fail if time bucket is invalid', async () => {
// const { status, body } = await request(app)
// .get('/timeline/bucket')
// .set('Authorization', `Bearer ${user.accessToken}`)
// .query({ size: TimeBucketSize.Month, timeBucket: 'foo' });
// .query({ timeBucket: 'foo' });

// expect(status).toBe(400);
// expect(body).toEqual(errorDto.badRequest);
Expand All @@ -183,10 +181,26 @@ describe('/timeline', () => {
const { status, body } = await request(app)
.get('/timeline/bucket')
.set('Authorization', `Bearer ${timeBucketUser.accessToken}`)
.query({ size: TimeBucketSize.Month, timeBucket: '1970-02-10' });
.query({ timeBucket: '1970-02-10' });

expect(status).toBe(200);
expect(body).toEqual([]);
expect(body).toEqual({
city: [],
country: [],
duration: [],
id: [],
visibility: [],
isFavorite: [],
isImage: [],
isTrashed: [],
livePhotoVideoId: [],
localDateTime: [],
ownerId: [],
projectionType: [],
ratio: [],
status: [],
thumbhash: [],
});
});
});
});
4 changes: 2 additions & 2 deletions mobile/openapi/README.md

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions mobile/openapi/lib/api.dart

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

47 changes: 24 additions & 23 deletions mobile/openapi/lib/api/timeline_api.dart

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 4 additions & 4 deletions mobile/openapi/lib/api_client.dart

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 0 additions & 3 deletions mobile/openapi/lib/api_helper.dart

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading