chore: remove axios dependency from CLI (#6888)

This commit is contained in:
Ben McCann
2024-02-05 11:29:00 -08:00
committed by GitHub
parent c29976cd6f
commit 6ed33da2a4
218 changed files with 22789 additions and 237 deletions

182
cli/package-lock.json generated
View File

@@ -21,7 +21,6 @@
"@typescript-eslint/eslint-plugin": "^6.4.1",
"@typescript-eslint/parser": "^6.4.1",
"@vitest/coverage-v8": "^1.2.2",
"axios": "^1.6.7",
"byte-size": "^8.1.1",
"cli-progress": "^3.12.0",
"commander": "^11.0.0",
@@ -46,12 +45,17 @@
"version": "1.92.1",
"dev": true,
"license": "MIT",
"dependencies": {
"axios": "^1.6.7"
},
"devDependencies": {
"@types/node": "^20.11.0",
"typescript": "^5.3.3"
},
"peerDependencies": {
"axios": "^1.6.7"
},
"peerDependenciesMeta": {
"axios": {
"optional": true
}
}
},
"../server": {
@@ -2127,23 +2131,6 @@
"integrity": "sha512-coglx5yIWuetakm3/1dsX9hxCNox22h7+V80RQOu2XUUMidtArxKoZoOtHUPuR84SycKTXzgGzAUR5hJxujyJQ==",
"dev": true
},
"node_modules/asynckit": {
"version": "0.4.0",
"resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
"integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==",
"dev": true
},
"node_modules/axios": {
"version": "1.6.7",
"resolved": "https://registry.npmjs.org/axios/-/axios-1.6.7.tgz",
"integrity": "sha512-/hDJGff6/c7u0hDkvkGxR/oy6CbCs8ziCsC7SqmhjfozqiJGc8Z11wrv9z9lYfY4K8l+H9TpjcMDX0xOZmx+RA==",
"dev": true,
"dependencies": {
"follow-redirects": "^1.15.4",
"form-data": "^4.0.0",
"proxy-from-env": "^1.1.0"
}
},
"node_modules/b4a": {
"version": "1.6.4",
"resolved": "https://registry.npmjs.org/b4a/-/b4a-1.6.4.tgz",
@@ -2464,18 +2451,6 @@
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
"dev": true
},
"node_modules/combined-stream": {
"version": "1.0.8",
"resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
"integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
"dev": true,
"dependencies": {
"delayed-stream": "~1.0.0"
},
"engines": {
"node": ">= 0.8"
}
},
"node_modules/commander": {
"version": "11.1.0",
"resolved": "https://registry.npmjs.org/commander/-/commander-11.1.0.tgz",
@@ -2620,15 +2595,6 @@
"integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==",
"dev": true
},
"node_modules/delayed-stream": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
"integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==",
"dev": true,
"engines": {
"node": ">=0.4.0"
}
},
"node_modules/diff-sequences": {
"version": "29.6.3",
"resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.6.3.tgz",
@@ -3187,26 +3153,6 @@
"integrity": "sha512-36yxDn5H7OFZQla0/jFJmbIKTdZAQHngCedGxiMmpNfEZM0sdEeT+WczLQrjK6D7o2aiyLYDnkw0R3JK0Qv1RQ==",
"dev": true
},
"node_modules/follow-redirects": {
"version": "1.15.5",
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.5.tgz",
"integrity": "sha512-vSFWUON1B+yAw1VN4xMfxgn5fTUiaOzAJCKBwIIgT/+7CuGy9+r+5gITvP62j3RmaD5Ph65UaERdOSRGUzZtgw==",
"dev": true,
"funding": [
{
"type": "individual",
"url": "https://github.com/sponsors/RubenVerborgh"
}
],
"engines": {
"node": ">=4.0"
},
"peerDependenciesMeta": {
"debug": {
"optional": true
}
}
},
"node_modules/foreground-child": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.1.1.tgz",
@@ -3223,20 +3169,6 @@
"url": "https://github.com/sponsors/isaacs"
}
},
"node_modules/form-data": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz",
"integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==",
"dev": true,
"dependencies": {
"asynckit": "^0.4.0",
"combined-stream": "^1.0.8",
"mime-types": "^2.1.12"
},
"engines": {
"node": ">= 6"
}
},
"node_modules/fs-constants": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz",
@@ -3944,27 +3876,6 @@
"node": ">=8.6"
}
},
"node_modules/mime-db": {
"version": "1.52.0",
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
"integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
"dev": true,
"engines": {
"node": ">= 0.6"
}
},
"node_modules/mime-types": {
"version": "2.1.35",
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
"integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
"dev": true,
"dependencies": {
"mime-db": "1.52.0"
},
"engines": {
"node": ">= 0.6"
}
},
"node_modules/min-indent": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz",
@@ -4472,12 +4383,6 @@
"url": "https://github.com/steveukx/properties?sponsor=1"
}
},
"node_modules/proxy-from-env": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
"integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==",
"dev": true
},
"node_modules/pump": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz",
@@ -6430,7 +6335,6 @@
"version": "file:../open-api/typescript-sdk",
"requires": {
"@types/node": "^20.11.0",
"axios": "^1.6.7",
"typescript": "^5.3.3"
}
},
@@ -7326,23 +7230,6 @@
"integrity": "sha512-coglx5yIWuetakm3/1dsX9hxCNox22h7+V80RQOu2XUUMidtArxKoZoOtHUPuR84SycKTXzgGzAUR5hJxujyJQ==",
"dev": true
},
"asynckit": {
"version": "0.4.0",
"resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
"integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==",
"dev": true
},
"axios": {
"version": "1.6.7",
"resolved": "https://registry.npmjs.org/axios/-/axios-1.6.7.tgz",
"integrity": "sha512-/hDJGff6/c7u0hDkvkGxR/oy6CbCs8ziCsC7SqmhjfozqiJGc8Z11wrv9z9lYfY4K8l+H9TpjcMDX0xOZmx+RA==",
"dev": true,
"requires": {
"follow-redirects": "^1.15.4",
"form-data": "^4.0.0",
"proxy-from-env": "^1.1.0"
}
},
"b4a": {
"version": "1.6.4",
"resolved": "https://registry.npmjs.org/b4a/-/b4a-1.6.4.tgz",
@@ -7552,15 +7439,6 @@
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
"dev": true
},
"combined-stream": {
"version": "1.0.8",
"resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
"integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
"dev": true,
"requires": {
"delayed-stream": "~1.0.0"
}
},
"commander": {
"version": "11.1.0",
"resolved": "https://registry.npmjs.org/commander/-/commander-11.1.0.tgz",
@@ -7668,12 +7546,6 @@
"integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==",
"dev": true
},
"delayed-stream": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
"integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==",
"dev": true
},
"diff-sequences": {
"version": "29.6.3",
"resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.6.3.tgz",
@@ -8101,12 +7973,6 @@
"integrity": "sha512-36yxDn5H7OFZQla0/jFJmbIKTdZAQHngCedGxiMmpNfEZM0sdEeT+WczLQrjK6D7o2aiyLYDnkw0R3JK0Qv1RQ==",
"dev": true
},
"follow-redirects": {
"version": "1.15.5",
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.5.tgz",
"integrity": "sha512-vSFWUON1B+yAw1VN4xMfxgn5fTUiaOzAJCKBwIIgT/+7CuGy9+r+5gITvP62j3RmaD5Ph65UaERdOSRGUzZtgw==",
"dev": true
},
"foreground-child": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.1.1.tgz",
@@ -8117,17 +7983,6 @@
"signal-exit": "^4.0.1"
}
},
"form-data": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz",
"integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==",
"dev": true,
"requires": {
"asynckit": "^0.4.0",
"combined-stream": "^1.0.8",
"mime-types": "^2.1.12"
}
},
"fs-constants": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz",
@@ -8762,21 +8617,6 @@
"picomatch": "^2.3.1"
}
},
"mime-db": {
"version": "1.52.0",
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
"integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
"dev": true
},
"mime-types": {
"version": "2.1.35",
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
"integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
"dev": true,
"requires": {
"mime-db": "1.52.0"
}
},
"min-indent": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz",
@@ -9137,12 +8977,6 @@
"mkdirp": "^1.0.4"
}
},
"proxy-from-env": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
"integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==",
"dev": true
},
"pump": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz",

