mirror of
https://github.com/KevinMidboe/immich.git
synced 2025-10-29 17:40:28 +00:00
refactor(server)*: tsconfigs (#2689)
* refactor(server): tsconfigs * chore: dummy commit * fix: start.sh * chore: restore original entry scripts
This commit is contained in:
46
server/src/microservices/utils/exif/coordinates.spec.ts
Normal file
46
server/src/microservices/utils/exif/coordinates.spec.ts
Normal file
@@ -0,0 +1,46 @@
|
||||
import { describe, it, expect } from '@jest/globals';
|
||||
import { parseLatitude, parseLongitude } from './coordinates';
|
||||
|
||||
describe('parsing latitude from string input', () => {
|
||||
it('returns null for invalid inputs', () => {
|
||||
expect(parseLatitude('')).toBeNull();
|
||||
expect(parseLatitude('NaN')).toBeNull();
|
||||
expect(parseLatitude('Infinity')).toBeNull();
|
||||
expect(parseLatitude('-Infinity')).toBeNull();
|
||||
expect(parseLatitude('90.001')).toBeNull();
|
||||
expect(parseLatitude(-90.000001)).toBeNull();
|
||||
expect(parseLatitude('1000')).toBeNull();
|
||||
expect(parseLatitude(-1000)).toBeNull();
|
||||
});
|
||||
|
||||
it('returns the numeric coordinate for valid inputs', () => {
|
||||
expect(parseLatitude('90')).toBeCloseTo(90);
|
||||
expect(parseLatitude('-90')).toBeCloseTo(-90);
|
||||
expect(parseLatitude(89.999999)).toBeCloseTo(89.999999);
|
||||
expect(parseLatitude('-89.9')).toBeCloseTo(-89.9);
|
||||
expect(parseLatitude(0)).toBeCloseTo(0);
|
||||
expect(parseLatitude('-0.0')).toBeCloseTo(-0.0);
|
||||
});
|
||||
});
|
||||
|
||||
describe('parsing longitude from string input', () => {
|
||||
it('returns null for invalid inputs', () => {
|
||||
expect(parseLongitude('')).toBeNull();
|
||||
expect(parseLongitude('NaN')).toBeNull();
|
||||
expect(parseLongitude(Infinity)).toBeNull();
|
||||
expect(parseLongitude('-Infinity')).toBeNull();
|
||||
expect(parseLongitude('180.001')).toBeNull();
|
||||
expect(parseLongitude('-180.000001')).toBeNull();
|
||||
expect(parseLongitude(1000)).toBeNull();
|
||||
expect(parseLongitude('-1000')).toBeNull();
|
||||
});
|
||||
|
||||
it('returns the numeric coordinate for valid inputs', () => {
|
||||
expect(parseLongitude(180)).toBeCloseTo(180);
|
||||
expect(parseLongitude('-180')).toBeCloseTo(-180);
|
||||
expect(parseLongitude('179.999999')).toBeCloseTo(179.999999);
|
||||
expect(parseLongitude(-179.9)).toBeCloseTo(-179.9);
|
||||
expect(parseLongitude('0')).toBeCloseTo(0);
|
||||
expect(parseLongitude('-0.0')).toBeCloseTo(-0.0);
|
||||
});
|
||||
});
|
||||
19
server/src/microservices/utils/exif/coordinates.ts
Normal file
19
server/src/microservices/utils/exif/coordinates.ts
Normal file
@@ -0,0 +1,19 @@
|
||||
import { isNumberInRange } from '../numbers';
|
||||
|
||||
export function parseLatitude(input: string | number): number | null {
|
||||
const latitude = typeof input === 'string' ? Number.parseFloat(input) : input;
|
||||
|
||||
if (isNumberInRange(latitude, -90, 90)) {
|
||||
return latitude;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
export function parseLongitude(input: string | number): number | null {
|
||||
const longitude = typeof input === 'string' ? Number.parseFloat(input) : input;
|
||||
|
||||
if (isNumberInRange(longitude, -180, 180)) {
|
||||
return longitude;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
36
server/src/microservices/utils/exif/date-time.spec.ts
Normal file
36
server/src/microservices/utils/exif/date-time.spec.ts
Normal file
@@ -0,0 +1,36 @@
|
||||
import { describe, expect, it } from '@jest/globals';
|
||||
import { ExifDateTime } from 'exiftool-vendored';
|
||||
import { exifTimeZone, exifToDate } from './date-time';
|
||||
|
||||
describe('converts exif date to JS date', () => {
|
||||
it('returns null for invalid inputs', () => {
|
||||
expect(exifToDate(undefined)).toBeNull();
|
||||
expect(exifToDate('invalid')).toBeNull();
|
||||
expect(exifToDate(new Date('invalid'))).toBeNull();
|
||||
expect(exifToDate(ExifDateTime.fromEXIF('invalid'))).toBeNull();
|
||||
});
|
||||
|
||||
it('returns a valid date object for valid inputs', () => {
|
||||
const date = new Date('2023');
|
||||
expect(exifToDate(date)).toBeInstanceOf(Date);
|
||||
expect(exifToDate(date)?.toISOString()).toBe('2023-01-01T00:00:00.000Z');
|
||||
expect(exifToDate('2023')).toBeInstanceOf(Date);
|
||||
|
||||
const exifDateTime = ExifDateTime.fromISO('2023-01-01T00:00:00.000Z');
|
||||
expect(exifToDate(exifDateTime)).toBeInstanceOf(Date);
|
||||
expect(exifToDate(exifDateTime)?.toISOString()).toBe('2023-01-01T00:00:00.000Z');
|
||||
});
|
||||
});
|
||||
|
||||
describe('extracts the timezone from a date', () => {
|
||||
it('returns null for invalid inputs', () => {
|
||||
expect(exifTimeZone(undefined)).toBeNull();
|
||||
expect(exifTimeZone('')).toBeNull();
|
||||
expect(exifTimeZone(new Date('2023'))).toBeNull();
|
||||
expect(exifTimeZone(ExifDateTime.fromEXIF('invalid'))).toBeNull();
|
||||
});
|
||||
|
||||
it('returns the timezone for valid inputs', () => {
|
||||
expect(exifTimeZone(ExifDateTime.fromEXIF('2020:12:29 14:24:45.700-05:00'))).toBe('UTC-5');
|
||||
});
|
||||
});
|
||||
24
server/src/microservices/utils/exif/date-time.ts
Normal file
24
server/src/microservices/utils/exif/date-time.ts
Normal file
@@ -0,0 +1,24 @@
|
||||
import { ExifDateTime } from 'exiftool-vendored';
|
||||
import { isDecimalNumber } from '../numbers';
|
||||
|
||||
export function exifToDate(exifDate: string | Date | ExifDateTime | undefined): Date | null {
|
||||
if (!exifDate) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const date = exifDate instanceof ExifDateTime ? exifDate.toDate() : new Date(exifDate);
|
||||
if (!isDecimalNumber(date.valueOf())) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return date;
|
||||
}
|
||||
|
||||
export function exifTimeZone(exifDate: string | Date | ExifDateTime | undefined): string | null {
|
||||
const isExifDate = exifDate instanceof ExifDateTime;
|
||||
if (!isExifDate) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return exifDate.zone ?? null;
|
||||
}
|
||||
24
server/src/microservices/utils/exif/iso.spec.ts
Normal file
24
server/src/microservices/utils/exif/iso.spec.ts
Normal file
@@ -0,0 +1,24 @@
|
||||
import { describe, it, expect } from '@jest/globals';
|
||||
import { parseISO } from './iso';
|
||||
|
||||
describe('parsing ISO values', () => {
|
||||
it('returns null for invalid values', () => {
|
||||
expect(parseISO('')).toBeNull();
|
||||
expect(parseISO(',,,')).toBeNull();
|
||||
expect(parseISO('invalid')).toBeNull();
|
||||
expect(parseISO('-5')).toBeNull();
|
||||
expect(parseISO('99999999999999')).toBeNull();
|
||||
});
|
||||
|
||||
it('returns the ISO number for valid inputs', () => {
|
||||
expect(parseISO('0.0')).toBe(0);
|
||||
expect(parseISO('32000.9')).toBe(32000);
|
||||
});
|
||||
|
||||
it('returns the first valid ISO number in a comma separated list', () => {
|
||||
expect(parseISO('400, 200, 100')).toBe(400);
|
||||
expect(parseISO('-1600,800')).toBe(800);
|
||||
expect(parseISO('-1, a., 1200')).toBe(1200);
|
||||
expect(parseISO('NaN,50,100')).toBe(50);
|
||||
});
|
||||
});
|
||||
14
server/src/microservices/utils/exif/iso.ts
Normal file
14
server/src/microservices/utils/exif/iso.ts
Normal file
@@ -0,0 +1,14 @@
|
||||
import { isNumberInRange } from '../numbers';
|
||||
|
||||
export function parseISO(input: string): number | null {
|
||||
const values = input.split(',');
|
||||
|
||||
for (const value of values) {
|
||||
const iso = Number.parseInt(value, 10);
|
||||
if (isNumberInRange(iso, 0, 2 ** 32)) {
|
||||
return iso;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
47
server/src/microservices/utils/numbers.spec.ts
Normal file
47
server/src/microservices/utils/numbers.spec.ts
Normal file
@@ -0,0 +1,47 @@
|
||||
import { describe, it, expect } from '@jest/globals';
|
||||
import { isDecimalNumber, isNumberInRange, toNumberOrNull } from './numbers';
|
||||
|
||||
describe('checks if a number is a decimal number', () => {
|
||||
it('returns false for non-decimal numbers', () => {
|
||||
expect(isDecimalNumber(NaN)).toBe(false);
|
||||
expect(isDecimalNumber(Infinity)).toBe(false);
|
||||
expect(isDecimalNumber(-Infinity)).toBe(false);
|
||||
});
|
||||
|
||||
it('returns true for decimal numbers', () => {
|
||||
expect(isDecimalNumber(0)).toBe(true);
|
||||
expect(isDecimalNumber(-0)).toBe(true);
|
||||
expect(isDecimalNumber(10.12345)).toBe(true);
|
||||
expect(isDecimalNumber(Number.MAX_VALUE)).toBe(true);
|
||||
expect(isDecimalNumber(Number.MIN_VALUE)).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('checks if a number is within a range', () => {
|
||||
it('returns false for numbers outside the range', () => {
|
||||
expect(isNumberInRange(0, 10, 10)).toBe(false);
|
||||
expect(isNumberInRange(0.01, 10, 10)).toBe(false);
|
||||
expect(isNumberInRange(50.1, 0, 50)).toBe(false);
|
||||
});
|
||||
|
||||
it('returns true for numbers inside the range', () => {
|
||||
expect(isNumberInRange(0, 0, 50)).toBe(true);
|
||||
expect(isNumberInRange(50, 0, 50)).toBe(true);
|
||||
expect(isNumberInRange(-50.12345, -50.12345, 0)).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('converts input to a number or null', () => {
|
||||
it('returns null for invalid inputs', () => {
|
||||
expect(toNumberOrNull(null)).toBeNull();
|
||||
expect(toNumberOrNull(undefined)).toBeNull();
|
||||
expect(toNumberOrNull('')).toBeNull();
|
||||
expect(toNumberOrNull(NaN)).toBeNull();
|
||||
});
|
||||
|
||||
it('returns a number for valid inputs', () => {
|
||||
expect(toNumberOrNull(0)).toBeCloseTo(0);
|
||||
expect(toNumberOrNull('0')).toBeCloseTo(0);
|
||||
expect(toNumberOrNull('-123.45')).toBeCloseTo(-123.45);
|
||||
});
|
||||
});
|
||||
19
server/src/microservices/utils/numbers.ts
Normal file
19
server/src/microservices/utils/numbers.ts
Normal file
@@ -0,0 +1,19 @@
|
||||
export function isDecimalNumber(num: number): boolean {
|
||||
return !Number.isNaN(num) && Number.isFinite(num);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if `num` is a valid number and is between `start` and `end` (inclusive)
|
||||
*/
|
||||
export function isNumberInRange(num: number, start: number, end: number): boolean {
|
||||
return isDecimalNumber(num) && num >= start && num <= end;
|
||||
}
|
||||
|
||||
export function toNumberOrNull(input: number | string | null | undefined): number | null {
|
||||
if (input === null || input === undefined) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const num = typeof input === 'string' ? Number.parseFloat(input) : input;
|
||||
return isDecimalNumber(num) ? num : null;
|
||||
}
|
||||
Reference in New Issue
Block a user