Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix n+1 problem for zoom account availability #82

Merged
merged 9 commits into from
Jun 9, 2020
15 changes: 7 additions & 8 deletions api/graphql/query/Resource/availability.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,17 +10,16 @@ class ZoomAccountAvailability extends BaseResolver {
});
const topic = await resource.getTopic();

if (this.parent.subject_type === 'ZoomAccount')
return ZoomAccount.findAvailaibilityDuring(
this.parent.subject_id,
{
if (this.parent.subject_type === 'ZoomAccount') {
return await this.ctx.loaders.zoomAccountAvailability.load({
id: this.parent.id,
dateTimeRange: {
start_at: topic.start_at,
end_at: topic.end_at,
},
{
excludeTopics: [topic.id],
},
);
excludeEvents: [topic.id],
});
}

//NOTE(naman) For a new resource add finding availability logic here
return false;
Expand Down
11 changes: 5 additions & 6 deletions api/graphql/query/ZoomAccount/availability.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
import ZoomAccount from 'Services/ZoomAccount';
import BaseResolver from 'Graphql/base/Resolver';

class ZoomAccountAvailability extends BaseResolver {
resolve = () =>
ZoomAccount.findAvailaibilityDuring(
this.parent.id,
this.args.dateTimeRange,
);
resolve = async () =>
await this.ctx.loaders.zoomAccountAvailability.load({
id: this.parent.id,
dateTimeRange: this.args.dateTimeRange,
});
}

export default ZoomAccountAvailability.resolver();
2 changes: 2 additions & 0 deletions api/routes/graphql/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { ApolloServer } from 'apollo-server-micro';
import authenticate from 'Middlewares/authenticate';
import CalendarEventInvite from 'Services/CalendarEventInvite';
import User from 'Services/User';
import ZoomAccount from 'Services/ZoomAccount';

const server = new ApolloServer({
schema,
Expand All @@ -13,6 +14,7 @@ const server = new ApolloServer({
loaders: {
userAvailability: User.getAvailabilityLoader(),
eventInviteStatus: CalendarEventInvite.getStatusLoader(),
zoomAccountAvailability: ZoomAccount.getAvailabilityLoader()
},
}),
});
Expand Down
64 changes: 64 additions & 0 deletions api/services/ZoomAccount/Loaders/Availability.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import Models from 'Models';
import BaseLoader from 'Services/BaseModelService/Loader';
import overlapDateTimeClause from 'Utils/overlapDateTimeClause';

export default class Availability extends BaseLoader {
load = async () => {
const accounts = await Models.ZoomAccount.findAll({
include: [
{
model: Models.Resource,
as: 'uses',
include: [
{
model: Models.CalendarEvent,
as: 'calendarEvent',
where: {
[Models.Sequelize.Op.or]: this.keys.map(
({ id, dateTimeRange, excludeEvents = [] }) => ({
[Models.Sequelize.Op.and]: [
overlapDateTimeClause(dateTimeRange),
{
'$zoom_account.id$': id,
},
{
id: {
[Models.Sequelize.Op.notIn]: excludeEvents,
},
},
],
}),
),
},
required: true,
},
],
required: true,
},
],
});

return this.keys.map(
(key) =>
!accounts.find((account) => this.equateKeyAndAccount(key, account)),
);
};

equateKeyAndAccount({ id, excludedEvents = [], dateTimeRange }, account) {
if (
account.id !== id ||
excludedEvents.includes(account['uses.calendarEvent.id'])
)
return false;

if (
new Date(account['uses.calendarEvent.start_at']) >
new Date(dateTimeRange.end_at) ||
new Date(account['uses.calendarEvent.end_at']) <
new Date(dateTimeRange.start_at)
)
return false;

return true;
}
}
4 changes: 3 additions & 1 deletion api/services/ZoomAccount/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import BaseModelService, {
requireInstance,
} from 'Services/BaseModelService';
import { utilizedResourceClause } from './utils';
import Availability from './Loaders/Availability';

export default class ZoomAccount extends BaseModelService {
static findAllInUse(...args) {
Expand All @@ -15,10 +16,11 @@ export default class ZoomAccount extends BaseModelService {
where: { id },
...utilizedResourceClause(...args),
});

return !data;
}

static getAvailabilityLoader = Availability.loader();

@requireInstance
ifAvailableDuring(dateTimeRange, options) {
return ZoomAccount.findAvailaibilityDuring(
Expand Down