Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
51 changes: 29 additions & 22 deletions e2e/src/specs/server/api/api-key.e2e-spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,14 +34,14 @@ describe('/api-keys', () => {
permissions: [Permission.ApiKeyRead],
});
expect(body).toEqual({
id: expect.any(String),
name: 'API Key',
permissions: [Permission.ApiKeyRead],
createdAt: expect.any(String),
updatedAt: expect.any(String),
secret: expect.any(String),
apiKey: {
id: expect.any(String),
name: 'API Key',
permissions: [Permission.ApiKeyRead],
createdAt: expect.any(String),
updatedAt: expect.any(String),
},
// TODO: remove in v4
apiKey: expect.any(Object),
});
expect(status).toBe(201);
});
Expand Down Expand Up @@ -72,14 +72,14 @@ describe('/api-keys', () => {
.send({ name: 'API Key', permissions: [Permission.All] })
.set('Authorization', `Bearer ${admin.accessToken}`);
expect(body).toEqual({
apiKey: {
id: expect.any(String),
name: 'API Key',
permissions: [Permission.All],
createdAt: expect.any(String),
updatedAt: expect.any(String),
},
id: expect.any(String),
name: 'API Key',
permissions: [Permission.All],
createdAt: expect.any(String),
updatedAt: expect.any(String),
secret: expect.any(String),
// TODO: remove in v4
apiKey: expect.any(Object),
});
expect(status).toEqual(201);
});
Expand All @@ -93,23 +93,30 @@ describe('/api-keys', () => {
});

it('should return a list of api keys', async () => {
const [{ apiKey: apiKey1 }, { apiKey: apiKey2 }, { apiKey: apiKey3 }] = await Promise.all([
const [apiKey1, apiKey2, apiKey3] = await Promise.all([
create(admin.accessToken, [Permission.All]),
create(admin.accessToken, [Permission.All]),
create(admin.accessToken, [Permission.All]),
]);

const { status, body } = await request(app).get('/api-keys').set('Authorization', `Bearer ${admin.accessToken}`);
expect(body).toHaveLength(3);
expect(body).toEqual(expect.arrayContaining([apiKey1, apiKey2, apiKey3]));
expect(body).toEqual(
expect.arrayContaining([
expect.objectContaining({ id: apiKey1.id }),
expect.objectContaining({ id: apiKey2.id }),
expect.objectContaining({ id: apiKey3.id }),
]),
);
expect(status).toEqual(200);
});
});

