-
Notifications
You must be signed in to change notification settings - Fork 2
Add a marketing tool API with marketer keys and audience queries #78
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
Open
peachbits
wants to merge
9
commits into
master
Choose a base branch
from
matthew/marketing-push-tool
base: master
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
9 commits
Select commit
Hold shift + click to select a range
b84ad9d
Add pushServerConfig.sample.json
peachbits c70a278
Add an api-key location view and batched device lookups
peachbits 254b1ab
Add marketer API keys with target scoping
peachbits d2f9ec5
Add the marketing tool API
peachbits a769049
fixup! Add the marketing tool API
peachbits 29f2e40
fixup! Add the marketing tool API
peachbits d04cdf4
fixup! Add the marketing tool API
peachbits e31c1e3
Clean up push tokens whose app has been uninstalled
peachbits a733a6d
Report send progress on a clock instead of per device
peachbits File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,7 @@ | ||
| { | ||
| "listenHost": "127.0.0.1", | ||
| "listenPort": 8008, | ||
| "amqpUri": "amqp://username:password@localhost:5672", | ||
| "couchUri": "http://username:password@localhost:5984", | ||
| "currentCluster": "" | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,99 @@ | ||
| import { Device } from '../../types/pushTypes' | ||
|
|
||
| /** | ||
| * An include/exclude filter over the city and region a device is located in. | ||
| * The lists hold normalized values (see `parseLocationList`). | ||
| */ | ||
| export interface LocationFilter { | ||
| cityInclude: string[] | ||
| cityExclude: string[] | ||
| regionInclude: string[] | ||
| regionExclude: string[] | ||
| } | ||
|
|
||
| /** | ||
| * Why a device cannot receive a marketing push. | ||
| */ | ||
| export type DeviceSkipReason = | ||
| | 'ignore-marketing' | ||
| | 'unregistered' | ||
| | 'invalid-token' | ||
|
|
||
| // Firebase tokens are alphanumerics plus these separators. Anything else is a | ||
| // corrupt row that would only fail at delivery time: | ||
| const VALID_TOKEN = /^[a-zA-Z0-9_\-:]+$/ | ||
|
|
||
| /** | ||
| * Normalizes one include/exclude box: each line is a separate value, blank | ||
| * lines are dropped, and matching is case-insensitive. | ||
| */ | ||
| export function parseLocationList(lines: string[]): string[] { | ||
| const out = new Set<string>() | ||
| for (const line of lines) { | ||
| const value = line.trim().toLowerCase() | ||
| if (value !== '') out.add(value) | ||
| } | ||
| return [...out] | ||
| } | ||
|
|
||
| /** | ||
| * Decides whether a device's location passes the filter. Values within one box | ||
| * are OR'd together, and the boxes are AND'ed with each other. An empty include | ||
| * box means "no restriction", while an empty exclude box excludes nothing. | ||
| * | ||
| * Country is deliberately absent: the caller selects it through the Couch view | ||
| * prefix, so re-checking it here could only disagree with the query. | ||
| */ | ||
| export function matchesLocationFilter( | ||
| location: Device['location'], | ||
| filter: LocationFilter | ||
| ): boolean { | ||
| if (location == null) return false | ||
| const city = location.city.trim().toLowerCase() | ||
| const region = location.region.trim().toLowerCase() | ||
|
|
||
| return ( | ||
| passes(city, filter.cityInclude, filter.cityExclude) && | ||
| passes(region, filter.regionInclude, filter.regionExclude) | ||
| ) | ||
| } | ||
|
|
||
| function passes(value: string, include: string[], exclude: string[]): boolean { | ||
| if (include.length > 0 && !include.includes(value)) return false | ||
| return !exclude.includes(value) | ||
| } | ||
|
|
||
| /** The parts of a device that decide whether it can be sent to. */ | ||
| export type SendableFields = Pick< | ||
| Device, | ||
| 'apiKey' | 'deviceToken' | 'ignoreMarketing' | ||
| > | ||
|
|
||
| /** | ||
| * Reports why a device cannot be sent to, or undefined if it can. | ||
| * | ||
| * The `targetApiKeys` are the app keys the marketing key may send to. Location | ||
| * queries get their scoping from the Couch view, but a send driven by a | ||
| * caller-supplied list of device ids does not, and the publish daemon resolves | ||
| * Firebase credentials from whatever key the device itself carries. | ||
| * | ||
| * This takes only the fields it reads so that queries can apply it to view | ||
| * rows, and sends to whole documents, without the two drifting apart. | ||
| */ | ||
| export function getDeviceSkipReason( | ||
| device: SendableFields, | ||
| targetApiKeys: ReadonlySet<string> | ||
| ): DeviceSkipReason | undefined { | ||
| const { apiKey, deviceToken, ignoreMarketing } = device | ||
|
|
||
| if (ignoreMarketing) return 'ignore-marketing' | ||
| if ( | ||
| apiKey == null || | ||
| !targetApiKeys.has(apiKey) || | ||
| deviceToken == null || | ||
| deviceToken.trim() === '' | ||
| ) { | ||
| return 'unregistered' | ||
| } | ||
| if (!VALID_TOKEN.test(deviceToken)) return 'invalid-token' | ||
| } |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.