Skip to content

Commit

Permalink
Merge pull request #797 from OneSignal/cleanup/secondary_channel_manager
Browse files Browse the repository at this point in the history
Added SecondaryChannelManager to handle Email and future channel player updates
  • Loading branch information
jkasten2 authored Apr 23, 2021
2 parents 9f5c17f + 874e689 commit 31bf1be
Show file tree
Hide file tree
Showing 45 changed files with 1,264 additions and 283 deletions.
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -130,12 +130,12 @@
},
{
"path": "./build/bundles/OneSignalPageSDKES6.js",
"maxSize": "68 kB",
"maxSize": "72 kB",
"compression": "gzip"
},
{
"path": "./build/bundles/OneSignalSDKWorker.js",
"maxSize": "38 kB",
"maxSize": "42 kB",
"compression": "gzip"
},
{
Expand Down
128 changes: 14 additions & 114 deletions src/OneSignal.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ import { ValidatorUtils } from './utils/ValidatorUtils';
import { DeviceRecord } from './models/DeviceRecord';
import TimedLocalStorage from './modules/TimedLocalStorage';
import { EmailProfile } from './models/EmailProfile';
import { EmailDeviceRecord } from './models/EmailDeviceRecord';
import { SecondaryChannelDeviceRecord } from './models/SecondaryChannelDeviceRecord';
import Emitter, { EventHandler } from './libraries/Emitter';
import Log from './libraries/Log';
import ConfigManager from "./managers/ConfigManager";
Expand All @@ -58,6 +58,8 @@ import OutcomesHelper from "./helpers/shared/OutcomesHelper";
import { OutcomeAttributionType } from "./models/Outcomes";
import { AppUserConfigNotifyButton, DelayedPromptType } from './models/Prompts';
import LocalStorage from './utils/LocalStorage';
import { AuthHashOptionsValidatorHelper } from './helpers/page/AuthHashOptionsValidatorHelper';
import { TagsObject } from './models/Tags';

export default class OneSignal {
/**
Expand Down Expand Up @@ -99,113 +101,23 @@ export default class OneSignal {
throw new InvalidArgumentError('email', InvalidArgumentReason.Malformed);
}

const isIdentifierAuthHashDefined = options && !!options.identifierAuthHash;
const isEmailAuthHashDefined = options && !!options.emailAuthHash;

const authHash = isIdentifierAuthHashDefined ? options.identifierAuthHash :
(isEmailAuthHashDefined ? options.emailAuthHash : undefined);

if (!!authHash) {
if (isIdentifierAuthHashDefined && isEmailAuthHashDefined) {
Log.error("Both `emailAuthHash` and `identifierAuthHash` provided.");
throw new InvalidArgumentError('options', InvalidArgumentReason.Malformed);
}
// identifierAuthHash / emailAuthHash are expected to be a 64 character SHA-256 hex hash
const isIdentifierAuthHashMalformed = isIdentifierAuthHashDefined && options.identifierAuthHash.length !== 64;
const isEmailAuthHashIsMalformed = isEmailAuthHashDefined && options.emailAuthHash.length !== 64;

if ( isIdentifierAuthHashMalformed ) {
throw new InvalidArgumentError('options.identifierAuthHash', InvalidArgumentReason.Malformed);
}

if ( isEmailAuthHashIsMalformed ) {
throw new InvalidArgumentError('options.emailAuthHash', InvalidArgumentReason.Malformed);
}
}
const authHash = AuthHashOptionsValidatorHelper.throwIfInvalidAuthHashOptions(
options,
["identifierAuthHash", "emailAuthHash"]
);

logMethodCall('setEmail', email, options);
await awaitOneSignalInitAndSupported();

const appConfig = await Database.getAppConfig();
const { deviceId } = await Database.getSubscription();
const existingEmailProfile = await Database.getEmailProfile();

const newEmailProfile = new EmailProfile(existingEmailProfile.emailId, email, authHash);
const isExistingEmailSaved = !!existingEmailProfile.emailId;

if (isExistingEmailSaved) {
// If we already have a saved email player ID, make a PUT call to update the existing email record
newEmailProfile.emailId = await OneSignalApi.updateEmailRecord(
appConfig,
newEmailProfile,
deviceId
);
} else {
// Otherwise, make a POST call to create a new email record
newEmailProfile.emailId = await OneSignalApi.createEmailRecord(
appConfig,
newEmailProfile,
deviceId
);
}

// If we are subscribed to web push
const isExistingPushRecordSaved = deviceId;
// And if we previously saved an email ID and it's different from the new returned ID
const emailPreviouslySavedAndDifferent = !isExistingEmailSaved ||
existingEmailProfile.emailId !== newEmailProfile.emailId;
// Or if we previously saved an email and the email changed
const emailPreviouslySavedAndChanged = !existingEmailProfile.emailAddress ||
newEmailProfile.emailAddress !== existingEmailProfile.emailAddress;

if (!!deviceId && isExistingPushRecordSaved && (emailPreviouslySavedAndDifferent || emailPreviouslySavedAndChanged))
{
const authHash = await OneSignal.database.getExternalUserIdAuthHash();
// Then update the push device record with a reference to the new email ID and email address
await OneSignalApi.updatePlayer(
appConfig.appId,
deviceId,
{
parent_player_id: newEmailProfile.emailId,
email: newEmailProfile.emailAddress,
external_user_id_auth_hash: authHash
}
);
}

// email record update / create call returned successfully
if (!!newEmailProfile.emailId) {
await Database.setEmailProfile(newEmailProfile);
}
return newEmailProfile.emailId;
return await this.context.secondaryChannelManager.email.setIdentifier(email, authHash);
}

/**
* @PublicApi
*/
static async logoutEmail() {
await awaitOneSignalInitAndSupported();

const appConfig = await Database.getAppConfig();
const emailProfile = await Database.getEmailProfile();
const { deviceId } = await Database.getSubscription();

if (!emailProfile.emailId) {
Log.warn(new NotSubscribedError(NotSubscribedReason.NoEmailSet));
return;
}

if (!deviceId) {
Log.warn(new NotSubscribedError(NotSubscribedReason.NoDeviceId));
return;
}

if (!await OneSignalApi.logoutEmail(appConfig, emailProfile, deviceId)) {
Log.warn("Failed to logout email.");
return;
}

await Database.setEmailProfile(new EmailProfile());
return await this.context.secondaryChannelManager.email.logout();
}

/**
Expand Down Expand Up @@ -488,7 +400,7 @@ export default class OneSignal {
/**
* @PublicApi
*/
static async sendTags(tags: {[key: string]: any}, callback?: Action<Object>): Promise<Object | null> {
static async sendTags(tags: TagsObject<any>, callback?: Action<Object>): Promise<Object | null> {
await awaitOneSignalInitAndSupported();
logMethodCall('sendTags', tags, callback);
if (!tags || Object.keys(tags).length === 0) {
Expand All @@ -503,15 +415,7 @@ export default class OneSignal {
});
const { appId } = await Database.getAppConfig();

const emailProfile = await Database.getEmailProfile();
if (emailProfile.emailId) {
const emailOptions : UpdatePlayerOptions = {
tags,
identifier_auth_hash: emailProfile.identifierAuthHash
};

await OneSignalApi.updatePlayer(appId, emailProfile.emailId, emailOptions);
}
this.context.secondaryChannelManager.synchronizer.setTags(tags);

const { deviceId } = await Database.getSubscription();
if (!deviceId) {
Expand Down Expand Up @@ -569,11 +473,7 @@ export default class OneSignal {
await awaitOneSignalInitAndSupported();
logMethodCall("setExternalUserId");

if (!!authHash) {
if ( authHash.length !== 64 ) {
throw new InvalidArgumentError('options.identifierAuthHash', InvalidArgumentReason.Malformed);
}
}
AuthHashOptionsValidatorHelper.throwIfInvalidAuthHash(authHash, "authHash");

const isExistingUser = await this.context.subscriptionManager.isAlreadyRegisteredWithOneSignal();
if (!isExistingUser) {
Expand Down Expand Up @@ -738,7 +638,7 @@ export default class OneSignal {
await awaitOneSignalInitAndSupported();
logMethodCall('getEmailId', callback);
const emailProfile = await Database.getEmailProfile();
const emailId = emailProfile.emailId;
const emailId = emailProfile.subscriptionId;
executeCallback(callback, emailId);
return emailId;
}
Expand Down Expand Up @@ -999,7 +899,7 @@ export default class OneSignal {
static context: Context;
static checkAndWipeUserSubscription = function () { }
static DeviceRecord = DeviceRecord;
static EmailDeviceRecord = EmailDeviceRecord;
static SecondaryChannelDeviceRecord = SecondaryChannelDeviceRecord;

static notificationPermission = NotificationPermission;

Expand Down
18 changes: 9 additions & 9 deletions src/OneSignalApi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,11 @@ import SdkEnvironment from "./managers/SdkEnvironment";
import { AppConfig, ServerAppConfig } from './models/AppConfig';
import { DeviceRecord } from './models/DeviceRecord';
import { WindowEnvironmentKind } from './models/WindowEnvironmentKind';
import { EmailProfile } from './models/EmailProfile';
import { SecondaryChannelProfile } from './models/SecondaryChannelProfile';
import OneSignalApiSW from "./OneSignalApiSW";
import OneSignalApiShared from "./OneSignalApiShared";
import { UpdatePlayerOptions } from './models/UpdatePlayerOptions';
import { EmailProfile } from './models/EmailProfile';

export default class OneSignalApi {
static getPlayer(appId: string, playerId: string) {
Expand Down Expand Up @@ -49,20 +50,19 @@ export default class OneSignalApi {
return await OneSignalApiShared.createUser(deviceRecord);
}

static async createEmailRecord(
static async createSecondaryChannelRecord(
appConfig: AppConfig,
emailProfile: EmailProfile,
profile: SecondaryChannelProfile,
pushDeviceRecordId?: string
): Promise<string | null> {
return await OneSignalApiShared.createEmailRecord(appConfig, emailProfile, pushDeviceRecordId);
return await OneSignalApiShared.createSecondaryChannelRecord(appConfig, profile, pushDeviceRecordId);
}

static async updateEmailRecord(
static async updateSecondaryChannelRecord(
appConfig: AppConfig,
emailProfile: EmailProfile,
pushDeviceRecordId?: string
): Promise<string | null> {
return await OneSignalApiShared.updateEmailRecord(appConfig, emailProfile, pushDeviceRecordId);
profile: SecondaryChannelProfile
): Promise<void> {
await OneSignalApiShared.updateSecondaryChannelRecord(appConfig, profile);
}

static async logoutEmail(appConfig: AppConfig, emailProfile: EmailProfile, deviceId: string): Promise<boolean> {
Expand Down
51 changes: 25 additions & 26 deletions src/OneSignalApiShared.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
import { AppConfig } from './models/AppConfig';
import { DeviceRecord } from './models/DeviceRecord';
import { OneSignalApiErrorKind, OneSignalApiError } from './errors/OneSignalApiError';
import { EmailProfile } from './models/EmailProfile';
import { EmailDeviceRecord } from './models/EmailDeviceRecord';
import { SecondaryChannelProfile } from './models/SecondaryChannelProfile';
import { SecondaryChannelDeviceRecord } from './models/SecondaryChannelDeviceRecord';
import { OutcomeRequestData } from "./models/OutcomeRequestData";
import OneSignalApiBase from "./OneSignalApiBase";
import Utils from "./context/shared/utils/Utils";
import Log from "./libraries/Log";
import { UpdatePlayerOptions } from './models/UpdatePlayerOptions';
import { EmailProfile } from './models/EmailProfile';

export default class OneSignalApiShared {
static getPlayer(appId: string, playerId: string) {
Expand Down Expand Up @@ -54,57 +55,55 @@ export default class OneSignalApiShared {
return null;
}

static async createEmailRecord(
static async createSecondaryChannelRecord(
appConfig: AppConfig,
emailProfile: EmailProfile,
profile: SecondaryChannelProfile,
pushDeviceRecordId?: string
): Promise<string | null> {
Utils.enforceAppId(appConfig.appId);

const emailRecord = new EmailDeviceRecord(
emailProfile.emailAddress,
emailProfile.identifierAuthHash,
const secondaryChannelRecord = new SecondaryChannelDeviceRecord(
profile.identifier,
profile.identifierAuthHash,
pushDeviceRecordId
);

emailRecord.appId = appConfig.appId;
const response = await OneSignalApiBase.post(`players`, emailRecord.serialize());
secondaryChannelRecord.appId = appConfig.appId;
const response = await OneSignalApiBase.post(`players`, secondaryChannelRecord.serialize());
if (response && response.success) {
return response.id;
} else {
return null;
}
}

static async updateEmailRecord(
/**
* Make a PUT call to update the secondary channel's identifier.
* @param {AppConfig} appConfig - This contains an appId which will be included
* @param {SecondaryChannelProfile} profile - This the profile we will be using fields from to include in the update
*/
static async updateSecondaryChannelRecord(
appConfig: AppConfig,
emailProfile: EmailProfile,
pushDeviceRecordId?: string
): Promise<string | null> {
profile: SecondaryChannelProfile
): Promise<void> {
Utils.enforceAppId(appConfig.appId);
Utils.enforcePlayerId(emailProfile.emailId);
Utils.enforcePlayerId(profile.subscriptionId);

const emailRecord = new EmailDeviceRecord(
emailProfile.emailAddress,
emailProfile.identifierAuthHash,
pushDeviceRecordId
const secondaryChannelRecord = new SecondaryChannelDeviceRecord(
profile.identifier,
profile.identifierAuthHash
);

emailRecord.appId = appConfig.appId;
const response = await OneSignalApiBase.put(`players/${emailProfile.emailId}`, emailRecord.serialize());
if (response && response.success) {
return response.id;
} else {
return null;
}
secondaryChannelRecord.appId = appConfig.appId;
await OneSignalApiBase.put(`players/${profile.subscriptionId}`, secondaryChannelRecord.serialize());
}

static async logoutEmail(appConfig: AppConfig, emailProfile: EmailProfile, deviceId: string): Promise<boolean> {
Utils.enforceAppId(appConfig.appId);
Utils.enforcePlayerId(deviceId);
const response = await OneSignalApiBase.post(`players/${deviceId}/email_logout`, {
app_id: appConfig.appId,
parent_player_id: emailProfile.emailId,
parent_player_id: emailProfile.subscriptionId,
identifier_auth_hash: emailProfile.identifierAuthHash ? emailProfile.identifierAuthHash : undefined
});
if (response && response.success) {
Expand Down
18 changes: 1 addition & 17 deletions src/helpers/InitHelper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import OneSignalApiShared from '../OneSignalApiShared';
import { ContextInterface } from '../models/Context';
import { WorkerMessengerCommand } from '../libraries/WorkerMessenger';
import { DynamicResourceLoader } from '../services/DynamicResourceLoader';
import { EmailDeviceRecord } from '../models/EmailDeviceRecord';
import { SecondaryChannelDeviceRecord } from '../models/SecondaryChannelDeviceRecord';
import { SubscriptionStrategyKind } from "../models/SubscriptionStrategyKind";
import { IntegrationKind } from '../models/IntegrationKind';
import { Subscription } from "../models/Subscription";
Expand Down Expand Up @@ -359,22 +359,6 @@ export default class InitHelper {
}
}

public static async updateEmailSessionCount() {
const context: ContextInterface = OneSignal.context;
/* Both HTTP and HTTPS pages can update email session by API request without origin/push feature restrictions */
if (context.pageViewManager.isFirstPageView()) {
const emailProfile = await Database.getEmailProfile();
if (emailProfile.emailId) {
const emailDeviceRecord = new EmailDeviceRecord(emailProfile.emailAddress, emailProfile.identifierAuthHash);
emailDeviceRecord.appId = context.appConfig.appId;
await OneSignalApiShared.updateUserSession(
emailProfile.emailId,
emailDeviceRecord
);
}
}
}

protected static async showPromptsFromWebConfigEditor() {
const config: AppConfig = OneSignal.config;
if (config.userConfig.promptOptions) {
Expand Down
Loading

0 comments on commit 31bf1be

Please sign in to comment.