describe('GET /api-keys/:id', () => {
it('should get api key details', async () => {
const { apiKey } = await create(user.accessToken, [Permission.All]);
const { id } = await create(user.accessToken, [Permission.All]);
const { status, body } = await request(app)
.get(`/api-keys/${apiKey.id}`)
.get(`/api-keys/${id}`)
.set('Authorization', `Bearer ${user.accessToken}`);
expect(status).toBe(200);
expect(body).toEqual({
Expand All @@ -124,9 +131,9 @@ describe('/api-keys', () => {

describe('PUT /api-keys/:id', () => {
it('should update api key details', async () => {
const { apiKey } = await create(user.accessToken, [Permission.All]);
const { id } = await create(user.accessToken, [Permission.All]);
const { status, body } = await request(app)
.put(`/api-keys/${apiKey.id}`)
.put(`/api-keys/${id}`)
.send({
name: 'new name',
permissions: [Permission.ActivityCreate, Permission.ActivityRead, Permission.ActivityUpdate],
Expand All @@ -145,9 +152,9 @@ describe('/api-keys', () => {

describe('DELETE /api-keys/:id', () => {
it('should delete an api key', async () => {
const { apiKey } = await create(user.accessToken, [Permission.All]);
const { id } = await create(user.accessToken, [Permission.All]);
const { status } = await request(app)
.delete(`/api-keys/${apiKey.id}`)
.delete(`/api-keys/${id}`)
.set('Authorization', `Bearer ${user.accessToken}`);
expect(status).toBe(204);
});
Expand Down
2 changes: 1 addition & 1 deletion e2e/src/specs/server/api/asset.e2e-spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,7 @@ describe('/asset', () => {
});
await utils.createFace({
assetId: user1Assets[0].id,
personId: person1.id,
personGroupId: person1.id,
});
};
beforeAll(setupTests, 30_000);
Expand Down
2 changes: 1 addition & 1 deletion e2e/src/specs/server/api/jobs.e2e-spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ describe('/jobs', () => {
config.machineLearning.enabled = false;
config.metadata.faces.import = false;
config.machineLearning.clip.enabled = false;
await updateConfig({ systemConfigDto: config }, { headers: asBearerAuth(admin.accessToken) });
await updateConfig({ adminConfigDto: config }, { headers: asBearerAuth(admin.accessToken) });
});

it('should queue metadata extraction for missing assets', async () => {
Expand Down
6 changes: 3 additions & 3 deletions e2e/src/specs/server/api/oauth.e2e-spec.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { OAuthClient, OAuthUser, generateLogoutToken } from '@immich/e2e-auth-server';
import {
AdminConfigOAuthDto,
LoginResponseDto,
SystemConfigOAuthDto,
getConfigDefaults,
getMyUser,
getSessions,
Expand Down Expand Up @@ -70,7 +70,7 @@ const loginWithOAuth = async (sub: OAuthUser | string, redirectUri?: string) =>
return { url: redirectUrl, state, codeVerifier };
};

const setupOAuth = async (token: string, dto: Partial<SystemConfigOAuthDto>) => {
const setupOAuth = async (token: string, dto: Partial<AdminConfigOAuthDto>) => {
const options = { headers: asBearerAuth(token) };
const defaults = await getConfigDefaults(options);
const merged = {
Expand All @@ -80,7 +80,7 @@ const setupOAuth = async (token: string, dto: Partial<SystemConfigOAuthDto>) =>
allowInsecureRequests: true,
...dto,
};
await updateConfig({ systemConfigDto: { ...defaults, oauth: merged } }, options);
await updateConfig({ adminConfigDto: { ...defaults, oauth: merged } }, options);
};

describe(`/oauth`, () => {
Expand Down
42 changes: 21 additions & 21 deletions e2e/src/specs/server/api/person.e2e-spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -82,32 +82,32 @@ describe('/people', () => {
const asset4 = await utils.createAsset(admin.accessToken);

await Promise.all([
utils.createFace({ assetId: asset1.id, personId: visiblePerson.id }),
utils.createFace({ assetId: asset1.id, personId: hiddenPerson.id }),
utils.createFace({ assetId: asset1.id, personId: multipleAssetsPerson.id }),
utils.createFace({ assetId: asset1.id, personId: multipleAssetsPerson.id }),
utils.createFace({ assetId: asset2.id, personId: multipleAssetsPerson.id }),
utils.createFace({ assetId: asset3.id, personId: multipleAssetsPerson.id }), // 4 assets
utils.createFace({ assetId: asset1.id, personGroupId: visiblePerson.id }),
utils.createFace({ assetId: asset1.id, personGroupId: hiddenPerson.id }),
utils.createFace({ assetId: asset1.id, personGroupId: multipleAssetsPerson.id }),
utils.createFace({ assetId: asset1.id, personGroupId: multipleAssetsPerson.id }),
utils.createFace({ assetId: asset2.id, personGroupId: multipleAssetsPerson.id }),
utils.createFace({ assetId: asset3.id, personGroupId: multipleAssetsPerson.id }), // 4 assets
// Named persons
utils.createFace({ assetId: asset1.id, personId: nameCharliePerson.id }), // 1 asset
utils.createFace({ assetId: asset1.id, personId: nameBobPerson.id }),
utils.createFace({ assetId: asset2.id, personId: nameBobPerson.id }), // 2 assets
utils.createFace({ assetId: asset1.id, personId: nameAlicePerson.id }), // 1 asset
utils.createFace({ assetId: asset1.id, personGroupId: nameCharliePerson.id }), // 1 asset
utils.createFace({ assetId: asset1.id, personGroupId: nameBobPerson.id }),
utils.createFace({ assetId: asset2.id, personGroupId: nameBobPerson.id }), // 2 assets
utils.createFace({ assetId: asset1.id, personGroupId: nameAlicePerson.id }), // 1 asset
// Null-named person 4 assets
utils.createFace({ assetId: asset1.id, personId: nameNullPerson4Assets.id }),
utils.createFace({ assetId: asset2.id, personId: nameNullPerson4Assets.id }),
utils.createFace({ assetId: asset3.id, personId: nameNullPerson4Assets.id }),
utils.createFace({ assetId: asset4.id, personId: nameNullPerson4Assets.id }), // 4 assets
utils.createFace({ assetId: asset1.id, personGroupId: nameNullPerson4Assets.id }),
utils.createFace({ assetId: asset2.id, personGroupId: nameNullPerson4Assets.id }),
utils.createFace({ assetId: asset3.id, personGroupId: nameNullPerson4Assets.id }),
utils.createFace({ assetId: asset4.id, personGroupId: nameNullPerson4Assets.id }), // 4 assets
// Null-named person 3 assets
utils.createFace({ assetId: asset1.id, personId: nameNullPerson3Assets.id }),
utils.createFace({ assetId: asset2.id, personId: nameNullPerson3Assets.id }),
utils.createFace({ assetId: asset3.id, personId: nameNullPerson3Assets.id }), // 3 assets
utils.createFace({ assetId: asset1.id, personGroupId: nameNullPerson3Assets.id }),
utils.createFace({ assetId: asset2.id, personGroupId: nameNullPerson3Assets.id }),
utils.createFace({ assetId: asset3.id, personGroupId: nameNullPerson3Assets.id }), // 3 assets
// Null-named person 1 asset
utils.createFace({ assetId: asset3.id, personId: nameNullPerson1Asset.id }),
utils.createFace({ assetId: asset3.id, personGroupId: nameNullPerson1Asset.id }),
// Favourite People
utils.createFace({ assetId: asset1.id, personId: nameFreddyPersonFavourite.id }),
utils.createFace({ assetId: asset2.id, personId: nameFreddyPersonFavourite.id }),
utils.createFace({ assetId: asset1.id, personId: nameBillPersonFavourite.id }),
utils.createFace({ assetId: asset1.id, personGroupId: nameFreddyPersonFavourite.id }),
utils.createFace({ assetId: asset2.id, personGroupId: nameFreddyPersonFavourite.id }),
utils.createFace({ assetId: asset1.id, personGroupId: nameBillPersonFavourite.id }),
]);
});

Expand Down
8 changes: 4 additions & 4 deletions e2e/src/specs/server/cli/login.e2e-spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,8 @@ describe(`immich login`, () => {

it('should login and save auth.yml with 600', async () => {
const admin = await utils.adminSetup();
const key = await utils.createApiKey(admin.accessToken, [Permission.All]);
const { stdout, stderr, exitCode } = await immichCli(['login', app, key.secret]);
const apiKey = await utils.createApiKey(admin.accessToken, [Permission.All]);
const { stdout, stderr, exitCode } = await immichCli(['login', app, apiKey.secret]);
expect(stdout.split('\n')).toEqual([
'Logging in to http://127.0.0.1:2285/api',
'Logged in as admin@immich.cloud',
Expand All @@ -47,8 +47,8 @@ describe(`immich login`, () => {

it('should login without /api in the url', async () => {
const admin = await utils.adminSetup();
const key = await utils.createApiKey(admin.accessToken, [Permission.All]);
const { stdout, stderr, exitCode } = await immichCli(['login', app.replaceAll('/api', ''), key.secret]);
const apiKey = await utils.createApiKey(admin.accessToken, [Permission.All]);
const { stdout, stderr, exitCode } = await immichCli(['login', app.replaceAll('/api', ''), apiKey.secret]);
expect(stdout.split('\n')).toEqual([
'Logging in to http://127.0.0.1:2285',
'Discovered API at http://127.0.0.1:2285/api',
Expand Down
18 changes: 11 additions & 7 deletions e2e/src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,8 @@ export const utils = {
'library',
'shared_link',
'person',
'person_group',
'cluster_group',
'album',
'asset',
'asset_face',
Expand Down Expand Up @@ -434,20 +436,22 @@ export const utils = {
return person;
},

createFace: async ({ assetId, personId }: { assetId: string; personId: string }) => {
createFace: async ({ assetId, personGroupId }: { assetId: string; personGroupId: string }) => {
if (!client) {
return;
}

await client.query('INSERT INTO asset_face ("assetId", "personId") VALUES ($1, $2)', [assetId, personId]);
await client.query('INSERT INTO asset_face ("assetId", "personGroupId") VALUES ($1, $2)', [assetId, personGroupId]);
},

setPersonThumbnail: async (personId: string) => {
if (!client) {
return;
}

await client.query(`UPDATE "person" set "thumbnailPath" = '/my/awesome/thumbnail.jpg' where "id" = $1`, [personId]);
await client.query(`UPDATE "person" set "thumbnailPath" = '/my/awesome/thumbnail.jpg' where "personGroupId" = $1`, [
personId,
]);
},

createSharedLink: (accessToken: string, dto: SharedLinkCreateDto) =>
Expand Down Expand Up @@ -647,7 +651,7 @@ export const utils = {

resetAdminConfig: async (accessToken: string) => {
const defaultConfig = await getConfigDefaults({ headers: asBearerAuth(accessToken) });
await updateConfig({ systemConfigDto: defaultConfig }, { headers: asBearerAuth(accessToken) });
await updateConfig({ adminConfigDto: defaultConfig }, { headers: asBearerAuth(accessToken) });
},

isQueueEmpty: async (accessToken: string, queue: keyof QueuesResponseLegacyDto) => {
Expand Down Expand Up @@ -675,9 +679,9 @@ export const utils = {
},

cliLogin: async (accessToken: string) => {
const key = await utils.createApiKey(accessToken, [Permission.All]);
await immichCli(['login', app, key.secret]);
return key.secret;
const { secret } = await utils.createApiKey(accessToken, [Permission.All]);
await immichCli(['login', app, secret]);
return secret;
},

scan: async (accessToken: string, id: string) => {
Expand Down
14 changes: 14 additions & 0 deletions i18n/en.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
{
"about": "About",
"accept": "Accept",
"account": "Account",
"account_settings": "Account Settings",
"acknowledge": "Acknowledge",
Expand Down Expand Up @@ -36,6 +37,7 @@
"add_to_bottom_bar": "Add to",
"add_upload_to_stack": "Add upload to stack",
"add_url": "Add URL",
"add_user": "Add user",
"added_to_archive": "Added to archive",
"added_to_favorites": "Added to favorites",
"added_to_favorites_count": "Added {count, number} to favorites",
Expand Down Expand Up @@ -734,6 +736,9 @@
"client_cert_subtitle": "Supports PKCS12 (.p12, .pfx) format only. Certificate import/removal is available only before login",
"client_cert_title": "SSL client certificate [EXPERIMENTAL]",
"close": "Close",
"cluster_group": "Cluster group",
"cluster_group_description": "People are recognized across the photos of everyone in the group",
"cluster_group_invite_description": "You have been invited to join this cluster group.",
"collapse": "Collapse",
"collapse_all": "Collapse all",
"color": "Color",
Expand Down Expand Up @@ -831,6 +836,7 @@
"date_time_original": "Date/Time Original",
"day": "Day",
"days": "Days",
"decline": "Decline",
"deduplicate_all": "Deduplicate All",
"default_quality_subtitle": "Quality used when tapping share. Long press the share button to choose each time.",
"default_share_quality": "Default share quality",
Expand Down Expand Up @@ -1041,8 +1047,10 @@
"unable_to_get_comments_number": "Unable to get number of comments",
"unable_to_get_shared_link": "Failed to get shared link",
"unable_to_hide_person": "Unable to hide person",
"unable_to_leave_cluster_group": "Unable to leave cluster group",
"unable_to_link_motion_video": "Unable to link motion video",
"unable_to_link_oauth_account": "Unable to link OAuth account",
"unable_to_load_cluster_group": "Unable to load cluster group",
"unable_to_load_map": "Unable to load map",
"unable_to_load_map_description": "The map requires WebGL to work properly.",
"unable_to_log_out_all_devices": "Unable to log out all devices",
Expand Down Expand Up @@ -1263,6 +1271,8 @@
"latitude": "Latitude",
"leave": "Leave",
"leave_album": "Leave album",
"leave_group": "Leave group",
"leave_group_description": "People will no longer be recognized across the photos of everyone in the group. Are you sure you want to continue?",
"lens_model": "Lens model",
"less": "Less",
"let_others_respond": "Let others respond",
Expand Down Expand Up @@ -1369,6 +1379,7 @@
"manage_media_access_settings": "Open settings",
"manage_media_access_subtitle": "Allow the Immich app to manage and move media files.",
"manage_media_access_title": "Media Management Access",
"manage_sharing_with_other_users": "Manage sharing with other users",
"manage_sharing_with_partners": "Manage sharing with partners",
"manage_the_app_settings": "Manage the app settings",
"manage_your_account": "Manage your account",
Expand Down Expand Up @@ -1775,6 +1786,7 @@
"removed_tagged_assets": "Removed tag from {count, plural, one {# asset} other {# assets}}",
"rename": "Rename",
"repository": "Repository",
"request_received_description": "You have been invited to another group",
"require_password": "Require password",
"rescan": "Rescan",
"reset": "Reset",
Expand Down Expand Up @@ -2268,6 +2280,7 @@
"view_all_users": "View all users",
"view_asset_owners": "View asset owners",
"view_details": "View Details",
"view_group": "View group",
"view_in_timeline": "View in timeline",
"view_link": "View link",
"view_name": "View",
Expand Down Expand Up @@ -2318,6 +2331,7 @@
"year": "Year",
"years_ago": "{years, plural, one {# year} other {# years}} ago",
"yes": "Yes",
"you": "You",
"you_dont_have_any_shared_links": "You don't have any shared links",
"your_wifi_name": "Your Wi-Fi name",
"zero_to_clear_rating": "press 0 to clear asset rating",
Expand Down

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

Loading
Loading