[CORL-420] Upgrade Relay (#2346)

* chore: upgrade Relay

* fix: fix errors

* fix: snapshot

* fix: relay prefix

* fix: fragment spec error
This commit is contained in:
Vinh
2019-06-07 23:42:26 +02:00
committed by Wyatt Johnson
parent ed4e5fa2a8
commit d4b99a2a57
38 changed files with 682 additions and 929 deletions
@@ -9,7 +9,7 @@ interface Props {
}
const NavigationLink: FunctionComponent<Props> = props => (
<Link to={props.to} Component={AppBarNavigationItem} activePropName="active">
<Link to={props.to} as={AppBarNavigationItem} activePropName="active">
{props.children}
</Link>
);
@@ -1,11 +1,11 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`renders correctly 1`] = `
<Link
Component={[Function]}
<ForwardRef(render)
activePropName="active"
as={[Function]}
to="/moderate"
>
link
</Link>
</ForwardRef(render)>
`;
@@ -5,6 +5,7 @@ import { AuthCheckContainerQueryResponse } from "coral-admin/__generated__/AuthC
import { SetRedirectPathMutation } from "coral-admin/mutations";
import { AbilityType, can } from "coral-admin/permissions";
import RestrictedContainer from "coral-admin/views/restricted/containers/RestrictedContainer";
import { roleIsAtLeast } from "coral-framework/helpers";
import { graphql, MutationProp, withMutation } from "coral-framework/lib/relay";
import { withRouteConfig } from "coral-framework/lib/router";
import { GQLUSER_ROLE } from "coral-framework/schema";
@@ -13,114 +14,115 @@ interface Props {
match: Match;
router: Router;
setRedirectPath: MutationProp<typeof SetRedirectPathMutation>;
data:
| AuthCheckContainerQueryResponse & {
route: {
// An AbilityType can be passed in as the Route data
// to perform a permission check.
data?: AbilityType;
};
}
| null;
data: AuthCheckContainerQueryResponse;
}
class AuthCheckContainer extends React.Component<Props> {
private wasLoggedIn = false;
constructor(props: Props) {
super(props);
this.redirectIfNotLoggedIn();
}
public componentWillReceiveProps(nextProps: Props) {
if (nextProps.data && nextProps.data.viewer) {
this.wasLoggedIn = true;
type CheckParams =
| {
role: GQLUSER_ROLE;
ability?: AbilityType;
}
this.redirectIfNotLoggedIn(nextProps, this.props);
if (nextProps.data && !nextProps.data.viewer) {
this.wasLoggedIn = false;
}
}
| {
role?: GQLUSER_ROLE;
ability: AbilityType;
};
private shouldRedirectTo(props: Props = this.props) {
if (!props.data || props.data.viewer) {
return false;
function createAuthCheckContainer(check: CheckParams) {
class AuthCheckContainer extends React.Component<Props> {
private wasLoggedIn = false;
constructor(props: Props) {
super(props);
this.redirectIfNotLoggedIn();
}
return true;
}
private hasAccess(props: Props = this.props) {
const { viewer } = props.data!;
if (viewer) {
if (
viewer.role === GQLUSER_ROLE.COMMENTER ||
viewer.role === GQLUSER_ROLE.STAFF ||
(props.data &&
props.data.route.data &&
// Perform permission check on the ability passed in by the route data
!can(viewer, props.data.route.data))
) {
public componentWillReceiveProps(nextProps: Props) {
if (nextProps.data && nextProps.data.viewer) {
this.wasLoggedIn = true;
}
this.redirectIfNotLoggedIn(nextProps, this.props);
if (nextProps.data && !nextProps.data.viewer) {
this.wasLoggedIn = false;
}
}
private shouldRedirectTo(props: Props = this.props) {
if (!props.data || props.data.viewer) {
return false;
}
return true;
}
return false;
}
private async redirectIfNotLoggedIn(
props: Props = this.props,
prevProps: Props | null = null
) {
if (!this.shouldRedirectTo(props)) {
return;
}
// If I was previously logged in then logged out, we don't need to set the redirect path.
if (!this.wasLoggedIn) {
const location = props.match.location;
await props.setRedirectPath({
path: location.pathname + location.search + location.hash,
});
}
props.router.replace("/admin/login");
}
public render() {
if (!this.props.data || this.shouldRedirectTo()) {
return null;
}
if (this.hasAccess()) {
return this.props.children;
}
return <RestrictedContainer viewer={this.props.data.viewer!} />;
}
}
const enhanced = withRouteConfig<Props>({
query: graphql`
query AuthCheckContainerQuery {
viewer {
...RestrictedContainer_viewer
username
email
profiles {
__typename
private hasAccess(props: Props = this.props) {
const { viewer } = props.data!;
if (viewer) {
if (
(check.role && !roleIsAtLeast(viewer.role, check.role)) ||
(check.ability && !can(viewer, check.ability))
) {
return false;
}
role
return true;
}
settings {
auth {
integrations {
local {
enabled
targetFilter {
admin
stream
return false;
}
private async redirectIfNotLoggedIn(
props: Props = this.props,
prevProps: Props | null = null
) {
if (!this.shouldRedirectTo(props)) {
return;
}
// If I was previously logged in then logged out, we don't need to set the redirect path.
if (!this.wasLoggedIn) {
const location = props.match.location;
await props.setRedirectPath({
path: location.pathname + location.search + location.hash,
});
}
props.router.replace("/admin/login");
}
public render() {
if (!this.props.data || this.shouldRedirectTo()) {
return null;
}
if (this.hasAccess()) {
return this.props.children;
}
return <RestrictedContainer viewer={this.props.data.viewer!} />;
}
}
const enhanced = withRouteConfig<Props>({
query: graphql`
query AuthCheckContainerQuery {
viewer {
...RestrictedContainer_viewer
username
email
profiles {
__typename
}
role
}
settings {
auth {
integrations {
local {
enabled
targetFilter {
admin
stream
}
}
}
}
}
}
}
`,
})(withRouter(withMutation(SetRedirectPathMutation)(AuthCheckContainer)));
`,
})(withRouter(withMutation(SetRedirectPathMutation)(AuthCheckContainer)));
return enhanced;
}
export default enhanced;
export default createAuthCheckContainer;
@@ -47,18 +47,12 @@ const ApproveCommentMutation = createMutation(
input: {
commentID: input.commentID,
commentRevisionID: input.commentRevisionID,
clientMutationId: clientMutationId.toString(),
},
},
optimisticResponse: {
approveComment: {
comment: {
id: input.commentID,
status: "APPROVED",
},
clientMutationId: (clientMutationId++).toString(),
},
},
optimisticUpdater: store => {
store.get(input.commentID)!.setValue("APPROVED", "status");
},
updater: store => {
const connections = [
getQueueConnection(store, "reported", input.storyID),
@@ -46,7 +46,7 @@ const BanUserMutation = createMutation(
current: lookup<GQLUser>(
environment,
input.userID
)!.status.current.concat([GQLUSER_STATUS.BANNED]),
)!.status.current.concat(GQLUSER_STATUS.BANNED),
ban: {
active: true,
},
@@ -47,18 +47,12 @@ const RejectCommentMutation = createMutation(
input: {
commentID: input.commentID,
commentRevisionID: input.commentRevisionID,
clientMutationId: clientMutationId.toString(),
},
},
optimisticResponse: {
rejectComment: {
comment: {
id: input.commentID,
status: "REJECTED",
},
clientMutationId: (clientMutationId++).toString(),
},
},
optimisticUpdater: store => {
store.get(input.commentID)!.setValue("REJECTED", "status");
},
updater: store => {
const connections = [
getQueueConnection(store, "reported", input.storyID),
+9 -4
View File
@@ -1,8 +1,9 @@
import { makeRouteConfig, Redirect, Route } from "found";
import React from "react";
import { GQLUSER_ROLE } from "coral-framework/schema";
import AppContainer from "./containers/AppContainer";
import AuthCheckContainer from "./containers/AuthCheckContainer";
import createAuthCheckContainer from "./containers/AuthCheckContainer";
import { Ability } from "./permissions";
import CommunityContainer from "./routes/community/containers/CommunityContainer";
import ConfigureContainer from "./routes/configure/containers/ConfigureContainer";
@@ -26,7 +27,10 @@ import StoriesContainer from "./routes/stories/containers/StoriesContainer";
export default makeRouteConfig(
<Route path="admin">
<Route {...AuthCheckContainer.routeConfig}>
<Route
{...createAuthCheckContainer({ role: GQLUSER_ROLE.MODERATOR })
.routeConfig}
>
<Route {...AppContainer.routeConfig}>
<Redirect from="/" to="/admin/moderate" />
<Route
@@ -64,8 +68,9 @@ export default makeRouteConfig(
<Route path="community" {...CommunityContainer.routeConfig} />
<Route path="stories" Component={Stories} />
<Route
{...AuthCheckContainer.routeConfig}
data={Ability.CHANGE_CONFIGURATION}
{...createAuthCheckContainer({
ability: Ability.CHANGE_CONFIGURATION,
}).routeConfig}
>
<Route path="configure" Component={ConfigureContainer}>
<Redirect from="/" to="/admin/configure/general" />
@@ -4,12 +4,12 @@ exports[`renders correctly 1`] = `
<li
className="customClassName"
>
<Link
<ForwardRef(render)
activeClassName="Link-linkActive"
className="Link-link"
to="/admin"
>
child
</Link>
</ForwardRef(render)>
</li>
`;
@@ -1,4 +1,4 @@
import { withRouter, WithRouter } from "found";
import { RouterState, withRouter } from "found";
import * as React from "react";
import { Component } from "react";
@@ -25,7 +25,7 @@ type Props = {
auth: AuthData;
viewer: UserData | null;
setRedirectPath: MutationProp<typeof SetRedirectPathMutation>;
} & WithRouter;
} & RouterState;
function handleAccountCompletion(props: Props) {
const {
@@ -9,7 +9,7 @@ interface Props {
}
const NavigationLink: FunctionComponent<Props> = props => (
<Link to={props.to} Component={SubBarNavigationItem} activePropName="active">
<Link to={props.to} as={SubBarNavigationItem} activePropName="active">
{props.children}
</Link>
);
@@ -4,7 +4,7 @@ exports[`renders correctly 1`] = `
<div
data-testid="moderate-container"
>
<withRouter(Relay(ModerateSearchBarContainer))
<ForwardRef(render)
allStories={true}
story={Object {}}
/>
@@ -1,11 +1,11 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`renders correctly 1`] = `
<Link
Component={[Function]}
<ForwardRef(render)
activePropName="active"
as={[Function]}
to="/moderate"
>
link
</Link>
</ForwardRef(render)>
`;
@@ -12,12 +12,12 @@ exports[`renders correctly 1`] = `
<Localized
id="moderate-single-goToModerationQueues"
>
<Link
<ForwardRef(render)
className="SingleModerate-subBarBegin"
to="/admin/moderate/"
>
Go to moderation queues
</Link>
</ForwardRef(render)>
</Localized>
<Localized
id="moderate-single-singleCommentView"
@@ -20,11 +20,12 @@ export class FlagDetailsContainer extends React.Component<Props> {
const nodes = this.props.comment.flags.nodes;
const offensiveList: React.ReactElement[] = [];
const spamList: React.ReactElement[] = [];
nodes.forEach(n => {
nodes.forEach((n, i) => {
switch (n.reason) {
case GQLCOMMENT_FLAG_REASON.COMMENT_REPORTED_OFFENSIVE:
offensiveList.push(
<FlagDetailsEntry
key={i}
user={n.flagger ? n.flagger.username : <NotAvailable />}
details={n.additionalDetails}
/>
@@ -33,6 +34,7 @@ export class FlagDetailsContainer extends React.Component<Props> {
case GQLCOMMENT_FLAG_REASON.COMMENT_REPORTED_SPAM:
spamList.push(
<FlagDetailsEntry
key={i}
user={n.flagger ? n.flagger.username : <NotAvailable />}
details={n.additionalDetails}
/>
@@ -23,10 +23,7 @@ interface Props {
const UserRow: FunctionComponent<Props> = props => (
<TableRow>
<TableCell className={styles.titleColumn}>
<Link
to={getModerationLink("default", props.storyID)}
Component={TextLink}
>
<Link to={getModerationLink("default", props.storyID)} as={TextLink}>
{props.title || <NotAvailable />}
</Link>
</TableCell>
@@ -14,7 +14,7 @@ interface Props {
const GoToCommentLink: FunctionComponent<Props> = props => {
return (
<Link
Component={TextLink}
as={TextLink}
className={styles.root}
to={props.href}
onClick={props.onClick}
@@ -1,8 +1,8 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`renders correctly 1`] = `
<Link
Component={[Function]}
<ForwardRef(render)
as={[Function]}
className="GoToCommentLink-root"
onClick={[Function]}
to="#"
@@ -18,5 +18,5 @@ exports[`renders correctly 1`] = `
<ForwardRef(forwardRef)>
chevron_right
</ForwardRef(forwardRef)>
</Link>
</ForwardRef(render)>
`;