View File

@@ -22,7 +22,6 @@
"@typescript-eslint/eslint-plugin": "^6.4.1",
"@typescript-eslint/parser": "^6.4.1",
"@vitest/coverage-v8": "^1.2.2",
"axios": "^1.6.7",
"byte-size": "^8.1.1",
"cli-progress": "^3.12.0",
"commander": "^11.0.0",

View File

@@ -3,9 +3,9 @@ import { BaseCommand } from './base-command';
export class ServerInfoCommand extends BaseCommand {
public async run() {
await this.connect();
const { data: versionInfo } = await this.immichApi.serverInfoApi.getServerVersion();
const { data: mediaTypes } = await this.immichApi.serverInfoApi.getSupportedMediaTypes();
const { data: statistics } = await this.immichApi.assetApi.getAssetStatistics();
const versionInfo = await this.immichApi.serverInfoApi.getServerVersion();
const mediaTypes = await this.immichApi.serverInfoApi.getSupportedMediaTypes();
const statistics = await this.immichApi.assetApi.getAssetStatistics();
console.log(`Server Version: ${versionInfo.major}.${versionInfo.minor}.${versionInfo.patch}`);
console.log(`Image Types: ${mediaTypes.image.map((extension) => extension.replace('.', ''))}`);

View File

@@ -1,4 +1,3 @@
import axios, { AxiosRequestConfig, AxiosResponse } from 'axios';
import byteSize from 'byte-size';
import cliProgress from 'cli-progress';
import fs, { createReadStream } from 'node:fs';
@@ -114,7 +113,7 @@ export class UploadCommand extends BaseCommand {
await this.connect();
const formatResponse = await this.immichApi.serverInfoApi.getSupportedMediaTypes();
const crawlService = new CrawlService(formatResponse.data.image, formatResponse.data.video);
const crawlService = new CrawlService(formatResponse.image, formatResponse.video);
const inputFiles: string[] = [];
for (const pathArgument of paths) {
@@ -163,7 +162,7 @@ export class UploadCommand extends BaseCommand {
}
}
const { data: existingAlbums } = await this.immichApi.albumApi.getAllAlbums();
const existingAlbums = await this.immichApi.albumApi.getAllAlbums();
uploadProgress.start(totalSize, 0);
uploadProgress.update({ value_formatted: 0, total_formatted: byteSize(totalSize) });
@@ -186,11 +185,11 @@ export class UploadCommand extends BaseCommand {
assetBulkUploadCheckDto,
});
skipUpload = checkResponse.data.results[0].action === 'reject';
skipUpload = checkResponse.results[0].action === 'reject';
const isDuplicate = checkResponse.data.results[0].reason === 'duplicate';
const isDuplicate = checkResponse.results[0].reason === 'duplicate';
if (isDuplicate) {
existingAssetId = checkResponse.data.results[0].assetId;
existingAssetId = checkResponse.results[0].assetId;
}
skipAsset = skipUpload && !isDuplicate;
@@ -199,8 +198,9 @@ export class UploadCommand extends BaseCommand {
if (!skipAsset && !options.dryRun) {
if (!skipUpload) {
const formData = await asset.getUploadFormData();
const { data } = await this.uploadAsset(formData);
existingAssetId = data.id;
const response = await this.uploadAsset(formData);
const json = await response.json();
existingAssetId = json.id;
uploadCounter++;
totalSizeUploaded += asset.fileSize;
}
@@ -208,10 +208,10 @@ export class UploadCommand extends BaseCommand {
if ((options.album || options.albumName) && asset.albumName !== undefined) {
let album = existingAlbums.find((album) => album.albumName === asset.albumName);
if (!album) {
const { data } = await this.immichApi.albumApi.createAlbum({
const response = await this.immichApi.albumApi.createAlbum({
createAlbumDto: { albumName: asset.albumName },
});
album = data;
album = response;
existingAlbums.push(album);
}
@@ -259,21 +259,20 @@ export class UploadCommand extends BaseCommand {
}
}
private async uploadAsset(data: FormData): Promise<AxiosResponse> {
private async uploadAsset(data: FormData): Promise<Response> {
const url = this.immichApi.instanceUrl + '/asset/upload';
const config: AxiosRequestConfig = {
const response = await fetch(url, {
method: 'post',
maxRedirects: 0,
url,
redirect: 'error',
headers: {
'x-api-key': this.immichApi.apiKey,
},
maxContentLength: Number.POSITIVE_INFINITY,
maxBodyLength: Number.POSITIVE_INFINITY,
data,
};
return axios(config);
body: data,
});
if (response.status !== 200 && response.status !== 201) {
throw new Error(await response.text());
}
return response;
}
}

View File

@@ -10,7 +10,6 @@ import {
SystemConfigApi,
UserApi,
} from '@immich/sdk';
import FormData from 'form-data';
export class ImmichApi {
public userApi: UserApi;
@@ -31,12 +30,9 @@ export class ImmichApi {
) {
this.config = new Configuration({
basePath: instanceUrl,
baseOptions: {
headers: {
'x-api-key': apiKey,
},
headers: {
'x-api-key': apiKey,
},
formDataCtor: FormData,
});
this.userApi = new UserApi(this.config);
@@ -52,6 +48,9 @@ export class ImmichApi {
setApiKey(apiKey: string) {
this.apiKey = apiKey;
this.config.baseOptions.headers['x-api-key'] = apiKey;
if (!this.config.headers) {
throw new Error('missing headers');
}
this.config.headers['x-api-key'] = apiKey;
}
}

View File

@@ -12,8 +12,8 @@ import {
spyOnConsole,
} from '../../test/cli-test-utils';
const mockPingServer = vi.fn(() => Promise.resolve({ data: { res: 'pong' } }));
const mockUserInfo = vi.fn(() => Promise.resolve({ data: { email: 'admin@example.com' } }));
const mockPingServer = vi.fn(() => Promise.resolve({ res: 'pong' }));
const mockUserInfo = vi.fn(() => Promise.resolve({ email: 'admin@example.com' }));
vi.mock('@immich/sdk', async () => ({
...(await vi.importActual('@immich/sdk')),

View File

@@ -51,7 +51,7 @@ export class SessionService {
const api = new ImmichApi(instanceUrl, apiKey);
const { data: pingResponse } = await api.serverInfoApi.pingServer().catch((error) => {
const pingResponse = await api.serverInfoApi.pingServer().catch((error) => {
throw new Error(`Failed to connect to server ${api.instanceUrl}: ${error.message}`);
});
@@ -68,7 +68,7 @@ export class SessionService {
const api = new ImmichApi(instanceUrl, apiKey);
// Check if server and api key are valid
const { data: userInfo } = await api.userApi.getMyUserInfo().catch((error) => {
const userInfo = await api.userApi.getMyUserInfo().catch((error) => {
throw new LoginError(`Failed to connect to server ${instanceUrl}: ${error.message}`);
});

View File

@@ -14,10 +14,10 @@ export const setup = async () => {
await api.authenticationApi.signUpAdmin({
signUpDto: { email: 'cli@immich.app', password: 'password', name: 'Administrator' },
});
const { data: admin } = await api.authenticationApi.login({
const admin = await api.authenticationApi.login({
loginCredentialDto: { email: 'cli@immich.app', password: 'password' },
});
const { data: apiKey } = await api.keyApi.createApiKey(
const apiKey = await api.keyApi.createApiKey(
{ aPIKeyCreateDto: { name: 'CLI Test' } },
{ headers: { Authorization: `Bearer ${admin.accessToken}` } },
);

View File

@@ -32,7 +32,7 @@ describe(`login-key (e2e)`, () => {
it('should error when providing an invalid API key', async () => {
await expect(new LoginCommand(CLI_BASE_OPTIONS).run(instanceUrl, 'invalid')).rejects.toThrow(
`Failed to connect to server ${instanceUrl}: Request failed with status code 401`,
`Failed to connect to server ${instanceUrl}: Response returned an error code`,
);
});

View File

@@ -26,13 +26,13 @@ describe(`upload (e2e)`, () => {
it('should upload a folder recursively', async () => {
await new UploadCommand(CLI_BASE_OPTIONS).run([`${IMMICH_TEST_ASSET_PATH}/albums/nature/`], { recursive: true });
const { data: assets } = await api.assetApi.getAllAssets({}, { headers: { 'x-api-key': api.apiKey } });
const assets = await api.assetApi.getAllAssets({}, { headers: { 'x-api-key': api.apiKey } });
expect(assets.length).toBeGreaterThan(4);
});
it('should not create a new album', async () => {
await new UploadCommand(CLI_BASE_OPTIONS).run([`${IMMICH_TEST_ASSET_PATH}/albums/nature/`], { recursive: true });
const { data: albums } = await api.albumApi.getAllAlbums({}, { headers: { 'x-api-key': api.apiKey } });
const albums = await api.albumApi.getAllAlbums({}, { headers: { 'x-api-key': api.apiKey } });
expect(albums.length).toEqual(0);
});
@@ -42,7 +42,7 @@ describe(`upload (e2e)`, () => {
album: true,
});
const { data: albums } = await api.albumApi.getAllAlbums({}, { headers: { 'x-api-key': api.apiKey } });
const albums = await api.albumApi.getAllAlbums({}, { headers: { 'x-api-key': api.apiKey } });
expect(albums.length).toEqual(1);
const natureAlbum = albums[0];
expect(natureAlbum.albumName).toEqual('nature');
@@ -59,7 +59,7 @@ describe(`upload (e2e)`, () => {
album: true,
});
const { data: albums } = await api.albumApi.getAllAlbums({}, { headers: { 'x-api-key': api.apiKey } });
const albums = await api.albumApi.getAllAlbums({}, { headers: { 'x-api-key': api.apiKey } });
expect(albums.length).toEqual(1);
const natureAlbum = albums[0];
expect(natureAlbum.albumName).toEqual('nature');
@@ -71,7 +71,7 @@ describe(`upload (e2e)`, () => {
albumName: 'testAlbum',
});
const { data: albums } = await api.albumApi.getAllAlbums({}, { headers: { 'x-api-key': api.apiKey } });
const albums = await api.albumApi.getAllAlbums({}, { headers: { 'x-api-key': api.apiKey } });
expect(albums.length).toEqual(1);
const testAlbum = albums[0];
expect(testAlbum.albumName).toEqual('testAlbum');