[next] Support external config (#2088)

* fix: correctly determine expired token

* feat: support external config

* fix: lint

* fix: npm audit fix
This commit is contained in:
Kiwi
2018-11-27 18:54:55 +00:00
committed by Wyatt Johnson
parent 50cb46a053
commit 3fcb9a179a
15 changed files with 428 additions and 258 deletions
@@ -109,7 +109,6 @@ it("submits form successfully", async () => {
})
)}.${btoa(
JSON.stringify({
exp: 1540503165,
jti: "31b26591-4e9a-4388-a7ff-e1bdc5d97cce",
})
)}`;
+9
View File
@@ -3,11 +3,13 @@ import qs from "query-string";
import ensureNoEndSlash from "talk-common/utils/ensureNoEndSlash";
import urls from "talk-framework/helpers/urls";
import { ExternalConfig } from "talk-framework/lib/externalConfig";
import {
Decorator,
withAutoHeight,
withClickEvent,
withConfig,
withEventEmitter,
withIOSSafariWidthWorkaround,
withPymStorage,
@@ -28,6 +30,7 @@ export interface StreamEmbedConfig {
eventEmitter: EventEmitter2;
id: string;
rootURL: string;
authToken?: string;
}
export class StreamEmbed {
@@ -97,6 +100,11 @@ export class StreamEmbed {
if (this.pymControl) {
throw new Error("Stream Embed already rendered");
}
const externalConfig: ExternalConfig = {
authToken: this.config.authToken,
};
const streamDecorators: ReadonlyArray<Decorator> = [
withIOSSafariWidthWorkaround,
withAutoHeight,
@@ -105,6 +113,7 @@ export class StreamEmbed {
withEventEmitter(this.config.eventEmitter),
withPymStorage(localStorage, "localStorage"),
withPymStorage(sessionStorage, "sessionStorage"),
withConfig(externalConfig),
];
const query = qs.stringify({
+2
View File
@@ -11,6 +11,7 @@ export interface Config {
id?: string;
autoRender?: boolean;
events?: (eventEmitter: EventEmitter2) => void;
authToken?: string;
}
function getLocationOrigin() {
@@ -56,5 +57,6 @@ export function createStreamEmbed(config: Config): StreamEmbed {
rootURL: config.rootURL || getLocationOrigin(),
autoRender: config.autoRender,
eventEmitter,
authToken: config.authToken,
});
}
@@ -4,6 +4,7 @@ export { default as withClickEvent } from "./withClickEvent";
export { default as withSetCommentID } from "./withSetCommentID";
export { default as withEventEmitter } from "./withEventEmitter";
export { default as withPymStorage } from "./withPymStorage";
export { default as withConfig } from "./withConfig";
export {
default as withIOSSafariWidthWorkaround,
} from "./withIOSSafariWidthWorkaround";
@@ -0,0 +1,19 @@
import sinon from "sinon";
import withConfig from "./withConfig";
it("should emit events from pym to Config", () => {
const config = { authToken: "token" };
const fakePym = {
onMessage: (type: string, callback: () => void) => {
expect(type).toBe("getConfig");
callback();
},
sendMessage: sinon
.stub()
.withArgs("config", JSON.stringify(config))
.returns(null),
};
withConfig(config)(fakePym as any);
expect(fakePym.sendMessage.calledOnce).toBe(true);
});
@@ -0,0 +1,11 @@
import { ExternalConfig } from "talk-framework/lib/externalConfig";
import { Decorator } from "./types";
const withConfig = (config: ExternalConfig): Decorator => pym => {
pym.onMessage("getConfig", () => {
pym.sendMessage("config", JSON.stringify(config));
});
};
export default withConfig;
+1 -1
View File
@@ -1,7 +1,7 @@
{
"extends": "../tsconfig.json",
"compilerOptions": {
"lib": ["dom", "es5"],
"lib": ["dom", "es6"],
"types": ["jest", "node"],
"baseUrl": "./",
"paths": {
@@ -0,0 +1,20 @@
import { Child as PymChild } from "pym.js";
import { areWeInIframe } from "talk-framework/utils";
export interface ExternalConfig {
authToken?: string;
}
export function getExternalConfig(
pym?: PymChild
): Promise<ExternalConfig> | null {
if (pym && areWeInIframe()) {
return new Promise(resolve => {
pym.sendMessage("getConfig", "");
pym.onMessage("config", raw => {
resolve(JSON.parse(raw) as ExternalConfig);
});
});
}
return null;
}
+1 -1
View File
@@ -24,7 +24,7 @@ export function parseJWT(token: string, skewTolerance = 300): JWT {
header,
payload,
get expired() {
return Date.now() - skewTolerance < payload.exp;
return Date.now() / 1000 - skewTolerance >= payload.exp;
},
};
}
@@ -41,9 +41,12 @@ export function setAuthTokenInLocalState(
export async function initLocalBaseState(
environment: Environment,
{ localStorage }: TalkContext
{ localStorage }: TalkContext,
authToken?: string | null
) {
const authToken = await localStorage!.getItem("authToken");
if (authToken === undefined) {
authToken = await localStorage!.getItem("authToken");
}
commitLocalUpdate(environment, s => {
const root = s.getRoot();
@@ -53,7 +56,7 @@ export async function initLocalBaseState(
root.setLinkedRecord(localRecord, "local");
// Set auth token
setAuthTokenInLocalState(authToken, s);
setAuthTokenInLocalState(authToken || null, s);
// Create network Record
const networkRecord = createAndRetain(
@@ -0,0 +1,10 @@
/**
* Returns true if we are in an iframe.
*/
export default function areWeInIframe() {
try {
return window.self !== window.top;
} catch (e) {
return true;
}
}
+1
View File
@@ -1,3 +1,4 @@
export { default as buildURL } from "./buildURL";
export { default as parseURL } from "./parseURL";
export { default as modifyQuery } from "./modifyQuery";
export { default as areWeInIframe } from "./areWeInIframe";
@@ -2,6 +2,7 @@ import qs from "query-string";
import { commitLocalUpdate, Environment } from "relay-runtime";
import { TalkContext } from "talk-framework/lib/bootstrap";
import { getExternalConfig } from "talk-framework/lib/externalConfig";
import { createAndRetain, initLocalBaseState } from "talk-framework/lib/relay";
import { AUTH_POPUP_ID, AUTH_POPUP_TYPE } from "./constants";
@@ -13,7 +14,13 @@ export default async function initLocalState(
environment: Environment,
context: TalkContext
) {
await initLocalBaseState(environment, context);
const config = await getExternalConfig(context.pym);
await initLocalBaseState(
environment,
context,
config ? config.authToken : undefined
);
commitLocalUpdate(environment, s => {
const root = s.getRoot();
const localRecord = root.getLinkedRecord("local")!;