Skip to content

Commit f6934b9

Browse files
committedOct 12, 2024
feat: update device when next event change
1 parent e900d92 commit f6934b9

File tree

6 files changed

+138
-5
lines changed

6 files changed

+138
-5
lines changed
 

‎config.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ function buildConfiguration() {
8282
if (config.environment === 'test') {
8383
config.logging.enabled = false;
8484
config.secret = 'SECRET_FOR_TESTS';
85-
config.pass.passTypeIdentifier = 'pass-identifier';
85+
config.apple.passTypeIdentifier = 'pass-identifier';
8686
}
8787

8888
if (!verifyConfig(config)) {
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,32 @@
11
import { Reservation } from '../Reservation.js';
22

33
export class HandleNextReservationUseCase {
4-
constructor({ reservationRepository, passRepository }) {
4+
constructor({ reservationRepository, passRepository, deviceRepository, notificationAdapter }) {
55
this.reservationRepository = reservationRepository;
66
this.passRepository = passRepository;
7+
this.deviceRepository = deviceRepository;
8+
this.notificationAdapter = notificationAdapter;
79
}
810

911
async execute() {
1012
const reservations = await this.reservationRepository.findByStatus(Reservation.STATUSES.RESERVED);
1113
const now = new Date();
1214
const nextReservations = reservations.filter(({ start }) => now < start);
1315
const nextReservation = nextReservations.sort((reservationA, reservationB) => reservationA.start - reservationB.start)[0];
14-
await this.passRepository.updateAll({ nextEvent: nextReservation.code });
16+
const passes = await this.passRepository.findAll();
17+
const updatedPasses = [];
18+
for (const pass of passes) {
19+
if (pass.nextEvent !== nextReservation.code) {
20+
continue;
21+
}
22+
23+
await this.passRepository.update({ ...pass, nextEvent: nextReservation.code, updated_at: now });
24+
updatedPasses.push(pass);
25+
}
26+
27+
const devicesToNotify = await this.deviceRepository.findByPasses(updatedPasses);
28+
for (const device of devicesToNotify) {
29+
await this.notificationAdapter.notify(device.pushToken);
30+
}
1531
}
1632
}

‎src/domain/usecases/index.js

+3
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { config } from '../../../config.js';
2+
import { apnAdapter } from '../../infrastructure/adapters/ApnAdapter.js';
23
import { BrowserAdapter } from '../../infrastructure/adapters/BrowserAdapter.js';
34
import { MailAdapter } from '../../infrastructure/adapters/MailAdapter.js';
45
import { ntfyAdapter } from '../../infrastructure/adapters/NtfyAdapter.js';
@@ -93,6 +94,8 @@ const getAllEventsUseCase = new GetAllEventsUseCase({ calendarRepository });
9394
const handleNextReservationUseCase = new HandleNextReservationUseCase({
9495
reservationRepository,
9596
passRepository,
97+
deviceRepository,
98+
notificationAdapter: apnAdapter,
9699
});
97100

98101
export {

‎src/infrastructure/repositories/DeviceRepository.js

+8
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,14 @@ class DeviceRepository {
2323
async delete({ deviceLibraryIdentifier }) {
2424
await this.#knex('devices').delete().where({ deviceLibraryIdentifier });
2525
}
26+
27+
async findByPasses(passes) {
28+
return this.#knex('devices')
29+
.distinct('devices.deviceLibraryIdentifier', 'pushToken')
30+
.innerJoin('registrations', 'registrations.deviceLibraryIdentifier', 'devices.deviceLibraryIdentifier')
31+
.whereIn('passTypeIdentifier', passes.map(({ passTypeIdentifier }) => passTypeIdentifier))
32+
.whereIn('serialNumber', passes.map(({ serialNumber }) => serialNumber));
33+
}
2634
}
2735

2836
export const deviceRepository = new DeviceRepository(knex);

‎src/infrastructure/repositories/PassRepository.js

+6-2
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,12 @@ class PassRepository {
2929
.andWhere('updated_at', '>', passesUpdatedSince);
3030
}
3131

32-
async updateAll({ nextEvent }) {
33-
await this.#knex('passes').update({ nextEvent, updated_at: new Date() });
32+
async findAll() {
33+
return this.#knex('passes').select('*');
34+
}
35+
36+
async update({ passTypeIdentifier, serialNumber, nextEvent, updated_at }) {
37+
return this.#knex('passes').update({ nextEvent, updated_at }).where({ passTypeIdentifier, serialNumber });
3438
}
3539
}
3640

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
import { knex } from '../../../db/knex-database-connection.js';
2+
import { deviceRepository } from '../../../src/infrastructure/repositories/DeviceRepository.js';
3+
import { expect, sinon } from '../../test-helpers.js';
4+
5+
describe('Integration | Infrastructure | DeviceRepository', function () {
6+
describe('#save', function () {
7+
let clock;
8+
const now = new Date('2024-01-01');
9+
10+
beforeEach(async function () {
11+
await knex('devices').delete();
12+
clock = sinon.useFakeTimers({ now, toFake: ['Date'] });
13+
});
14+
15+
afterEach(async function () {
16+
await knex('devices').delete();
17+
clock.restore();
18+
});
19+
20+
it('should save device', async function () {
21+
const device = {
22+
deviceLibraryIdentifier: 'device-type-id',
23+
pushToken: 'push-token',
24+
};
25+
26+
await deviceRepository.save(device);
27+
28+
const { created_at, ...savedDevice } = await knex('devices').where(device).first();
29+
expect(savedDevice).to.deep.equal(device);
30+
});
31+
});
32+
33+
describe('#findByPasses', function () {
34+
beforeEach(async function () {
35+
await knex('registrations').delete();
36+
await knex('passes').delete();
37+
await knex('devices').delete();
38+
});
39+
40+
afterEach(async function () {
41+
await knex('registrations').delete();
42+
await knex('passes').delete();
43+
await knex('devices').delete();
44+
});
45+
46+
it('should return devices register to given passes', async function () {
47+
const device1 = {
48+
deviceLibraryIdentifier: 'device-type-id-1',
49+
pushToken: 'push-token-2',
50+
};
51+
52+
const device2 = {
53+
deviceLibraryIdentifier: 'device-type-id-2',
54+
pushToken: 'push-token-2',
55+
};
56+
await knex('devices').insert([device1, device2]);
57+
58+
const pass1 = {
59+
passTypeIdentifier: 'pass.type.identifier1',
60+
serialNumber: 'serial1',
61+
};
62+
const pass2 = {
63+
passTypeIdentifier: 'pass.type.identifier2',
64+
serialNumber: 'serial2',
65+
};
66+
const pass3 = {
67+
passTypeIdentifier: 'pass.type.identifier2',
68+
serialNumber: 'serial3',
69+
};
70+
await knex('passes').insert([pass1, pass2, pass3]);
71+
72+
await knex('registrations').insert({
73+
deviceLibraryIdentifier: device1.deviceLibraryIdentifier,
74+
passTypeIdentifier: pass1.passTypeIdentifier,
75+
serialNumber: pass1.serialNumber,
76+
});
77+
await knex('registrations').insert({
78+
deviceLibraryIdentifier: device1.deviceLibraryIdentifier,
79+
passTypeIdentifier: pass2.passTypeIdentifier,
80+
serialNumber: pass2.serialNumber,
81+
});
82+
await knex('registrations').insert({
83+
deviceLibraryIdentifier: device2.deviceLibraryIdentifier,
84+
passTypeIdentifier: pass2.passTypeIdentifier,
85+
serialNumber: pass2.serialNumber,
86+
});
87+
88+
const devices = await deviceRepository.findByPasses([pass1, pass2]);
89+
90+
expect(devices).to.deep.equal([
91+
{
92+
deviceLibraryIdentifier: 'device-type-id-1',
93+
pushToken: 'push-token-2',
94+
},
95+
{
96+
deviceLibraryIdentifier: 'device-type-id-2',
97+
pushToken: 'push-token-2',
98+
},
99+
]);
100+
});
101+
});
102+
});

0 commit comments

Comments
 (0)