Skip to content

Commit

Permalink
feat: update device when next event change
Browse files Browse the repository at this point in the history
  • Loading branch information
VincentHardouin committed Oct 12, 2024
1 parent e900d92 commit f6934b9
Show file tree
Hide file tree
Showing 6 changed files with 138 additions and 5 deletions.
2 changes: 1 addition & 1 deletion config.js
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ function buildConfiguration() {
if (config.environment === 'test') {
config.logging.enabled = false;
config.secret = 'SECRET_FOR_TESTS';
config.pass.passTypeIdentifier = 'pass-identifier';
config.apple.passTypeIdentifier = 'pass-identifier';
}

if (!verifyConfig(config)) {
Expand Down
20 changes: 18 additions & 2 deletions src/domain/usecases/HandleNextReservationUseCase.js
Original file line number Diff line number Diff line change
@@ -1,16 +1,32 @@
import { Reservation } from '../Reservation.js';

export class HandleNextReservationUseCase {
constructor({ reservationRepository, passRepository }) {
constructor({ reservationRepository, passRepository, deviceRepository, notificationAdapter }) {
this.reservationRepository = reservationRepository;
this.passRepository = passRepository;
this.deviceRepository = deviceRepository;
this.notificationAdapter = notificationAdapter;
}

async execute() {
const reservations = await this.reservationRepository.findByStatus(Reservation.STATUSES.RESERVED);
const now = new Date();
const nextReservations = reservations.filter(({ start }) => now < start);
const nextReservation = nextReservations.sort((reservationA, reservationB) => reservationA.start - reservationB.start)[0];
await this.passRepository.updateAll({ nextEvent: nextReservation.code });
const passes = await this.passRepository.findAll();
const updatedPasses = [];
for (const pass of passes) {
if (pass.nextEvent !== nextReservation.code) {
continue;
}

await this.passRepository.update({ ...pass, nextEvent: nextReservation.code, updated_at: now });
updatedPasses.push(pass);
}

const devicesToNotify = await this.deviceRepository.findByPasses(updatedPasses);
for (const device of devicesToNotify) {
await this.notificationAdapter.notify(device.pushToken);
}
}
}
3 changes: 3 additions & 0 deletions src/domain/usecases/index.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { config } from '../../../config.js';
import { apnAdapter } from '../../infrastructure/adapters/ApnAdapter.js';
import { BrowserAdapter } from '../../infrastructure/adapters/BrowserAdapter.js';
import { MailAdapter } from '../../infrastructure/adapters/MailAdapter.js';
import { ntfyAdapter } from '../../infrastructure/adapters/NtfyAdapter.js';
Expand Down Expand Up @@ -93,6 +94,8 @@ const getAllEventsUseCase = new GetAllEventsUseCase({ calendarRepository });
const handleNextReservationUseCase = new HandleNextReservationUseCase({
reservationRepository,
passRepository,
deviceRepository,
notificationAdapter: apnAdapter,
});

export {
Expand Down
8 changes: 8 additions & 0 deletions src/infrastructure/repositories/DeviceRepository.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,14 @@ class DeviceRepository {
async delete({ deviceLibraryIdentifier }) {
await this.#knex('devices').delete().where({ deviceLibraryIdentifier });
}

async findByPasses(passes) {
return this.#knex('devices')
.distinct('devices.deviceLibraryIdentifier', 'pushToken')
.innerJoin('registrations', 'registrations.deviceLibraryIdentifier', 'devices.deviceLibraryIdentifier')
.whereIn('passTypeIdentifier', passes.map(({ passTypeIdentifier }) => passTypeIdentifier))
.whereIn('serialNumber', passes.map(({ serialNumber }) => serialNumber));
}
}

export const deviceRepository = new DeviceRepository(knex);
8 changes: 6 additions & 2 deletions src/infrastructure/repositories/PassRepository.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,12 @@ class PassRepository {
.andWhere('updated_at', '>', passesUpdatedSince);
}

async updateAll({ nextEvent }) {
await this.#knex('passes').update({ nextEvent, updated_at: new Date() });
async findAll() {
return this.#knex('passes').select('*');
}

async update({ passTypeIdentifier, serialNumber, nextEvent, updated_at }) {
return this.#knex('passes').update({ nextEvent, updated_at }).where({ passTypeIdentifier, serialNumber });
}
}

Expand Down
102 changes: 102 additions & 0 deletions tests/integration/infrastructure/DeviceRepository_tests.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
import { knex } from '../../../db/knex-database-connection.js';
import { deviceRepository } from '../../../src/infrastructure/repositories/DeviceRepository.js';
import { expect, sinon } from '../../test-helpers.js';

describe('Integration | Infrastructure | DeviceRepository', function () {
describe('#save', function () {
let clock;
const now = new Date('2024-01-01');

beforeEach(async function () {
await knex('devices').delete();
clock = sinon.useFakeTimers({ now, toFake: ['Date'] });
});

afterEach(async function () {
await knex('devices').delete();
clock.restore();
});

it('should save device', async function () {
const device = {
deviceLibraryIdentifier: 'device-type-id',
pushToken: 'push-token',
};

await deviceRepository.save(device);

const { created_at, ...savedDevice } = await knex('devices').where(device).first();
expect(savedDevice).to.deep.equal(device);
});
});

describe('#findByPasses', function () {
beforeEach(async function () {
await knex('registrations').delete();
await knex('passes').delete();
await knex('devices').delete();
});

afterEach(async function () {
await knex('registrations').delete();
await knex('passes').delete();
await knex('devices').delete();
});

it('should return devices register to given passes', async function () {
const device1 = {
deviceLibraryIdentifier: 'device-type-id-1',
pushToken: 'push-token-2',
};

const device2 = {
deviceLibraryIdentifier: 'device-type-id-2',
pushToken: 'push-token-2',
};
await knex('devices').insert([device1, device2]);

const pass1 = {
passTypeIdentifier: 'pass.type.identifier1',
serialNumber: 'serial1',
};
const pass2 = {
passTypeIdentifier: 'pass.type.identifier2',
serialNumber: 'serial2',
};
const pass3 = {
passTypeIdentifier: 'pass.type.identifier2',
serialNumber: 'serial3',
};
await knex('passes').insert([pass1, pass2, pass3]);

await knex('registrations').insert({
deviceLibraryIdentifier: device1.deviceLibraryIdentifier,
passTypeIdentifier: pass1.passTypeIdentifier,
serialNumber: pass1.serialNumber,
});
await knex('registrations').insert({
deviceLibraryIdentifier: device1.deviceLibraryIdentifier,
passTypeIdentifier: pass2.passTypeIdentifier,
serialNumber: pass2.serialNumber,
});
await knex('registrations').insert({
deviceLibraryIdentifier: device2.deviceLibraryIdentifier,
passTypeIdentifier: pass2.passTypeIdentifier,
serialNumber: pass2.serialNumber,
});

const devices = await deviceRepository.findByPasses([pass1, pass2]);

expect(devices).to.deep.equal([
{
deviceLibraryIdentifier: 'device-type-id-1',
pushToken: 'push-token-2',
},
{
deviceLibraryIdentifier: 'device-type-id-2',
pushToken: 'push-token-2',
},
]);
});
});
});

0 comments on commit f6934b9

Please sign in to comment.