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

feat(branch): add support for test environments #229

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@ import RNBranch

public class BranchAppDelegate: ExpoAppDelegateSubscriber {
public func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool {
if Bundle.main.object(forInfoDictionaryKey: "branch_test_environment") as? Bool ?? false {
RNBranch.useTestInstance()
}

RNBranch.initSession(launchOptions: launchOptions, isReferrable: true)
return true
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,52 +1,65 @@
import { AndroidConfig } from "@expo/config-plugins";
import { resolve } from "path";

import { getBranchApiKey, setBranchApiKey } from "../withBranchAndroid";
import {
setBranchApiKeys,
enableBranchTestEnvironment,
} from "../withBranchAndroid";

const { findMetaDataItem, getMainApplication, readAndroidManifestAsync } =
AndroidConfig.Manifest;

const sampleManifestPath = resolve(
__dirname,
"./fixtures",
"react-native-AndroidManifest.xml",
"react-native-AndroidManifest.xml"
);

describe(getBranchApiKey, () => {
it(`returns null if no android branch api key is provided`, () => {
expect(getBranchApiKey({ android: { config: {} } } as any)).toBe(null);
});

it(`returns apikey if android branch api key is provided`, () => {
expect(
getBranchApiKey({
android: { config: { branch: { apiKey: "MY-API-KEY" } } },
} as any),
).toBe("MY-API-KEY");
});
});

describe(setBranchApiKey, () => {
describe(setBranchApiKeys, () => {
it("sets branch api key in AndroidManifest.xml if given", async () => {
let androidManifestJson =
await readAndroidManifestAsync(sampleManifestPath);
androidManifestJson = await setBranchApiKey(
"MY-API-KEY",
androidManifestJson,
androidManifestJson = await setBranchApiKeys(
{ apiKey: "MY-API-KEY", testApiKey: "MY-TEST-API-KEY" },
androidManifestJson
);
let mainApplication = getMainApplication(androidManifestJson);

expect(
findMetaDataItem(mainApplication, "io.branch.sdk.BranchKey"),
findMetaDataItem(mainApplication, "io.branch.sdk.BranchKey")
).toBeGreaterThan(-1);
expect(
findMetaDataItem(mainApplication, "io.branch.sdk.BranchKey.test")
).toBeGreaterThan(-1);

// Unset the item

androidManifestJson = await setBranchApiKey(null, androidManifestJson);
androidManifestJson = await setBranchApiKeys({}, androidManifestJson);
mainApplication = getMainApplication(androidManifestJson);

expect(findMetaDataItem(mainApplication, "io.branch.sdk.BranchKey")).toBe(
-1,
-1
);
expect(
findMetaDataItem(mainApplication, "io.branch.sdk.BranchKey.test")
).toBe(-1);
});
});

describe(enableBranchTestEnvironment, () => {
it("sets branch test mode meta data item in AndroidManifest.xml", async () => {
let androidManifestJson =
await readAndroidManifestAsync(sampleManifestPath);

androidManifestJson = await enableBranchTestEnvironment(
true,
androidManifestJson
);

const mainApplication = getMainApplication(androidManifestJson);

expect(
findMetaDataItem(mainApplication, "io.branch.sdk.TestMode")
).toBeGreaterThan(-1);
});
});
41 changes: 25 additions & 16 deletions packages/react-native-branch/src/__tests__/withBranchIOS-test.ts
Original file line number Diff line number Diff line change
@@ -1,27 +1,36 @@
import { getBranchApiKey, setBranchApiKey } from "../withBranchIOS";
import {
setBranchApiKeys,
enableBranchTestEnvironment,
} from "../withBranchIOS";

describe(getBranchApiKey, () => {
it(`returns null if no api key is provided`, () => {
expect(getBranchApiKey({})).toBe(null);
describe(setBranchApiKeys, () => {
it(`sets branch_key.live if the api key is given`, () => {
expect(setBranchApiKeys({ apiKey: "LIVE-API-KEY" }, {})).toMatchObject({
branch_key: {
live: "LIVE-API-KEY",
},
});
});

it(`returns the api key if provided`, () => {
it(`sets branch_key.test if the test api key is given`, () => {
expect(
getBranchApiKey({ ios: { config: { branch: { apiKey: "123" } } } }),
).toBe("123");
});
});

describe(setBranchApiKey, () => {
it(`sets branch_key.live if the api key is given`, () => {
expect(setBranchApiKey("123", {})).toMatchObject({
setBranchApiKeys(
{ apiKey: "LIVE-API-KEY", testApiKey: "TEST-API-KEY" },
{}
)
).toMatchObject({
branch_key: {
live: "123",
live: "LIVE-API-KEY",
test: "TEST-API-KEY",
},
});
});
});

it(`makes no changes to the infoPlist no api key is provided`, () => {
expect(setBranchApiKey(null, {})).toMatchObject({});
describe(enableBranchTestEnvironment, () => {
it(`must assign the passed boolean value into branch_key.branch_test_mode`, () => {
expect(enableBranchTestEnvironment(true, {})).toMatchObject({
branch_test_environment: true,
});
});
});
7 changes: 7 additions & 0 deletions packages/react-native-branch/src/types.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
export type ConfigData = {
apiKey?: string;
testApiKey?: string;
iosAppDomain?: string;
iosUniversalLinkDomains?: string[];
enableTestEnvironment?: boolean;
};

export type BranchKeys = {
apiKey: string;
testApiKey?: string;
};
15 changes: 5 additions & 10 deletions packages/react-native-branch/src/withBranch.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,22 +4,17 @@ import { ConfigData } from "./types";
import { withBranchAndroid } from "./withBranchAndroid";
import { withBranchIOS } from "./withBranchIOS";

const withBranch: ConfigPlugin<ConfigData> = (
config,
{ apiKey, iosAppDomain, iosUniversalLinkDomains } = {},
) => {
config = withBranchAndroid(config, { apiKey });
config = withBranchIOS(config, {
apiKey,
iosAppDomain,
iosUniversalLinkDomains,
});
const withBranch: ConfigPlugin<ConfigData> = (config, branchConfig = {}) => {
config = withBranchAndroid(config, branchConfig);
config = withBranchIOS(config, branchConfig);

return config;
};

let pkg: { name: string; version?: string } = {
name: "react-native-branch",
};

try {
const branchPkg = require("react-native-branch/package.json");
pkg = branchPkg;
Expand Down
72 changes: 55 additions & 17 deletions packages/react-native-branch/src/withBranchAndroid.ts
Original file line number Diff line number Diff line change
@@ -1,52 +1,90 @@
import { ExpoConfig } from "expo/config";
import {
AndroidConfig,
ConfigPlugin,
withAndroidManifest,
} from "expo/config-plugins";

import { BranchKeys, ConfigData } from "./types";

const {
addMetaDataItemToMainApplication,
getMainApplicationOrThrow,
removeMetaDataItemFromMainApplication,
} = AndroidConfig.Manifest;

const META_BRANCH_KEY = "io.branch.sdk.BranchKey";
const META_BRANCH_KEY_TEST = "io.branch.sdk.BranchKey.test";
const META_BRANCH_KEY_TEST_MODE = "io.branch.sdk.TestMode";

export function getBranchApiKey(config: ExpoConfig) {
return config.android?.config?.branch?.apiKey ?? null;
}

export function setBranchApiKey(
apiKey: string,
androidManifest: AndroidConfig.Manifest.AndroidManifest,
export function setBranchApiKeys(
{ apiKey, testApiKey }: BranchKeys,
androidManifest: AndroidConfig.Manifest.AndroidManifest
) {
const mainApplication = getMainApplicationOrThrow(androidManifest);

if (apiKey) {
// If the item exists, add it back
addMetaDataItemToMainApplication(mainApplication, META_BRANCH_KEY, apiKey);
} else {
// Remove any existing item
removeMetaDataItemFromMainApplication(mainApplication, META_BRANCH_KEY);
}

if (testApiKey) {
addMetaDataItemToMainApplication(
mainApplication,
META_BRANCH_KEY_TEST,
testApiKey
);
} else {
removeMetaDataItemFromMainApplication(
mainApplication,
META_BRANCH_KEY_TEST
);
}

return androidManifest;
}

export function enableBranchTestEnvironment(
enableTestEnvironment: boolean,
androidManifest: AndroidConfig.Manifest.AndroidManifest
) {
const mainApplication = getMainApplicationOrThrow(androidManifest);

addMetaDataItemToMainApplication(
mainApplication,
META_BRANCH_KEY_TEST_MODE,
`${enableTestEnvironment}`
);

return androidManifest;
}

export const withBranchAndroid: ConfigPlugin<{ apiKey?: string }> = (
config,
data,
) => {
const apiKey = data.apiKey ?? getBranchApiKey(config);
export const withBranchAndroid: ConfigPlugin<ConfigData> = (config, data) => {
const apiKey = data.apiKey ?? config.android?.config?.branch?.apiKey ?? null;
const testApiKey =
data.testApiKey ?? config.android?.config?.branch?.testApiKey ?? null;
const enableTestEnvironment =
data.enableTestEnvironment ??
config.android?.config?.branch?.enableTestEnvironment ??
null;

if (!apiKey) {
throw new Error(
"Branch API key is required: expo.android.config.branch.apiKey",
"Branch API key is required: expo.android.config.branch.apiKey"
);
}

config = withAndroidManifest(config, (config) => {
config.modResults = setBranchApiKey(apiKey, config.modResults);
config.modResults = setBranchApiKeys(
{ apiKey, testApiKey },
config.modResults
);

config.modResults = enableBranchTestEnvironment(
enableTestEnvironment,
config.modResults
);

return config;
});

Expand Down
52 changes: 36 additions & 16 deletions packages/react-native-branch/src/withBranchIOS.ts
Original file line number Diff line number Diff line change
@@ -1,55 +1,75 @@
import { ExpoConfig } from "expo/config";
import { ConfigPlugin, InfoPlist, withInfoPlist } from "expo/config-plugins";

import { ConfigData } from "./types";
import { BranchKeys, ConfigData } from "./types";

export function getBranchApiKey(config: Pick<ExpoConfig, "ios">) {
return config.ios?.config?.branch?.apiKey ?? null;
}

export function setBranchApiKey(
apiKey: string | null,
infoPlist: InfoPlist,
export function setBranchApiKeys(
{ apiKey, testApiKey }: BranchKeys,
infoPlist: InfoPlist
): InfoPlist {
if (apiKey === null) {
return infoPlist;
}

return {
...infoPlist,
branch_key: {
live: apiKey,
...(testApiKey && { test: testApiKey }),
},
};
}

export function enableBranchTestEnvironment(
enableTestEnvironment: boolean,
infoPlist: InfoPlist
) {
return {
...infoPlist,
branch_test_environment: enableTestEnvironment,
};
}

export const withBranchIOS: ConfigPlugin<ConfigData> = (config, data) => {
// Ensure object exist
if (!config.ios) {
config.ios = {};
}

const apiKey = data.apiKey ?? getBranchApiKey(config);
const apiKey = data.apiKey ?? config.ios?.config?.branch?.apiKey ?? null;
const testApiKey =
data.testApiKey ?? config.ios?.config?.branch?.testApiKey ?? null;
const enableTestEnvironment =
data.enableTestEnvironment ??
config.android?.config?.branch?.enableTestEnvironment ??
false;

if (!apiKey) {
throw new Error(
"Branch API key is required: expo.ios.config.branch.apiKey",
"Branch API key is required: expo.ios.config.branch.apiKey"
);
}

// Update the infoPlist with the branch key and branch domain
config = withInfoPlist(config, (config) => {
config.modResults = setBranchApiKey(apiKey, config.modResults);
config.modResults = setBranchApiKeys(
{ apiKey, testApiKey },
config.modResults
);

config.modResults = enableBranchTestEnvironment(
enableTestEnvironment,
config.modResults
);

if (data.iosAppDomain) {
config.modResults.branch_app_domain = data.iosAppDomain;
} else {
delete config.modResults.branch_app_domain;
}

if (data.iosUniversalLinkDomains) {
config.modResults.branch_universal_link_domains =
data.iosUniversalLinkDomains;
} else {
delete config.modResults.branch_universal_link_domains;
}

return config;
});

Expand Down