Skip to content
This repository has been archived by the owner on Mar 27, 2024. It is now read-only.

Commit

Permalink
WIP Move logic inside store instances
Browse files Browse the repository at this point in the history
  • Loading branch information
naripok committed Jan 12, 2021
1 parent be7ab70 commit 1668a2b
Show file tree
Hide file tree
Showing 3 changed files with 104 additions and 81 deletions.
75 changes: 9 additions & 66 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
import * as engine from './engine';
import { viewStore, matchedAudienceStore } from './store';
import { timeStampInSecs } from './utils';
import { waitForConsent } from './gdpr';
import { Edkt, AudienceDefinition, MatchedAudience } from '../types';
import { Edkt } from '../types';

const run: Edkt['run'] = async (config) => {
const {
Expand All @@ -21,75 +19,20 @@ const run: Edkt['run'] = async (config) => {

// This is a no-op if undefined or lesser than 0
viewStore.setStorageSize(featureStorageSize);
viewStore.insert(pageFeatures, pageMetadata);

const currentTS = timeStampInSecs();
// TODO the problem here is that it is not immediately clear
// that call order for viewStore.savePageViews and
// matchedAudienceStore.saveMatchingAudiences matters,
// but it do.
viewStore.savePageViews(pageFeatures, pageMetadata);

const doesAudienceVersionMatch = (
audience: AudienceDefinition,
matchedAudience: MatchedAudience
) =>
audience.id === matchedAudience.id &&
audience.version === matchedAudience.version;

const newMatchedAudiences = audienceDefinitions
// drop prev matched audiences with same version
.filter((audience) => {
return !matchedAudienceStore.matchedAudiences.some((matchedAudience) =>
doesAudienceVersionMatch(audience, matchedAudience)
);
})
// translate audience definitions into engine queries
.map((audience) => {
return {
...audience,
conditions: engine.translate(audience),
};
})
// check if query matches any pageViews
.map((query) => {
const pageViewsWithinLookBack = viewStore.pageViews.filter((pageView) => {
return query.lookBack === 0
? true
: pageView.ts > currentTS - query.lookBack;
});
return {
id: query.id,
version: query.version,
matchedAt: currentTS,
expiresAt: currentTS + query.ttl,
matchedOnCurrentPageView: true,
matched: engine.check(query.conditions, pageViewsWithinLookBack),
};
})
// keep only matched audiences
.filter((audience) => audience.matched);

// We also want to keep prev matched audiences with matching versions
const prevMatchedAudiences = matchedAudienceStore.matchedAudiences.filter(
(matchedAudience) =>
audienceDefinitions.some((audience) =>
doesAudienceVersionMatch(audience, matchedAudience)
)
);

const matchedAudiences = [...prevMatchedAudiences, ...newMatchedAudiences];

matchedAudienceStore.setMatchedAudiences(matchedAudiences);
};

const getMatchedAudiences: Edkt['getMatchedAudiences'] = () => {
return matchedAudienceStore.matchedAudiences;
};

const getCopyOfPageViews: Edkt['getCopyOfPageViews'] = () => {
return [...viewStore.pageViews];
matchedAudienceStore.saveMatchingAudiences(audienceDefinitions, viewStore);
};

export const edkt: Edkt = {
run,
getMatchedAudiences,
getCopyOfPageViews,
getMatchedAudiences: () => matchedAudienceStore.getMatchedAudiences(),
getCopyOfPageViews: () => viewStore.getPageViews(),
};

export * from './store';
Expand Down
81 changes: 75 additions & 6 deletions src/store/matchedAudiences.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,26 @@
import { storage, timeStampInSecs } from '../utils';
import { StorageKeys, MatchedAudience } from '../../types';
import { StorageKeys, MatchedAudience, AudienceDefinition } from '../../types';
import * as engine from '../engine';
import { ViewStore } from '../store/pageview';

const doesAudienceVersionMatch = (
audience: AudienceDefinition,
matchedAudience: MatchedAudience
) =>
audience.id === matchedAudience.id &&
audience.version === matchedAudience.version;

class MatchedAudienceStore {
matchedAudiences: MatchedAudience[];
matchedAudienceIds: string[];
private matchedAudiences: MatchedAudience[];
private matchedAudienceIds: string[];

constructor() {
this.matchedAudiences = [];
this.matchedAudienceIds = [];
this._load();
}

_load() {
_load(): void {
const audiences: MatchedAudience[] =
storage.get(StorageKeys.MATCHED_AUDIENCES) || [];
const unExpiredAudiences = audiences
Expand All @@ -30,14 +39,74 @@ class MatchedAudienceStore {
this._save();
}

_save() {
_save(): void {
storage.set(StorageKeys.MATCHED_AUDIENCES, this.matchedAudiences);
storage.set(StorageKeys.MATCHED_AUDIENCE_IDS, this.matchedAudienceIds);
}

setMatchedAudiences(matchedAudiences: MatchedAudience[]) {
setMatchedAudiences(matchedAudiences: MatchedAudience[]): void {
this.matchedAudiences = matchedAudiences;
this.matchedAudienceIds = matchedAudiences.map((audience) => audience.id);
}

getMatchedAudiences(): MatchedAudience[] {
return [...this.matchedAudiences];
}

saveMatchingAudiences(
audienceDefinitions: AudienceDefinition[],
viewStore: ViewStore
): void {
const currentTS = timeStampInSecs();

const newlyMatchedAudiences = audienceDefinitions
// drop prev matched audiences with same version
.filter((audience) => {
return !this.matchedAudiences.some((matchedAudience) =>
doesAudienceVersionMatch(audience, matchedAudience)
);
})
// translate audience definitions into engine queries
.map((audience) => {
return {
...audience,
conditions: engine.translate(audience),
};
})
// check if query matches any pageViews
.map((query) => {
// TODO Ideally pageViewsWithinLookBack should be passed as
// argument to saveMatchingAudiences methods, but as it is now
// we got to take the entire viewStore instance as arg,
// or else there will be repetition.
const pageViewsWithinLookBack = viewStore.getPageViewsWithinLookBack(
query.lookBack
);
return {
id: query.id,
version: query.version,
matchedAt: currentTS,
expiresAt: currentTS + query.ttl,
matchedOnCurrentPageView: true,
matched: engine.check(query.conditions, pageViewsWithinLookBack),
};
})
// keep only matched audiences
.filter((audience) => audience.matched);

// We also want to keep prev matched audiences with matching versions
const prevMatchedAudiences = this.matchedAudiences.filter(
(matchedAudience) =>
audienceDefinitions.some((audience) =>
doesAudienceVersionMatch(audience, matchedAudience)
)
);

this.setMatchedAudiences([
...prevMatchedAudiences,
...newlyMatchedAudiences,
]);

this._save();
}
}
Expand Down
29 changes: 20 additions & 9 deletions src/store/pageview.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,25 +3,25 @@ import { PageView, StorageKeys, PageFeatureResult } from '../../types';

const DEFAULT_MAX_FEATURES_SIZE = 300;

class ViewStore {
pageViews: PageView[];
storageSize: number;
export class ViewStore {
private pageViews: PageView[];
private storageSize: number;

constructor() {
this.pageViews = [];
this.storageSize = DEFAULT_MAX_FEATURES_SIZE;
this._load();
}

_load() {
_load(): void {
this.pageViews = storage.get(StorageKeys.PAGE_VIEWS) || [];
}

_save() {
_save(): void {
storage.set(StorageKeys.PAGE_VIEWS, this.pageViews);
}

_trim() {
_trim(): void {
if (this.pageViews.length <= this.storageSize) return;
this.pageViews.sort((a: PageView, b: PageView): number => b.ts - a.ts);
this.pageViews = this.pageViews.slice(0, this.storageSize);
Expand All @@ -30,15 +30,15 @@ class ViewStore {
/**
* @param storageSize Max pageView items to be kept
*/
setStorageSize(storageSize?: number) {
setStorageSize(storageSize?: number): void {
if (!storageSize || storageSize < 0) return;
this.storageSize = storageSize;
}

insert(
savePageViews(
features: Record<string, PageFeatureResult> | undefined,
metadata?: Record<string, string | number | boolean>
) {
): void {
if (!features || Object.keys(features).length < 1) return;
const ts = timeStampInSecs();
const pageView = {
Expand All @@ -50,6 +50,17 @@ class ViewStore {
this._trim();
this._save();
}

getPageViewsWithinLookBack(lookBack: number): PageView[] {
const ts = timeStampInSecs();
return this.pageViews.filter((pageView) => {
return lookBack === 0 || pageView.ts > ts - lookBack;
});
}

getPageViews(): PageView[] {
return [...this.pageViews];
}
}

export const viewStore = new ViewStore();

0 comments on commit 1668a2b

Please sign in to comment.