From a53194b4a0e487ad5d47daefc2158e1d23f10b4b Mon Sep 17 00:00:00 2001 From: okbel Date: Fri, 23 Mar 2018 15:51:04 -0300 Subject: [PATCH 01/84] Removing styles from sign in --- .../stream/components/SetUsernameDialog.css | 7 -- .../stream/components/SetUsernameDialog.js | 95 +++++++++---------- 2 files changed, 44 insertions(+), 58 deletions(-) diff --git a/plugins/talk-plugin-auth/client/stream/components/SetUsernameDialog.css b/plugins/talk-plugin-auth/client/stream/components/SetUsernameDialog.css index c1947ca7d..856e95642 100644 --- a/plugins/talk-plugin-auth/client/stream/components/SetUsernameDialog.css +++ b/plugins/talk-plugin-auth/client/stream/components/SetUsernameDialog.css @@ -1,10 +1,3 @@ -.dialogusername { - border: none; - box-shadow: 0 9px 46px 8px rgba(0, 0, 0, 0.14), 0 11px 15px -7px rgba(0, 0, 0, 0.12), 0 24px 38px 3px rgba(0, 0, 0, 0.2); - width: 400px; - top: 10px; -} - .yourusername { display: block; } diff --git a/plugins/talk-plugin-auth/client/stream/components/SetUsernameDialog.js b/plugins/talk-plugin-auth/client/stream/components/SetUsernameDialog.js index 0fd742879..9cacc70b8 100644 --- a/plugins/talk-plugin-auth/client/stream/components/SetUsernameDialog.js +++ b/plugins/talk-plugin-auth/client/stream/components/SetUsernameDialog.js @@ -1,12 +1,7 @@ import React from 'react'; import PropTypes from 'prop-types'; import styles from './SetUsernameDialog.css'; -import { - Dialog, - Alert, - TextField, - Button, -} from 'plugin-api/beta/client/components/ui'; +import { Alert, TextField, Button } from 'plugin-api/beta/client/components/ui'; import { FakeComment } from './FakeComment'; import { t } from 'plugin-api/beta/client/services'; @@ -22,52 +17,50 @@ class SetUsernameDialog extends React.Component { const { username, usernameError, errorMessage } = this.props; return ( - -
-
-

- {t('talk-plugin-auth.set_username_dialog.write_your_username')} -

-
-
-

- {t('talk-plugin-auth.set_username_dialog.your_username')} -

- - {errorMessage && {errorMessage}} -
- {usernameError && ( - - {' '} - {t( - 'talk-plugin-auth.set_username_dialog.special_characters' - )}{' '} - - )} -
- - -
-
-
+
+
+

+ {t('talk-plugin-auth.set_username_dialog.write_your_username')} +

-
+
+

+ {t('talk-plugin-auth.set_username_dialog.your_username')} +

+ + {errorMessage && {errorMessage}} +
+ {usernameError && ( + + {' '} + {t( + 'talk-plugin-auth.set_username_dialog.special_characters' + )}{' '} + + )} +
+ + +
+
+
+ ); } } From 2fe17d29e398d674aae45701488ee9f75834270a Mon Sep 17 00:00:00 2001 From: okbel Date: Fri, 23 Mar 2018 16:03:33 -0300 Subject: [PATCH 02/84] Working on containers --- docs/source/_data/introspection.json | 9203 +++++++++++++++++ .../client/login/constants.js | 2 + .../talk-plugin-auth/client/login/reducer.js | 11 + .../stream/components/SetUsernameDialog.css | 7 + .../stream/components/SetUsernameDialog.js | 93 +- 5 files changed, 9273 insertions(+), 43 deletions(-) create mode 100644 docs/source/_data/introspection.json diff --git a/docs/source/_data/introspection.json b/docs/source/_data/introspection.json new file mode 100644 index 000000000..4e5390999 --- /dev/null +++ b/docs/source/_data/introspection.json @@ -0,0 +1,9203 @@ +{ + "__schema": { + "queryType": { + "name": "RootQuery" + }, + "mutationType": { + "name": "RootMutation" + }, + "subscriptionType": { + "name": "Subscription" + }, + "types": [ + { + "kind": "OBJECT", + "name": "RootQuery", + "description": "", + "fields": [ + { + "name": "settings", + "description": "Site wide settings and defaults.", + "args": [], + "type": { + "kind": "OBJECT", + "name": "Settings", + "ofType": null + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "comment", + "description": "Finds a specific comment based on it's id.", + "args": [ + { + "name": "id", + "description": "", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "ID", + "ofType": null + } + }, + "defaultValue": null + } + ], + "type": { + "kind": "OBJECT", + "name": "Comment", + "ofType": null + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "assets", + "description": "All assets. Requires the `ADMIN` role.", + "args": [ + { + "name": "query", + "description": "", + "type": { + "kind": "INPUT_OBJECT", + "name": "AssetsQuery", + "ofType": null + }, + "defaultValue": null + } + ], + "type": { + "kind": "OBJECT", + "name": "AssetConnection", + "ofType": null + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "asset", + "description": "Find or create an asset by url, or just find with the ID.", + "args": [ + { + "name": "id", + "description": "", + "type": { + "kind": "SCALAR", + "name": "ID", + "ofType": null + }, + "defaultValue": null + }, + { + "name": "url", + "description": "", + "type": { + "kind": "SCALAR", + "name": "String", + "ofType": null + }, + "defaultValue": null + } + ], + "type": { + "kind": "OBJECT", + "name": "Asset", + "ofType": null + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "comments", + "description": "Comments returned based on a query.", + "args": [ + { + "name": "query", + "description": "", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "INPUT_OBJECT", + "name": "CommentsQuery", + "ofType": null + } + }, + "defaultValue": null + } + ], + "type": { + "kind": "OBJECT", + "name": "CommentConnection", + "ofType": null + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "commentCount", + "description": "Return the count of comments satisfied by the query. Note that this edge is\nexpensive as it is not batched. Requires the `ADMIN` role.", + "args": [ + { + "name": "query", + "description": "", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "INPUT_OBJECT", + "name": "CommentCountQuery", + "ofType": null + } + }, + "defaultValue": null + } + ], + "type": { + "kind": "SCALAR", + "name": "Int", + "ofType": null + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "userCount", + "description": "Return the count of users satisfied by the query. Note that this edge is\nexpensive as it is not batched. This field is restricted.", + "args": [ + { + "name": "query", + "description": "", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "INPUT_OBJECT", + "name": "UserCountQuery", + "ofType": null + } + }, + "defaultValue": null + } + ], + "type": { + "kind": "SCALAR", + "name": "Int", + "ofType": null + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "me", + "description": "The currently logged in user based on the request. Requires any logged in\nrole.", + "args": [], + "type": { + "kind": "OBJECT", + "name": "User", + "ofType": null + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "users", + "description": "Users returned based on a query. Requires the `ADMIN` role.", + "args": [ + { + "name": "query", + "description": "", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "INPUT_OBJECT", + "name": "UsersQuery", + "ofType": null + } + }, + "defaultValue": null + } + ], + "type": { + "kind": "OBJECT", + "name": "UserConnection", + "ofType": null + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "user", + "description": "a single User by id", + "args": [ + { + "name": "id", + "description": "", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "ID", + "ofType": null + } + }, + "defaultValue": null + } + ], + "type": { + "kind": "OBJECT", + "name": "User", + "ofType": null + }, + "isDeprecated": false, + "deprecationReason": null + } + ], + "inputFields": null, + "interfaces": [], + "enumValues": null, + "possibleTypes": null + }, + { + "kind": "OBJECT", + "name": "Settings", + "description": "", + "fields": [ + { + "name": "moderation", + "description": "moderation is the moderation mode for all Asset's on the site.", + "args": [], + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "ENUM", + "name": "MODERATION_MODE", + "ofType": null + } + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "requireEmailConfirmation", + "description": "Enables a requirement for email confirmation before a user can login.", + "args": [], + "type": { + "kind": "SCALAR", + "name": "Boolean", + "ofType": null + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "infoBoxEnable", + "description": "infoBoxEnable will enable the Info Box content visible above the question\nbox.", + "args": [], + "type": { + "kind": "SCALAR", + "name": "Boolean", + "ofType": null + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "infoBoxContent", + "description": "infoBoxContent is the content of the Info Box.", + "args": [], + "type": { + "kind": "SCALAR", + "name": "String", + "ofType": null + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "questionBoxEnable", + "description": "questionBoxEnable will enable the Question Box's content to be visible above\nThe comment box.", + "args": [], + "type": { + "kind": "SCALAR", + "name": "Boolean", + "ofType": null + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "questionBoxContent", + "description": "questionBoxContent is the content of the Question Box.", + "args": [], + "type": { + "kind": "SCALAR", + "name": "String", + "ofType": null + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "premodLinksEnable", + "description": "premodLinksEnable will put all comments that contain links into premod.", + "args": [], + "type": { + "kind": "SCALAR", + "name": "Boolean", + "ofType": null + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "questionBoxIcon", + "description": "questionBoxIcon is the icon for the Question Box.", + "args": [], + "type": { + "kind": "SCALAR", + "name": "String", + "ofType": null + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "autoCloseStream", + "description": "autoCloseStream when true will auto close the stream when the `closeTimeout`\namount of seconds have been reached.", + "args": [], + "type": { + "kind": "SCALAR", + "name": "Boolean", + "ofType": null + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "customCssUrl", + "description": "customCssUrl is the URL of the custom CSS used to display on the frontend.", + "args": [], + "type": { + "kind": "SCALAR", + "name": "String", + "ofType": null + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "closedTimeout", + "description": "closedTimeout is the amount of seconds from the created_at timestamp that a\ngiven asset will be considered closed.", + "args": [], + "type": { + "kind": "SCALAR", + "name": "Int", + "ofType": null + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "closedMessage", + "description": "closedMessage is the message shown to the user when the given Asset is\nclosed.", + "args": [], + "type": { + "kind": "SCALAR", + "name": "String", + "ofType": null + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "editCommentWindowLength", + "description": "editCommentWindowLength is the length of time (in milliseconds) after a\ncomment is posted that it can still be edited by the author.", + "args": [], + "type": { + "kind": "SCALAR", + "name": "Int", + "ofType": null + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "charCountEnable", + "description": "charCountEnable is true when the character count restriction is enabled.", + "args": [], + "type": { + "kind": "SCALAR", + "name": "Boolean", + "ofType": null + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "charCount", + "description": "charCount is the maximum number of characters a comment may be.", + "args": [], + "type": { + "kind": "SCALAR", + "name": "Int", + "ofType": null + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "organizationName", + "description": "organizationName is the name of the organization.", + "args": [], + "type": { + "kind": "SCALAR", + "name": "String", + "ofType": null + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "wordlist", + "description": "wordlist will return a given list of words.", + "args": [], + "type": { + "kind": "OBJECT", + "name": "Wordlist", + "ofType": null + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "domains", + "description": "domains will return a given list of domains.", + "args": [], + "type": { + "kind": "OBJECT", + "name": "Domains", + "ofType": null + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "notificationsRequireConfirmation", + "description": "notificationsRequireConfirmation when true indicates that User's must have\ntheir email address confirmed/verified before they can receive\nnotifications.", + "args": [], + "type": { + "kind": "SCALAR", + "name": "Boolean", + "ofType": null + }, + "isDeprecated": false, + "deprecationReason": null + } + ], + "inputFields": null, + "interfaces": [], + "enumValues": null, + "possibleTypes": null + }, + { + "kind": "ENUM", + "name": "MODERATION_MODE", + "description": "The moderation mode of the site.", + "fields": null, + "inputFields": null, + "interfaces": null, + "enumValues": [ + { + "name": "PRE", + "description": "Comments posted while in `PRE` mode will be labeled with a `PREMOD`\nstatus and will require a moderator decision before being visible.", + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "POST", + "description": "Comments posted while in `POST` will be visible immediately.", + "isDeprecated": false, + "deprecationReason": null + } + ], + "possibleTypes": null + }, + { + "kind": "SCALAR", + "name": "Boolean", + "description": "The `Boolean` scalar type represents `true` or `false`.", + "fields": null, + "inputFields": null, + "interfaces": null, + "enumValues": null, + "possibleTypes": null + }, + { + "kind": "SCALAR", + "name": "String", + "description": "The `String` scalar type represents textual data, represented as UTF-8 character sequences. The String type is most often used by GraphQL to represent free-form human-readable text.", + "fields": null, + "inputFields": null, + "interfaces": null, + "enumValues": null, + "possibleTypes": null + }, + { + "kind": "SCALAR", + "name": "Int", + "description": "The `Int` scalar type represents non-fractional signed whole numeric values. Int can represent values between -(2^31) and 2^31 - 1. ", + "fields": null, + "inputFields": null, + "interfaces": null, + "enumValues": null, + "possibleTypes": null + }, + { + "kind": "OBJECT", + "name": "Wordlist", + "description": "Wordlist describes all the available wordlists.", + "fields": [ + { + "name": "banned", + "description": "banned words will by default reject the comment if it is found.", + "args": [], + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "LIST", + "name": null, + "ofType": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "String", + "ofType": null + } + } + } + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "suspect", + "description": "suspect words will simply flag the comment.", + "args": [], + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "LIST", + "name": null, + "ofType": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "String", + "ofType": null + } + } + } + }, + "isDeprecated": false, + "deprecationReason": null + } + ], + "inputFields": null, + "interfaces": [], + "enumValues": null, + "possibleTypes": null + }, + { + "kind": "OBJECT", + "name": "Domains", + "description": "Domains describes all the available lists of domains.", + "fields": [ + { + "name": "whitelist", + "description": "whitelist is the list of domains that the embed is allowed to render on.", + "args": [], + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "LIST", + "name": null, + "ofType": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "String", + "ofType": null + } + } + } + }, + "isDeprecated": false, + "deprecationReason": null + } + ], + "inputFields": null, + "interfaces": [], + "enumValues": null, + "possibleTypes": null + }, + { + "kind": "SCALAR", + "name": "ID", + "description": "The `ID` scalar type represents a unique identifier, often used to refetch an object or as key for a cache. The ID type appears in a JSON response as a String; however, it is not intended to be human-readable. When expected as an input type, any string (such as `\"4\"`) or integer (such as `4`) input value will be accepted as an ID.", + "fields": null, + "inputFields": null, + "interfaces": null, + "enumValues": null, + "possibleTypes": null + }, + { + "kind": "OBJECT", + "name": "Comment", + "description": "", + "fields": [ + { + "name": "parent", + "description": "The parent of the comment (if there is one).", + "args": [], + "type": { + "kind": "OBJECT", + "name": "Comment", + "ofType": null + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "id", + "description": "The ID of the comment.", + "args": [], + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "ID", + "ofType": null + } + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "body", + "description": "The actual comment data.", + "args": [], + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "String", + "ofType": null + } + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "body_history", + "description": "The body history of the comment.", + "args": [], + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "LIST", + "name": null, + "ofType": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "OBJECT", + "name": "CommentBodyHistory", + "ofType": null + } + } + } + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "tags", + "description": "The tags on the comment", + "args": [], + "type": { + "kind": "LIST", + "name": null, + "ofType": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "OBJECT", + "name": "TagLink", + "ofType": null + } + } + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "user", + "description": "The user who authored the comment.", + "args": [], + "type": { + "kind": "OBJECT", + "name": "User", + "ofType": null + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "replies", + "description": "The replies that were made to the comment.", + "args": [ + { + "name": "query", + "description": "", + "type": { + "kind": "INPUT_OBJECT", + "name": "RepliesQuery", + "ofType": null + }, + "defaultValue": "{sortOrder: ASC, sortBy: CREATED_AT, limit: 3}" + } + ], + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "OBJECT", + "name": "CommentConnection", + "ofType": null + } + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "replyCount", + "description": "replyCount is the number of replies with a depth of 1. Only direct replies\nto this comment are counted.", + "args": [], + "type": { + "kind": "SCALAR", + "name": "Int", + "ofType": null + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "actions", + "description": "Actions completed on the parent. Requires the `ADMIN` role.", + "args": [], + "type": { + "kind": "LIST", + "name": null, + "ofType": { + "kind": "INTERFACE", + "name": "Action", + "ofType": null + } + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "action_summaries", + "description": "Action summaries against a comment.", + "args": [], + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "LIST", + "name": null, + "ofType": { + "kind": "INTERFACE", + "name": "ActionSummary", + "ofType": null + } + } + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "asset", + "description": "The asset that a comment was made on.", + "args": [], + "type": { + "kind": "OBJECT", + "name": "Asset", + "ofType": null + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "status", + "description": "The current status of a comment.", + "args": [], + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "ENUM", + "name": "COMMENT_STATUS", + "ofType": null + } + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "status_history", + "description": "The status history of the comment. Requires the `ADMIN` or `MODERATOR` role.", + "args": [], + "type": { + "kind": "LIST", + "name": null, + "ofType": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "OBJECT", + "name": "CommentStatusHistory", + "ofType": null + } + } + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "created_at", + "description": "The time when the comment was created", + "args": [], + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "Date", + "ofType": null + } + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "updated_at", + "description": "The time when the comment was updated.", + "args": [], + "type": { + "kind": "SCALAR", + "name": "Date", + "ofType": null + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "editing", + "description": "describes how the comment can be edited", + "args": [], + "type": { + "kind": "OBJECT", + "name": "EditInfo", + "ofType": null + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "hasParent", + "description": "Indicates if it has a parent", + "args": [], + "type": { + "kind": "SCALAR", + "name": "Boolean", + "ofType": null + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "richTextBody", + "description": "", + "args": [], + "type": { + "kind": "SCALAR", + "name": "String", + "ofType": null + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "toxicity", + "description": "", + "args": [], + "type": { + "kind": "SCALAR", + "name": "Float", + "ofType": null + }, + "isDeprecated": false, + "deprecationReason": null + } + ], + "inputFields": null, + "interfaces": [], + "enumValues": null, + "possibleTypes": null + }, + { + "kind": "OBJECT", + "name": "CommentBodyHistory", + "description": "", + "fields": [ + { + "name": "body", + "description": "", + "args": [], + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "String", + "ofType": null + } + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "created_at", + "description": "", + "args": [], + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "Date", + "ofType": null + } + }, + "isDeprecated": false, + "deprecationReason": null + } + ], + "inputFields": null, + "interfaces": [], + "enumValues": null, + "possibleTypes": null + }, + { + "kind": "SCALAR", + "name": "Date", + "description": "Date represented as an ISO8601 string", + "fields": null, + "inputFields": null, + "interfaces": null, + "enumValues": null, + "possibleTypes": null + }, + { + "kind": "OBJECT", + "name": "TagLink", + "description": "TagLink is used to associate a given Tag with a Model via a TagLink.", + "fields": [ + { + "name": "tag", + "description": "The underlying Tag that is either duplicated from the global list or created\nuniquely for this specific model.", + "args": [], + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "OBJECT", + "name": "Tag", + "ofType": null + } + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "assigned_by", + "description": "The user that assigned the tag. This TagLink could have been created by the\nsystem, in which case this will be null. It could also be null if the\ncurrent user is not an Admin/Moderator.", + "args": [], + "type": { + "kind": "OBJECT", + "name": "User", + "ofType": null + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "created_at", + "description": "The date that the TagLink was created.", + "args": [], + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "Date", + "ofType": null + } + }, + "isDeprecated": false, + "deprecationReason": null + } + ], + "inputFields": null, + "interfaces": [], + "enumValues": null, + "possibleTypes": null + }, + { + "kind": "OBJECT", + "name": "Tag", + "description": "Tag represents the underlying Tag that can be either stored in a global list\nor added uniquely to the entity.", + "fields": [ + { + "name": "name", + "description": "The actual name of the tag entry.", + "args": [], + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "String", + "ofType": null + } + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "created_at", + "description": "The time that this Tag was created.", + "args": [], + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "Date", + "ofType": null + } + }, + "isDeprecated": false, + "deprecationReason": null + } + ], + "inputFields": null, + "interfaces": [], + "enumValues": null, + "possibleTypes": null + }, + { + "kind": "OBJECT", + "name": "User", + "description": "", + "fields": [ + { + "name": "id", + "description": "The ID of the User.", + "args": [], + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "ID", + "ofType": null + } + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "username", + "description": "Username of a user.", + "args": [], + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "String", + "ofType": null + } + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "created_at", + "description": "creation date of user", + "args": [], + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "Date", + "ofType": null + } + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "action_summaries", + "description": "Action summaries against the user.", + "args": [], + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "LIST", + "name": null, + "ofType": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "INTERFACE", + "name": "ActionSummary", + "ofType": null + } + } + } + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "actions", + "description": "Actions completed on the parent.", + "args": [], + "type": { + "kind": "LIST", + "name": null, + "ofType": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "INTERFACE", + "name": "Action", + "ofType": null + } + } + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "role", + "description": "The current roles of the user.", + "args": [], + "type": { + "kind": "ENUM", + "name": "USER_ROLES", + "ofType": null + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "profiles", + "description": "The current profiles of the user.", + "args": [], + "type": { + "kind": "LIST", + "name": null, + "ofType": { + "kind": "INTERFACE", + "name": "UserProfile", + "ofType": null + } + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "email", + "description": "The primary email address of the user. Only accessible to Administrators or\nthe current user.", + "args": [], + "type": { + "kind": "SCALAR", + "name": "String", + "ofType": null + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "tags", + "description": "The tags on the user.", + "args": [], + "type": { + "kind": "LIST", + "name": null, + "ofType": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "OBJECT", + "name": "TagLink", + "ofType": null + } + } + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "ignoredUsers", + "description": "ignored users.", + "args": [], + "type": { + "kind": "LIST", + "name": null, + "ofType": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "OBJECT", + "name": "User", + "ofType": null + } + } + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "tokens", + "description": "Tokens are the personal access tokens for a given user.", + "args": [], + "type": { + "kind": "LIST", + "name": null, + "ofType": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "OBJECT", + "name": "Token", + "ofType": null + } + } + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "comments", + "description": "returns all comments based on a query.", + "args": [ + { + "name": "query", + "description": "", + "type": { + "kind": "INPUT_OBJECT", + "name": "CommentsQuery", + "ofType": null + }, + "defaultValue": null + } + ], + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "OBJECT", + "name": "CommentConnection", + "ofType": null + } + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "reliable", + "description": "reliable is the reference to a given user's Reliability. If the requesting\nuser does not have permission to access the reliability, null will be\nreturned.", + "args": [], + "type": { + "kind": "OBJECT", + "name": "Reliability", + "ofType": null + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "state", + "description": "returns user status", + "args": [], + "type": { + "kind": "OBJECT", + "name": "UserState", + "ofType": null + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "notificationSettings", + "description": "", + "args": [], + "type": { + "kind": "OBJECT", + "name": "NotificationSettings", + "ofType": null + }, + "isDeprecated": false, + "deprecationReason": null + } + ], + "inputFields": null, + "interfaces": [], + "enumValues": null, + "possibleTypes": null + }, + { + "kind": "INTERFACE", + "name": "ActionSummary", + "description": "A summary of actions based on the specific grouping of the group_id.", + "fields": [ + { + "name": "count", + "description": "The count of actions with this group.", + "args": [], + "type": { + "kind": "SCALAR", + "name": "Int", + "ofType": null + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "current_user", + "description": "The current user's action.", + "args": [], + "type": { + "kind": "INTERFACE", + "name": "Action", + "ofType": null + }, + "isDeprecated": false, + "deprecationReason": null + } + ], + "inputFields": null, + "interfaces": null, + "enumValues": null, + "possibleTypes": [ + { + "kind": "OBJECT", + "name": "DefaultActionSummary", + "ofType": null + }, + { + "kind": "OBJECT", + "name": "FlagActionSummary", + "ofType": null + }, + { + "kind": "OBJECT", + "name": "DontAgreeActionSummary", + "ofType": null + }, + { + "kind": "OBJECT", + "name": "RespectActionSummary", + "ofType": null + } + ] + }, + { + "kind": "INTERFACE", + "name": "Action", + "description": "An action rendered against a parent entity item.", + "fields": [ + { + "name": "id", + "description": "The ID of the action.", + "args": [], + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "ID", + "ofType": null + } + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "user", + "description": "The author of the action.", + "args": [], + "type": { + "kind": "OBJECT", + "name": "User", + "ofType": null + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "updated_at", + "description": "The time when the Action was updated.", + "args": [], + "type": { + "kind": "SCALAR", + "name": "Date", + "ofType": null + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "created_at", + "description": "The time when the Action was created.", + "args": [], + "type": { + "kind": "SCALAR", + "name": "Date", + "ofType": null + }, + "isDeprecated": false, + "deprecationReason": null + } + ], + "inputFields": null, + "interfaces": null, + "enumValues": null, + "possibleTypes": [ + { + "kind": "OBJECT", + "name": "FlagAction", + "ofType": null + }, + { + "kind": "OBJECT", + "name": "DontAgreeAction", + "ofType": null + }, + { + "kind": "OBJECT", + "name": "RespectAction", + "ofType": null + }, + { + "kind": "OBJECT", + "name": "DefaultAction", + "ofType": null + } + ] + }, + { + "kind": "ENUM", + "name": "USER_ROLES", + "description": "Roles that a user can have, these can be combined.", + "fields": null, + "inputFields": null, + "interfaces": null, + "enumValues": [ + { + "name": "ADMIN", + "description": "an administrator of the site", + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "MODERATOR", + "description": "a moderator of the site", + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "STAFF", + "description": "a staff of the site", + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "COMMENTER", + "description": "a user without administrative roles.", + "isDeprecated": false, + "deprecationReason": null + } + ], + "possibleTypes": null + }, + { + "kind": "INTERFACE", + "name": "UserProfile", + "description": "", + "fields": [ + { + "name": "id", + "description": "The id is an identifier for the user profile (email, facebook id, etc)", + "args": [], + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "String", + "ofType": null + } + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "provider", + "description": "name of the provider attached to the authentication mode", + "args": [], + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "String", + "ofType": null + } + }, + "isDeprecated": false, + "deprecationReason": null + } + ], + "inputFields": null, + "interfaces": null, + "enumValues": null, + "possibleTypes": [ + { + "kind": "OBJECT", + "name": "DefaultUserProfile", + "ofType": null + }, + { + "kind": "OBJECT", + "name": "LocalUserProfile", + "ofType": null + } + ] + }, + { + "kind": "OBJECT", + "name": "Token", + "description": "Token is a personal access token associated with a given user.", + "fields": [ + { + "name": "id", + "description": "ID is the unique identifier for the token.", + "args": [], + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "ID", + "ofType": null + } + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "name", + "description": "Name is the description for the token.", + "args": [], + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "String", + "ofType": null + } + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "active", + "description": "Active determines if the token is available to hit the API.", + "args": [], + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "Boolean", + "ofType": null + } + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "jwt", + "description": "JWT is the actual token to use for authentication, this is only available\non token creation, otherwise it will be null.", + "args": [], + "type": { + "kind": "SCALAR", + "name": "String", + "ofType": null + }, + "isDeprecated": false, + "deprecationReason": null + } + ], + "inputFields": null, + "interfaces": [], + "enumValues": null, + "possibleTypes": null + }, + { + "kind": "INPUT_OBJECT", + "name": "CommentsQuery", + "description": "CommentsQuery allows the ability to query comments by a specific methods.", + "fields": null, + "inputFields": [ + { + "name": "author_id", + "description": "Author of the comments.", + "type": { + "kind": "SCALAR", + "name": "ID", + "ofType": null + }, + "defaultValue": null + }, + { + "name": "statuses", + "description": "Current status of a comment.\nThis field is restricted.", + "type": { + "kind": "LIST", + "name": null, + "ofType": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "ENUM", + "name": "COMMENT_STATUS", + "ofType": null + } + } + }, + "defaultValue": "[NONE, ACCEPTED]" + }, + { + "name": "asset_id", + "description": "Asset that a comment is on.", + "type": { + "kind": "SCALAR", + "name": "ID", + "ofType": null + }, + "defaultValue": null + }, + { + "name": "parent_id", + "description": "The parent of the comment that we want to retrieve.", + "type": { + "kind": "SCALAR", + "name": "ID", + "ofType": null + }, + "defaultValue": null + }, + { + "name": "action_type", + "description": "Comments returned will only be ones which have at least one action of this\ntype. Requires the `ADMIN` role.", + "type": { + "kind": "ENUM", + "name": "ACTION_TYPE", + "ofType": null + }, + "defaultValue": null + }, + { + "name": "limit", + "description": "Limit the number of results to be returned.", + "type": { + "kind": "SCALAR", + "name": "Int", + "ofType": null + }, + "defaultValue": "10" + }, + { + "name": "cursor", + "description": "Skip results from the last created_at timestamp.", + "type": { + "kind": "SCALAR", + "name": "Cursor", + "ofType": null + }, + "defaultValue": null + }, + { + "name": "sortOrder", + "description": "Sort the results by from largest first.", + "type": { + "kind": "ENUM", + "name": "SORT_ORDER", + "ofType": null + }, + "defaultValue": "DESC" + }, + { + "name": "sortBy", + "description": "The order to sort the comments by, sorting by default the created at\ntimestamp.", + "type": { + "kind": "ENUM", + "name": "SORT_COMMENTS_BY", + "ofType": null + }, + "defaultValue": "CREATED_AT" + }, + { + "name": "tags", + "description": "Filter by a specific tag name.", + "type": { + "kind": "LIST", + "name": null, + "ofType": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "String", + "ofType": null + } + } + }, + "defaultValue": null + }, + { + "name": "excludeIgnored", + "description": "Exclude comments ignored by the requesting user", + "type": { + "kind": "SCALAR", + "name": "Boolean", + "ofType": null + }, + "defaultValue": null + } + ], + "interfaces": null, + "enumValues": null, + "possibleTypes": null + }, + { + "kind": "ENUM", + "name": "COMMENT_STATUS", + "description": "The statuses that a comment may have.", + "fields": null, + "inputFields": null, + "interfaces": null, + "enumValues": [ + { + "name": "NONE", + "description": "The comment is not PREMOD, but was not applied a moderation status by a\nmoderator.", + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "ACCEPTED", + "description": "The comment has been accepted by a moderator.", + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "REJECTED", + "description": "The comment has been rejected by a moderator.", + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "PREMOD", + "description": "The comment was created while the asset's premoderation option was on, and\nnew comments that haven't been moderated yet are referred to as\n\"premoderated\" or \"premod\" comments.", + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "SYSTEM_WITHHELD", + "description": "SYSTEM_WITHHELD represents a comment that was withheld by the system because\nit was flagged by an internal process for further review.", + "isDeprecated": false, + "deprecationReason": null + } + ], + "possibleTypes": null + }, + { + "kind": "ENUM", + "name": "ACTION_TYPE", + "description": "", + "fields": null, + "inputFields": null, + "interfaces": null, + "enumValues": [ + { + "name": "FLAG", + "description": "Represents a FlagAction.", + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "DONTAGREE", + "description": "Represents a don't agree action", + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "RESPECT", + "description": "Represents a Respect.", + "isDeprecated": false, + "deprecationReason": null + } + ], + "possibleTypes": null + }, + { + "kind": "SCALAR", + "name": "Cursor", + "description": "Cursor represents a paginating cursor.", + "fields": null, + "inputFields": null, + "interfaces": null, + "enumValues": null, + "possibleTypes": null + }, + { + "kind": "ENUM", + "name": "SORT_ORDER", + "description": "Establishes the ordering of the content by their created_at time stamp.", + "fields": null, + "inputFields": null, + "interfaces": null, + "enumValues": [ + { + "name": "DESC", + "description": "newest to oldest order.", + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "ASC", + "description": "oldest to newer order.", + "isDeprecated": false, + "deprecationReason": null + } + ], + "possibleTypes": null + }, + { + "kind": "ENUM", + "name": "SORT_COMMENTS_BY", + "description": "", + "fields": null, + "inputFields": null, + "interfaces": null, + "enumValues": [ + { + "name": "CREATED_AT", + "description": "Comments will be sorted by their created at date.", + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "REPLIES", + "description": "Comments will be sorted by their immediate reply count (replies to the comment\nin question only, not including descendants).", + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "RESPECTS", + "description": "Comments will be sorted by their count of respects\non the comment.", + "isDeprecated": false, + "deprecationReason": null + } + ], + "possibleTypes": null + }, + { + "kind": "OBJECT", + "name": "CommentConnection", + "description": "CommentConnection represents a paginable subset of a comment list.", + "fields": [ + { + "name": "hasNextPage", + "description": "Indicates that there are more comments after this subset.", + "args": [], + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "Boolean", + "ofType": null + } + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "startCursor", + "description": "Cursor of first comment in subset.", + "args": [], + "type": { + "kind": "SCALAR", + "name": "Cursor", + "ofType": null + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "endCursor", + "description": "Cursor of last comment in subset.", + "args": [], + "type": { + "kind": "SCALAR", + "name": "Cursor", + "ofType": null + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "nodes", + "description": "Subset of comments.", + "args": [], + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "LIST", + "name": null, + "ofType": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "OBJECT", + "name": "Comment", + "ofType": null + } + } + } + }, + "isDeprecated": false, + "deprecationReason": null + } + ], + "inputFields": null, + "interfaces": [], + "enumValues": null, + "possibleTypes": null + }, + { + "kind": "OBJECT", + "name": "Reliability", + "description": "Reliability defines how a given user should be considered reliable for their\ncomment or flag activity.", + "fields": [ + { + "name": "flagger", + "description": "flagger will be `true` when the flagger is reliable, `false` if not, or\n`null` if the reliability cannot be determined.", + "args": [], + "type": { + "kind": "SCALAR", + "name": "Boolean", + "ofType": null + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "commenter", + "description": "Commenter will be `true` when the commenter is reliable, `false` if not, or\n`null` if the reliability cannot be determined.", + "args": [], + "type": { + "kind": "SCALAR", + "name": "Boolean", + "ofType": null + }, + "isDeprecated": false, + "deprecationReason": null + } + ], + "inputFields": null, + "interfaces": [], + "enumValues": null, + "possibleTypes": null + }, + { + "kind": "OBJECT", + "name": "UserState", + "description": "UserState describes the different permission based details for a user.", + "fields": [ + { + "name": "status", + "description": "status describes the statuses of different aspects of the user's details.", + "args": [], + "type": { + "kind": "OBJECT", + "name": "UserStatus", + "ofType": null + }, + "isDeprecated": false, + "deprecationReason": null + } + ], + "inputFields": null, + "interfaces": [], + "enumValues": null, + "possibleTypes": null + }, + { + "kind": "OBJECT", + "name": "UserStatus", + "description": "", + "fields": [ + { + "name": "username", + "description": "username is the status of the username.", + "args": [], + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "OBJECT", + "name": "UsernameStatus", + "ofType": null + } + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "banned", + "description": "banned is the bool that determines if the user is banned or not.", + "args": [], + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "OBJECT", + "name": "BannedStatus", + "ofType": null + } + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "suspension", + "description": "suspension is the date that the user is suspended until.", + "args": [], + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "OBJECT", + "name": "SuspensionStatus", + "ofType": null + } + }, + "isDeprecated": false, + "deprecationReason": null + } + ], + "inputFields": null, + "interfaces": [], + "enumValues": null, + "possibleTypes": null + }, + { + "kind": "OBJECT", + "name": "UsernameStatus", + "description": "", + "fields": [ + { + "name": "status", + "description": "", + "args": [], + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "ENUM", + "name": "USER_STATUS_USERNAME", + "ofType": null + } + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "history", + "description": "", + "args": [], + "type": { + "kind": "LIST", + "name": null, + "ofType": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "OBJECT", + "name": "UsernameStatusHistory", + "ofType": null + } + } + }, + "isDeprecated": false, + "deprecationReason": null + } + ], + "inputFields": null, + "interfaces": [], + "enumValues": null, + "possibleTypes": null + }, + { + "kind": "ENUM", + "name": "USER_STATUS_USERNAME", + "description": "USER_STATUS_USERNAME is the different states that a username can be in.", + "fields": null, + "inputFields": null, + "interfaces": null, + "enumValues": [ + { + "name": "UNSET", + "description": "UNSET is used when the username can be changed, and does not necessarily\nrequire moderator action to become active. This can be used when the user\nsigns up with a social login and has the option of setting their own\nusername.", + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "SET", + "description": "SET is used when the username has been set for the first time, but cannot\nchange without the username being rejected by a moderator and that moderator\nagreeing that the username should be allowed to change.", + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "APPROVED", + "description": "APPROVED is used when the username was changed, and subsequently approved by\nsaid moderator.", + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "REJECTED", + "description": "REJECTED is used when the username was changed, and subsequently rejected by\nsaid moderator.", + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "CHANGED", + "description": "CHANGED is used after a user has changed their username after it was\nrejected.", + "isDeprecated": false, + "deprecationReason": null + } + ], + "possibleTypes": null + }, + { + "kind": "OBJECT", + "name": "UsernameStatusHistory", + "description": "", + "fields": [ + { + "name": "status", + "description": "", + "args": [], + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "ENUM", + "name": "USER_STATUS_USERNAME", + "ofType": null + } + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "assigned_by", + "description": "", + "args": [], + "type": { + "kind": "OBJECT", + "name": "User", + "ofType": null + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "created_at", + "description": "", + "args": [], + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "Date", + "ofType": null + } + }, + "isDeprecated": false, + "deprecationReason": null + } + ], + "inputFields": null, + "interfaces": [], + "enumValues": null, + "possibleTypes": null + }, + { + "kind": "OBJECT", + "name": "BannedStatus", + "description": "", + "fields": [ + { + "name": "status", + "description": "", + "args": [], + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "Boolean", + "ofType": null + } + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "history", + "description": "", + "args": [], + "type": { + "kind": "LIST", + "name": null, + "ofType": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "OBJECT", + "name": "BannedStatusHistory", + "ofType": null + } + } + }, + "isDeprecated": false, + "deprecationReason": null + } + ], + "inputFields": null, + "interfaces": [], + "enumValues": null, + "possibleTypes": null + }, + { + "kind": "OBJECT", + "name": "BannedStatusHistory", + "description": "", + "fields": [ + { + "name": "status", + "description": "", + "args": [], + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "Boolean", + "ofType": null + } + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "assigned_by", + "description": "", + "args": [], + "type": { + "kind": "OBJECT", + "name": "User", + "ofType": null + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "created_at", + "description": "", + "args": [], + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "Date", + "ofType": null + } + }, + "isDeprecated": false, + "deprecationReason": null + } + ], + "inputFields": null, + "interfaces": [], + "enumValues": null, + "possibleTypes": null + }, + { + "kind": "OBJECT", + "name": "SuspensionStatus", + "description": "", + "fields": [ + { + "name": "until", + "description": "", + "args": [], + "type": { + "kind": "SCALAR", + "name": "Date", + "ofType": null + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "history", + "description": "", + "args": [], + "type": { + "kind": "LIST", + "name": null, + "ofType": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "OBJECT", + "name": "SuspensionStatusHistory", + "ofType": null + } + } + }, + "isDeprecated": false, + "deprecationReason": null + } + ], + "inputFields": null, + "interfaces": [], + "enumValues": null, + "possibleTypes": null + }, + { + "kind": "OBJECT", + "name": "SuspensionStatusHistory", + "description": "", + "fields": [ + { + "name": "until", + "description": "", + "args": [], + "type": { + "kind": "SCALAR", + "name": "Date", + "ofType": null + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "assigned_by", + "description": "", + "args": [], + "type": { + "kind": "OBJECT", + "name": "User", + "ofType": null + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "created_at", + "description": "", + "args": [], + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "Date", + "ofType": null + } + }, + "isDeprecated": false, + "deprecationReason": null + } + ], + "inputFields": null, + "interfaces": [], + "enumValues": null, + "possibleTypes": null + }, + { + "kind": "OBJECT", + "name": "NotificationSettings", + "description": "", + "fields": [ + { + "name": "digestFrequency", + "description": "", + "args": [], + "type": { + "kind": "ENUM", + "name": "DIGEST_FREQUENCY", + "ofType": null + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "onReply", + "description": "", + "args": [], + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "Boolean", + "ofType": null + } + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "onFeatured", + "description": "", + "args": [], + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "Boolean", + "ofType": null + } + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "onStaffReply", + "description": "", + "args": [], + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "Boolean", + "ofType": null + } + }, + "isDeprecated": false, + "deprecationReason": null + } + ], + "inputFields": null, + "interfaces": [], + "enumValues": null, + "possibleTypes": null + }, + { + "kind": "ENUM", + "name": "DIGEST_FREQUENCY", + "description": "", + "fields": null, + "inputFields": null, + "interfaces": null, + "enumValues": [ + { + "name": "NONE", + "description": "NONE will have the notifications send immediatly rather than bundling\nfor digesting.", + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "HOURLY", + "description": "HOURLY will queue up the notifications and send them hourly.", + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "DAILY", + "description": "DAILY will queue up the notifications and send them daily.", + "isDeprecated": false, + "deprecationReason": null + } + ], + "possibleTypes": null + }, + { + "kind": "INPUT_OBJECT", + "name": "RepliesQuery", + "description": "", + "fields": null, + "inputFields": [ + { + "name": "sortOrder", + "description": "Sort the results by from smallest first.", + "type": { + "kind": "ENUM", + "name": "SORT_ORDER", + "ofType": null + }, + "defaultValue": "ASC" + }, + { + "name": "sortBy", + "description": "The order to sort the comments by, sorting by default the created at\ntimestamp.", + "type": { + "kind": "ENUM", + "name": "SORT_COMMENTS_BY", + "ofType": null + }, + "defaultValue": "CREATED_AT" + }, + { + "name": "limit", + "description": "Limit the number of results to be returned.", + "type": { + "kind": "SCALAR", + "name": "Int", + "ofType": null + }, + "defaultValue": "3" + }, + { + "name": "excludeIgnored", + "description": "Exclude comments ignored by the requesting user", + "type": { + "kind": "SCALAR", + "name": "Boolean", + "ofType": null + }, + "defaultValue": null + } + ], + "interfaces": null, + "enumValues": null, + "possibleTypes": null + }, + { + "kind": "OBJECT", + "name": "Asset", + "description": "Where comments are made on.", + "fields": [ + { + "name": "id", + "description": "The current ID of the asset.", + "args": [], + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "ID", + "ofType": null + } + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "title", + "description": "The scraped title of the asset.", + "args": [], + "type": { + "kind": "SCALAR", + "name": "String", + "ofType": null + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "url", + "description": "The URL that the asset is located on.", + "args": [], + "type": { + "kind": "SCALAR", + "name": "String", + "ofType": null + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "comments", + "description": "The comments that are attached to the asset. When `deep` is true, the\ncomments returned will be at all depths.", + "args": [ + { + "name": "query", + "description": "", + "type": { + "kind": "INPUT_OBJECT", + "name": "CommentsQuery", + "ofType": null + }, + "defaultValue": "{statuses: [NONE, ACCEPTED], limit: 10, sortOrder: DESC, sortBy: CREATED_AT}" + }, + { + "name": "deep", + "description": "", + "type": { + "kind": "SCALAR", + "name": "Boolean", + "ofType": null + }, + "defaultValue": "false" + } + ], + "type": { + "kind": "OBJECT", + "name": "CommentConnection", + "ofType": null + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "comment", + "description": "A Comment from the Asset by comment's ID", + "args": [ + { + "name": "id", + "description": "", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "ID", + "ofType": null + } + }, + "defaultValue": null + } + ], + "type": { + "kind": "OBJECT", + "name": "Comment", + "ofType": null + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "commentCount", + "description": "The count of top level comments on the asset.", + "args": [ + { + "name": "tags", + "description": "", + "type": { + "kind": "LIST", + "name": null, + "ofType": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "String", + "ofType": null + } + } + }, + "defaultValue": null + } + ], + "type": { + "kind": "SCALAR", + "name": "Int", + "ofType": null + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "totalCommentCount", + "description": "The total count of all comments made on the asset.", + "args": [ + { + "name": "tags", + "description": "", + "type": { + "kind": "LIST", + "name": null, + "ofType": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "String", + "ofType": null + } + } + }, + "defaultValue": null + } + ], + "type": { + "kind": "SCALAR", + "name": "Int", + "ofType": null + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "settings", + "description": "The settings (rectified with the global settings) that should be applied to\nthis asset.", + "args": [], + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "OBJECT", + "name": "Settings", + "ofType": null + } + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "closedAt", + "description": "The date that the asset was closed at.", + "args": [], + "type": { + "kind": "SCALAR", + "name": "Date", + "ofType": null + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "isClosed", + "description": "True if asset is closed.", + "args": [], + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "Boolean", + "ofType": null + } + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "action_summaries", + "description": "Summary of all Actions against all entities associated with the Asset.\n(likes, flags, etc.). Requires the `ADMIN` role.", + "args": [], + "type": { + "kind": "LIST", + "name": null, + "ofType": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "INTERFACE", + "name": "AssetActionSummary", + "ofType": null + } + } + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "created_at", + "description": "The date that the asset was created.", + "args": [], + "type": { + "kind": "SCALAR", + "name": "Date", + "ofType": null + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "tags", + "description": "The tags on the asset", + "args": [], + "type": { + "kind": "LIST", + "name": null, + "ofType": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "OBJECT", + "name": "TagLink", + "ofType": null + } + } + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "author", + "description": "The author(s) of the asset.", + "args": [], + "type": { + "kind": "SCALAR", + "name": "String", + "ofType": null + }, + "isDeprecated": false, + "deprecationReason": null + } + ], + "inputFields": null, + "interfaces": [], + "enumValues": null, + "possibleTypes": null + }, + { + "kind": "INTERFACE", + "name": "AssetActionSummary", + "description": "A summary of actions for a specific action type on an Asset.", + "fields": [ + { + "name": "actionCount", + "description": "Number of actions associated with actionable types on this this Asset.", + "args": [], + "type": { + "kind": "SCALAR", + "name": "Int", + "ofType": null + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "actionableItemCount", + "description": "Number of unique actionable types that are referenced by the actions.", + "args": [], + "type": { + "kind": "SCALAR", + "name": "Int", + "ofType": null + }, + "isDeprecated": false, + "deprecationReason": null + } + ], + "inputFields": null, + "interfaces": null, + "enumValues": null, + "possibleTypes": [ + { + "kind": "OBJECT", + "name": "DefaultAssetActionSummary", + "ofType": null + }, + { + "kind": "OBJECT", + "name": "FlagAssetActionSummary", + "ofType": null + }, + { + "kind": "OBJECT", + "name": "RespectAssetActionSummary", + "ofType": null + } + ] + }, + { + "kind": "OBJECT", + "name": "CommentStatusHistory", + "description": "", + "fields": [ + { + "name": "type", + "description": "", + "args": [], + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "ENUM", + "name": "COMMENT_STATUS", + "ofType": null + } + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "created_at", + "description": "", + "args": [], + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "Date", + "ofType": null + } + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "assigned_by", + "description": "", + "args": [], + "type": { + "kind": "OBJECT", + "name": "User", + "ofType": null + }, + "isDeprecated": false, + "deprecationReason": null + } + ], + "inputFields": null, + "interfaces": [], + "enumValues": null, + "possibleTypes": null + }, + { + "kind": "OBJECT", + "name": "EditInfo", + "description": "", + "fields": [ + { + "name": "edited", + "description": "", + "args": [], + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "Boolean", + "ofType": null + } + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "editableUntil", + "description": "", + "args": [], + "type": { + "kind": "SCALAR", + "name": "Date", + "ofType": null + }, + "isDeprecated": false, + "deprecationReason": null + } + ], + "inputFields": null, + "interfaces": [], + "enumValues": null, + "possibleTypes": null + }, + { + "kind": "SCALAR", + "name": "Float", + "description": "The `Float` scalar type represents signed double-precision fractional values as specified by [IEEE 754](http://en.wikipedia.org/wiki/IEEE_floating_point). ", + "fields": null, + "inputFields": null, + "interfaces": null, + "enumValues": null, + "possibleTypes": null + }, + { + "kind": "INPUT_OBJECT", + "name": "AssetsQuery", + "description": "AssetsQuery allows teh ability to query assets by specific fields", + "fields": null, + "inputFields": [ + { + "name": "value", + "description": "a search string to match against titles, authors, urls, etc.", + "type": { + "kind": "SCALAR", + "name": "String", + "ofType": null + }, + "defaultValue": "\"\"" + }, + { + "name": "limit", + "description": "Limit the number of results to be returned", + "type": { + "kind": "SCALAR", + "name": "Int", + "ofType": null + }, + "defaultValue": "10" + }, + { + "name": "open", + "description": "open filters assets that are open/closed/all. Not providing this parameter\nwill return all the assets, true will return assets that are open, and false\nwill return assets that are closed.", + "type": { + "kind": "SCALAR", + "name": "Boolean", + "ofType": null + }, + "defaultValue": null + }, + { + "name": "sortOrder", + "description": "sortOrder specifies the order of the sort for the returned Assets.", + "type": { + "kind": "ENUM", + "name": "SORT_ORDER", + "ofType": null + }, + "defaultValue": "DESC" + }, + { + "name": "cursor", + "description": "Skip results from the last created_at timestamp.", + "type": { + "kind": "SCALAR", + "name": "Cursor", + "ofType": null + }, + "defaultValue": null + } + ], + "interfaces": null, + "enumValues": null, + "possibleTypes": null + }, + { + "kind": "OBJECT", + "name": "AssetConnection", + "description": "AssetConnection represents a paginable subset of a asset list.", + "fields": [ + { + "name": "hasNextPage", + "description": "Indicates that there are more assets after this subset.", + "args": [], + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "Boolean", + "ofType": null + } + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "startCursor", + "description": "Cursor of first asset in subset.", + "args": [], + "type": { + "kind": "SCALAR", + "name": "Cursor", + "ofType": null + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "endCursor", + "description": "Cursor of last asset in subset.", + "args": [], + "type": { + "kind": "SCALAR", + "name": "Cursor", + "ofType": null + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "nodes", + "description": "Subset of assets.", + "args": [], + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "LIST", + "name": null, + "ofType": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "OBJECT", + "name": "Asset", + "ofType": null + } + } + } + }, + "isDeprecated": false, + "deprecationReason": null + } + ], + "inputFields": null, + "interfaces": [], + "enumValues": null, + "possibleTypes": null + }, + { + "kind": "INPUT_OBJECT", + "name": "CommentCountQuery", + "description": "CommentCountQuery allows the ability to query comment counts by specific\nmethods.", + "fields": null, + "inputFields": [ + { + "name": "statuses", + "description": "Current status of a comment.\nThis field is restricted.", + "type": { + "kind": "LIST", + "name": null, + "ofType": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "ENUM", + "name": "COMMENT_STATUS", + "ofType": null + } + } + }, + "defaultValue": "[NONE, ACCEPTED]" + }, + { + "name": "asset_id", + "description": "Asset that a comment is on.", + "type": { + "kind": "SCALAR", + "name": "ID", + "ofType": null + }, + "defaultValue": null + }, + { + "name": "asset_url", + "description": "The URL that the asset is located on.", + "type": { + "kind": "SCALAR", + "name": "String", + "ofType": null + }, + "defaultValue": null + }, + { + "name": "parent_id", + "description": "The parent of the comment that we want to retrieve.", + "type": { + "kind": "SCALAR", + "name": "ID", + "ofType": null + }, + "defaultValue": null + }, + { + "name": "action_type", + "description": "comments returned will only be ones which have at least one action of this\ntype.", + "type": { + "kind": "ENUM", + "name": "ACTION_TYPE", + "ofType": null + }, + "defaultValue": null + }, + { + "name": "author_id", + "description": "author_id allows the querying of comment counts based on the author of the\ncomments.", + "type": { + "kind": "SCALAR", + "name": "ID", + "ofType": null + }, + "defaultValue": null + }, + { + "name": "tags", + "description": "Filter by a specific tag name.", + "type": { + "kind": "LIST", + "name": null, + "ofType": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "String", + "ofType": null + } + } + }, + "defaultValue": null + } + ], + "interfaces": null, + "enumValues": null, + "possibleTypes": null + }, + { + "kind": "INPUT_OBJECT", + "name": "UserCountQuery", + "description": "UserCountQuery allows the ability to query user counts by specific\nmethods.", + "fields": null, + "inputFields": [ + { + "name": "action_type", + "description": "comments returned will only be ones which have at least one action of this\ntype.", + "type": { + "kind": "ENUM", + "name": "ACTION_TYPE", + "ofType": null + }, + "defaultValue": null + }, + { + "name": "state", + "description": "state queries for a specific subset of users with the given state query.", + "type": { + "kind": "INPUT_OBJECT", + "name": "UserStateInput", + "ofType": null + }, + "defaultValue": null + } + ], + "interfaces": null, + "enumValues": null, + "possibleTypes": null + }, + { + "kind": "INPUT_OBJECT", + "name": "UserStateInput", + "description": "", + "fields": null, + "inputFields": [ + { + "name": "status", + "description": "", + "type": { + "kind": "INPUT_OBJECT", + "name": "UserStatusInput", + "ofType": null + }, + "defaultValue": null + } + ], + "interfaces": null, + "enumValues": null, + "possibleTypes": null + }, + { + "kind": "INPUT_OBJECT", + "name": "UserStatusInput", + "description": "UserStatusInput describes the queryable components of the UserStatus.", + "fields": null, + "inputFields": [ + { + "name": "username", + "description": "username will restrict the returned users to only those with the given\nusername status's. If not provided, no filtering will be performed.", + "type": { + "kind": "LIST", + "name": null, + "ofType": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "ENUM", + "name": "USER_STATUS_USERNAME", + "ofType": null + } + } + }, + "defaultValue": null + }, + { + "name": "banned", + "description": "banned will restrict the returned users to only those that are, or are not\nbanned. If not provided, no filtering will be performed.", + "type": { + "kind": "SCALAR", + "name": "Boolean", + "ofType": null + }, + "defaultValue": null + }, + { + "name": "suspended", + "description": "suspended will restrict the returned users to only those that are, or are not\nsuspended. If not provided, no filtering will be performed.", + "type": { + "kind": "SCALAR", + "name": "Boolean", + "ofType": null + }, + "defaultValue": null + } + ], + "interfaces": null, + "enumValues": null, + "possibleTypes": null + }, + { + "kind": "INPUT_OBJECT", + "name": "UsersQuery", + "description": "UsersQuery allows the ability to query users by a specific fields.", + "fields": null, + "inputFields": [ + { + "name": "action_type", + "description": "Users returned will only be ones which have at least one action of this.", + "type": { + "kind": "ENUM", + "name": "ACTION_TYPE", + "ofType": null + }, + "defaultValue": null + }, + { + "name": "state", + "description": "state will filter the users to a specific set of users that meet.", + "type": { + "kind": "INPUT_OBJECT", + "name": "UserStateInput", + "ofType": null + }, + "defaultValue": null + }, + { + "name": "value", + "description": "value is the search string to use to search for a pa", + "type": { + "kind": "SCALAR", + "name": "String", + "ofType": null + }, + "defaultValue": "\"\"" + }, + { + "name": "limit", + "description": "Limit the number of results to be returned.", + "type": { + "kind": "SCALAR", + "name": "Int", + "ofType": null + }, + "defaultValue": "10" + }, + { + "name": "cursor", + "description": "Skip results from the last created_at timestamp.", + "type": { + "kind": "SCALAR", + "name": "Cursor", + "ofType": null + }, + "defaultValue": null + }, + { + "name": "sortOrder", + "description": "Sort the results by created_at.", + "type": { + "kind": "ENUM", + "name": "SORT_ORDER", + "ofType": null + }, + "defaultValue": "DESC" + } + ], + "interfaces": null, + "enumValues": null, + "possibleTypes": null + }, + { + "kind": "OBJECT", + "name": "UserConnection", + "description": "UserConnection represents a paginable subset of a user list.", + "fields": [ + { + "name": "hasNextPage", + "description": "Indicates that there are more users after this subset.", + "args": [], + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "Boolean", + "ofType": null + } + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "startCursor", + "description": "Cursor of first user in subset.", + "args": [], + "type": { + "kind": "SCALAR", + "name": "Cursor", + "ofType": null + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "endCursor", + "description": "Cursor of last user in subset.", + "args": [], + "type": { + "kind": "SCALAR", + "name": "Cursor", + "ofType": null + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "nodes", + "description": "Subset of users.", + "args": [], + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "LIST", + "name": null, + "ofType": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "OBJECT", + "name": "User", + "ofType": null + } + } + } + }, + "isDeprecated": false, + "deprecationReason": null + } + ], + "inputFields": null, + "interfaces": [], + "enumValues": null, + "possibleTypes": null + }, + { + "kind": "OBJECT", + "name": "RootMutation", + "description": "", + "fields": [ + { + "name": "createComment", + "description": "Creates a comment on the asset.", + "args": [ + { + "name": "input", + "description": "", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "INPUT_OBJECT", + "name": "CreateCommentInput", + "ofType": null + } + }, + "defaultValue": null + } + ], + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "OBJECT", + "name": "CreateCommentResponse", + "ofType": null + } + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "createFlag", + "description": "Creates a flag on an entity.", + "args": [ + { + "name": "flag", + "description": "", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "INPUT_OBJECT", + "name": "CreateFlagInput", + "ofType": null + } + }, + "defaultValue": null + } + ], + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "OBJECT", + "name": "CreateFlagResponse", + "ofType": null + } + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "createDontAgree", + "description": "Creates a don't agree action on an entity.", + "args": [ + { + "name": "dontagree", + "description": "", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "INPUT_OBJECT", + "name": "CreateDontAgreeInput", + "ofType": null + } + }, + "defaultValue": null + } + ], + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "OBJECT", + "name": "CreateDontAgreeResponse", + "ofType": null + } + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "deleteAction", + "description": "Delete an action based on the action id.", + "args": [ + { + "name": "id", + "description": "", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "ID", + "ofType": null + } + }, + "defaultValue": null + } + ], + "type": { + "kind": "OBJECT", + "name": "DeleteActionResponse", + "ofType": null + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "editComment", + "description": "Edit a comment", + "args": [ + { + "name": "id", + "description": "", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "ID", + "ofType": null + } + }, + "defaultValue": null + }, + { + "name": "asset_id", + "description": "", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "ID", + "ofType": null + } + }, + "defaultValue": null + }, + { + "name": "edit", + "description": "", + "type": { + "kind": "INPUT_OBJECT", + "name": "EditCommentInput", + "ofType": null + }, + "defaultValue": null + } + ], + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "OBJECT", + "name": "EditCommentResponse", + "ofType": null + } + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "suspendUser", + "description": "Sets the suspension status on a given user. Requires the `MODERATOR` role.\nMutation is restricted.", + "args": [ + { + "name": "input", + "description": "", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "INPUT_OBJECT", + "name": "SuspendUserInput", + "ofType": null + } + }, + "defaultValue": null + } + ], + "type": { + "kind": "OBJECT", + "name": "SuspendUserResponse", + "ofType": null + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "unsuspendUser", + "description": "Sets the suspension status on a given user. Requires the `MODERATOR` role.\nMutation is restricted.", + "args": [ + { + "name": "input", + "description": "", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "INPUT_OBJECT", + "name": "UnsuspendUserInput", + "ofType": null + } + }, + "defaultValue": null + } + ], + "type": { + "kind": "OBJECT", + "name": "UnsuspendUserResponse", + "ofType": null + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "banUser", + "description": "Sets the ban status on a given user. Requires the `MODERATOR` role.\nMutation is restricted.", + "args": [ + { + "name": "input", + "description": "", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "INPUT_OBJECT", + "name": "BanUserInput", + "ofType": null + } + }, + "defaultValue": null + } + ], + "type": { + "kind": "OBJECT", + "name": "BanUsersResponse", + "ofType": null + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "unbanUser", + "description": "Sets the ban status on a given user. Requires the `MODERATOR` role.\nMutation is restricted.", + "args": [ + { + "name": "input", + "description": "", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "INPUT_OBJECT", + "name": "UnbanUserInput", + "ofType": null + } + }, + "defaultValue": null + } + ], + "type": { + "kind": "OBJECT", + "name": "UnbanUserResponse", + "ofType": null + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "approveUsername", + "description": "Sets the username status on a given user to `APPROVED`. Requires the\n`MODERATOR` role. Mutation is restricted.", + "args": [ + { + "name": "id", + "description": "", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "ID", + "ofType": null + } + }, + "defaultValue": null + } + ], + "type": { + "kind": "OBJECT", + "name": "SetUsernameStatusResponse", + "ofType": null + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "rejectUsername", + "description": "Sets the username status on a given user to `REJECTED`. Requires the\n`MODERATOR` role. Mutation is restricted.", + "args": [ + { + "name": "id", + "description": "", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "ID", + "ofType": null + } + }, + "defaultValue": null + } + ], + "type": { + "kind": "OBJECT", + "name": "SetUsernameStatusResponse", + "ofType": null + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "changeUsername", + "description": "Changes the username to the desired username. Mutation is restricted to\nthose users with permission do to so.", + "args": [ + { + "name": "id", + "description": "", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "ID", + "ofType": null + } + }, + "defaultValue": null + }, + { + "name": "username", + "description": "", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "String", + "ofType": null + } + }, + "defaultValue": null + } + ], + "type": { + "kind": "OBJECT", + "name": "ChangeUsernameResponse", + "ofType": null + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "setUsername", + "description": "Sets the username to the desired username if the user has not had a chance\nto set their username. Mutation is restricted to those users with permission\ndo to so that have not done so before.", + "args": [ + { + "name": "id", + "description": "", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "ID", + "ofType": null + } + }, + "defaultValue": null + }, + { + "name": "username", + "description": "", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "String", + "ofType": null + } + }, + "defaultValue": null + } + ], + "type": { + "kind": "OBJECT", + "name": "SetUsernameResponse", + "ofType": null + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "setCommentStatus", + "description": "Sets Comment status. Requires the `ADMIN` role.\nMutation is restricted.", + "args": [ + { + "name": "id", + "description": "", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "ID", + "ofType": null + } + }, + "defaultValue": null + }, + { + "name": "status", + "description": "", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "ENUM", + "name": "COMMENT_STATUS", + "ofType": null + } + }, + "defaultValue": null + } + ], + "type": { + "kind": "OBJECT", + "name": "SetCommentStatusResponse", + "ofType": null + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "addTag", + "description": "Add a tag.", + "args": [ + { + "name": "tag", + "description": "", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "INPUT_OBJECT", + "name": "ModifyTagInput", + "ofType": null + } + }, + "defaultValue": null + } + ], + "type": { + "kind": "OBJECT", + "name": "ModifyTagResponse", + "ofType": null + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "removeTag", + "description": "Removes a tag.", + "args": [ + { + "name": "tag", + "description": "", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "INPUT_OBJECT", + "name": "ModifyTagInput", + "ofType": null + } + }, + "defaultValue": null + } + ], + "type": { + "kind": "OBJECT", + "name": "ModifyTagResponse", + "ofType": null + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "setUserRole", + "description": "Set's a given users role to the one provided. If `null` is passed, the user\nwill not have any role.", + "args": [ + { + "name": "id", + "description": "", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "ID", + "ofType": null + } + }, + "defaultValue": null + }, + { + "name": "role", + "description": "", + "type": { + "kind": "ENUM", + "name": "USER_ROLES", + "ofType": null + }, + "defaultValue": null + } + ], + "type": { + "kind": "OBJECT", + "name": "SetUserRoleResponse", + "ofType": null + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "updateAssetSettings", + "description": "Updates settings on a given asset.\nMutation is restricted.", + "args": [ + { + "name": "id", + "description": "", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "ID", + "ofType": null + } + }, + "defaultValue": null + }, + { + "name": "input", + "description": "", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "INPUT_OBJECT", + "name": "AssetSettingsInput", + "ofType": null + } + }, + "defaultValue": null + } + ], + "type": { + "kind": "OBJECT", + "name": "UpdateAssetSettingsResponse", + "ofType": null + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "updateAssetStatus", + "description": "Updates the status of an asset allowing you to close/reopen an asset for\ncommenting.\nMutation is restricted.", + "args": [ + { + "name": "id", + "description": "", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "ID", + "ofType": null + } + }, + "defaultValue": null + }, + { + "name": "input", + "description": "", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "INPUT_OBJECT", + "name": "UpdateAssetStatusInput", + "ofType": null + } + }, + "defaultValue": null + } + ], + "type": { + "kind": "OBJECT", + "name": "UpdateAssetStatusResponse", + "ofType": null + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "closeAsset", + "description": "closeAsset will close the asset for commenting based on server time.", + "args": [ + { + "name": "id", + "description": "", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "ID", + "ofType": null + } + }, + "defaultValue": null + } + ], + "type": { + "kind": "OBJECT", + "name": "CloseAssetResponse", + "ofType": null + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "updateSettings", + "description": "updateSettings will update the global settings.\nMutation is restricted.", + "args": [ + { + "name": "input", + "description": "", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "INPUT_OBJECT", + "name": "UpdateSettingsInput", + "ofType": null + } + }, + "defaultValue": null + } + ], + "type": { + "kind": "OBJECT", + "name": "UpdateSettingsResponse", + "ofType": null + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "ignoreUser", + "description": "Ignore comments by another user", + "args": [ + { + "name": "id", + "description": "", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "ID", + "ofType": null + } + }, + "defaultValue": null + } + ], + "type": { + "kind": "OBJECT", + "name": "IgnoreUserResponse", + "ofType": null + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "createToken", + "description": "CreateToken will create a token that is attached to the current user.\nMutation is restricted.", + "args": [ + { + "name": "input", + "description": "", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "INPUT_OBJECT", + "name": "CreateTokenInput", + "ofType": null + } + }, + "defaultValue": null + } + ], + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "OBJECT", + "name": "CreateTokenResponse", + "ofType": null + } + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "revokeToken", + "description": "RevokeToken will revoke an existing token.\nMutation is restricted.", + "args": [ + { + "name": "input", + "description": "", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "INPUT_OBJECT", + "name": "RevokeTokenInput", + "ofType": null + } + }, + "defaultValue": null + } + ], + "type": { + "kind": "OBJECT", + "name": "RevokeTokenResponse", + "ofType": null + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "stopIgnoringUser", + "description": "Stop Ignoring comments by another user.", + "args": [ + { + "name": "id", + "description": "", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "ID", + "ofType": null + } + }, + "defaultValue": null + } + ], + "type": { + "kind": "OBJECT", + "name": "StopIgnoringUserResponse", + "ofType": null + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "forceScrapeAsset", + "description": "forceScrapeAsset will force scrape the Asset with the given ID.", + "args": [ + { + "name": "id", + "description": "", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "ID", + "ofType": null + } + }, + "defaultValue": null + } + ], + "type": { + "kind": "OBJECT", + "name": "ForceScrapeAssetResponse", + "ofType": null + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "delUser", + "description": "delUser will delete the user with the specified id.", + "args": [ + { + "name": "id", + "description": "", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "ID", + "ofType": null + } + }, + "defaultValue": null + } + ], + "type": { + "kind": "OBJECT", + "name": "DelUserResponse", + "ofType": null + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "createRespectAction", + "description": "Creates a respect on an entity.", + "args": [ + { + "name": "input", + "description": "", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "INPUT_OBJECT", + "name": "CreateRespectActionInput", + "ofType": null + } + }, + "defaultValue": null + } + ], + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "OBJECT", + "name": "CreateRespectActionResponse", + "ofType": null + } + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "deleteRespectAction", + "description": "", + "args": [ + { + "name": "input", + "description": "", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "INPUT_OBJECT", + "name": "DeleteRespectActionInput", + "ofType": null + } + }, + "defaultValue": null + } + ], + "type": { + "kind": "OBJECT", + "name": "DeleteRespectActionResponse", + "ofType": null + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "updateNotificationSettings", + "description": "updateNotificationSettings will update the current user's notification\nsettings.", + "args": [ + { + "name": "input", + "description": "", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "INPUT_OBJECT", + "name": "NotificationSettingsInput", + "ofType": null + } + }, + "defaultValue": null + } + ], + "type": { + "kind": "OBJECT", + "name": "UpdateNotificationSettingsResponse", + "ofType": null + }, + "isDeprecated": false, + "deprecationReason": null + } + ], + "inputFields": null, + "interfaces": [], + "enumValues": null, + "possibleTypes": null + }, + { + "kind": "INPUT_OBJECT", + "name": "CreateCommentInput", + "description": "", + "fields": null, + "inputFields": [ + { + "name": "asset_id", + "description": "The asset id", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "ID", + "ofType": null + } + }, + "defaultValue": null + }, + { + "name": "parent_id", + "description": "The id of the parent comment", + "type": { + "kind": "SCALAR", + "name": "ID", + "ofType": null + }, + "defaultValue": null + }, + { + "name": "body", + "description": "The body of the comment", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "String", + "ofType": null + } + }, + "defaultValue": null + }, + { + "name": "tags", + "description": "Tags", + "type": { + "kind": "LIST", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "String", + "ofType": null + } + }, + "defaultValue": null + }, + { + "name": "richTextBody", + "description": "", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "String", + "ofType": null + } + }, + "defaultValue": null + }, + { + "name": "checkToxicity", + "description": "If true, the mutation will fail when the\nbody contains toxic language.", + "type": { + "kind": "SCALAR", + "name": "Boolean", + "ofType": null + }, + "defaultValue": null + } + ], + "interfaces": null, + "enumValues": null, + "possibleTypes": null + }, + { + "kind": "OBJECT", + "name": "CreateCommentResponse", + "description": "CreateCommentResponse is returned with the comment that was created and any\nerrors that may have occurred in the attempt to create it.", + "fields": [ + { + "name": "comment", + "description": "The comment that was created.", + "args": [], + "type": { + "kind": "OBJECT", + "name": "Comment", + "ofType": null + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "actions", + "description": "Actions that was assigned during creation of the comment.", + "args": [], + "type": { + "kind": "LIST", + "name": null, + "ofType": { + "kind": "INTERFACE", + "name": "Action", + "ofType": null + } + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "errors", + "description": "An array of errors relating to the mutation that occurred.", + "args": [], + "type": { + "kind": "LIST", + "name": null, + "ofType": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "INTERFACE", + "name": "UserError", + "ofType": null + } + } + }, + "isDeprecated": false, + "deprecationReason": null + } + ], + "inputFields": null, + "interfaces": [ + { + "kind": "INTERFACE", + "name": "Response", + "ofType": null + } + ], + "enumValues": null, + "possibleTypes": null + }, + { + "kind": "INTERFACE", + "name": "Response", + "description": "Response defines what can be expected from any response to a mutation action.", + "fields": [ + { + "name": "errors", + "description": "An array of errors relating to the mutation that occurred.", + "args": [], + "type": { + "kind": "LIST", + "name": null, + "ofType": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "INTERFACE", + "name": "UserError", + "ofType": null + } + } + }, + "isDeprecated": false, + "deprecationReason": null + } + ], + "inputFields": null, + "interfaces": null, + "enumValues": null, + "possibleTypes": [ + { + "kind": "OBJECT", + "name": "CreateCommentResponse", + "ofType": null + }, + { + "kind": "OBJECT", + "name": "CreateFlagResponse", + "ofType": null + }, + { + "kind": "OBJECT", + "name": "CreateDontAgreeResponse", + "ofType": null + }, + { + "kind": "OBJECT", + "name": "DeleteActionResponse", + "ofType": null + }, + { + "kind": "OBJECT", + "name": "EditCommentResponse", + "ofType": null + }, + { + "kind": "OBJECT", + "name": "SuspendUserResponse", + "ofType": null + }, + { + "kind": "OBJECT", + "name": "UnsuspendUserResponse", + "ofType": null + }, + { + "kind": "OBJECT", + "name": "BanUsersResponse", + "ofType": null + }, + { + "kind": "OBJECT", + "name": "UnbanUserResponse", + "ofType": null + }, + { + "kind": "OBJECT", + "name": "SetUsernameStatusResponse", + "ofType": null + }, + { + "kind": "OBJECT", + "name": "ChangeUsernameResponse", + "ofType": null + }, + { + "kind": "OBJECT", + "name": "SetUsernameResponse", + "ofType": null + }, + { + "kind": "OBJECT", + "name": "SetCommentStatusResponse", + "ofType": null + }, + { + "kind": "OBJECT", + "name": "ModifyTagResponse", + "ofType": null + }, + { + "kind": "OBJECT", + "name": "SetUserRoleResponse", + "ofType": null + }, + { + "kind": "OBJECT", + "name": "UpdateAssetSettingsResponse", + "ofType": null + }, + { + "kind": "OBJECT", + "name": "UpdateAssetStatusResponse", + "ofType": null + }, + { + "kind": "OBJECT", + "name": "CloseAssetResponse", + "ofType": null + }, + { + "kind": "OBJECT", + "name": "UpdateSettingsResponse", + "ofType": null + }, + { + "kind": "OBJECT", + "name": "IgnoreUserResponse", + "ofType": null + }, + { + "kind": "OBJECT", + "name": "CreateTokenResponse", + "ofType": null + }, + { + "kind": "OBJECT", + "name": "RevokeTokenResponse", + "ofType": null + }, + { + "kind": "OBJECT", + "name": "StopIgnoringUserResponse", + "ofType": null + }, + { + "kind": "OBJECT", + "name": "ForceScrapeAssetResponse", + "ofType": null + }, + { + "kind": "OBJECT", + "name": "DelUserResponse", + "ofType": null + }, + { + "kind": "OBJECT", + "name": "CreateRespectActionResponse", + "ofType": null + }, + { + "kind": "OBJECT", + "name": "DeleteRespectActionResponse", + "ofType": null + }, + { + "kind": "OBJECT", + "name": "UpdateNotificationSettingsResponse", + "ofType": null + } + ] + }, + { + "kind": "INTERFACE", + "name": "UserError", + "description": "Any error rendered due to the user's input.", + "fields": [ + { + "name": "translation_key", + "description": "Translation key relating to a translatable string containing details to be\ndisplayed to the end user.", + "args": [], + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "String", + "ofType": null + } + }, + "isDeprecated": false, + "deprecationReason": null + } + ], + "inputFields": null, + "interfaces": null, + "enumValues": null, + "possibleTypes": [ + { + "kind": "OBJECT", + "name": "GenericUserError", + "ofType": null + }, + { + "kind": "OBJECT", + "name": "ValidationUserError", + "ofType": null + } + ] + }, + { + "kind": "INPUT_OBJECT", + "name": "CreateFlagInput", + "description": "", + "fields": null, + "inputFields": [ + { + "name": "item_id", + "description": "The item's id for which we are to create a flag.", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "ID", + "ofType": null + } + }, + "defaultValue": null + }, + { + "name": "item_type", + "description": "The type of the item for which we are to create the flag.", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "ENUM", + "name": "ACTION_ITEM_TYPE", + "ofType": null + } + }, + "defaultValue": null + }, + { + "name": "reason", + "description": "The reason for flagging the item.", + "type": { + "kind": "ENUM", + "name": "FLAG_REASON", + "ofType": null + }, + "defaultValue": null + }, + { + "name": "message", + "description": "An optional message sent with the flagging action by the user.", + "type": { + "kind": "SCALAR", + "name": "String", + "ofType": null + }, + "defaultValue": null + } + ], + "interfaces": null, + "enumValues": null, + "possibleTypes": null + }, + { + "kind": "ENUM", + "name": "ACTION_ITEM_TYPE", + "description": "Used to represent the item type for an action.", + "fields": null, + "inputFields": null, + "interfaces": null, + "enumValues": [ + { + "name": "ASSETS", + "description": "The action references a entity of type Asset.", + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "COMMENTS", + "description": "The action references a entity of type Comment.", + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "USERS", + "description": "The action references a entity of type User.", + "isDeprecated": false, + "deprecationReason": null + } + ], + "possibleTypes": null + }, + { + "kind": "ENUM", + "name": "FLAG_REASON", + "description": "", + "fields": null, + "inputFields": null, + "interfaces": null, + "enumValues": [ + { + "name": "USERNAME_OFFENSIVE", + "description": "The current user thinks that the flagged username is offensive.", + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "USERNAME_NOLIKE", + "description": "The current user does not like the flagged username.", + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "USERNAME_IMPERSONATING", + "description": "The current user thinks that the flagged username is being used to\nimpersonate another user.", + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "USERNAME_SPAM", + "description": "The current user thinks that the flagged username is spam.", + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "USERNAME_OTHER", + "description": "The current user thinks that the flagged username is wrong for another\nreason.", + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "COMMENT_OFFENSIVE", + "description": "The current user thinks that the flagged comment is offensive.", + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "COMMENT_SPAM", + "description": "The current user thinks that the flagged comment is spam.", + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "COMMENT_OTHER", + "description": "The current user thinks that the flagged comment is wrong for another\nreason.", + "isDeprecated": false, + "deprecationReason": null + } + ], + "possibleTypes": null + }, + { + "kind": "OBJECT", + "name": "CreateFlagResponse", + "description": "CreateFlagResponse is the response returned with possibly some errors\nrelating to the creating the flag action attempt and possibly the flag that\nwas created.", + "fields": [ + { + "name": "flag", + "description": "The flag that was created.", + "args": [], + "type": { + "kind": "OBJECT", + "name": "FlagAction", + "ofType": null + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "errors", + "description": "An array of errors relating to the mutation that occurred.", + "args": [], + "type": { + "kind": "LIST", + "name": null, + "ofType": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "INTERFACE", + "name": "UserError", + "ofType": null + } + } + }, + "isDeprecated": false, + "deprecationReason": null + } + ], + "inputFields": null, + "interfaces": [ + { + "kind": "INTERFACE", + "name": "Response", + "ofType": null + } + ], + "enumValues": null, + "possibleTypes": null + }, + { + "kind": "OBJECT", + "name": "FlagAction", + "description": "A FLAG action that contains flag metadata.", + "fields": [ + { + "name": "id", + "description": "The ID of the Flag Action.", + "args": [], + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "ID", + "ofType": null + } + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "reason", + "description": "The reason for which the Flag Action was created.", + "args": [], + "type": { + "kind": "SCALAR", + "name": "String", + "ofType": null + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "message", + "description": "An optional message sent with the flagging action by the user.", + "args": [], + "type": { + "kind": "SCALAR", + "name": "String", + "ofType": null + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "user", + "description": "The user who created the action.", + "args": [], + "type": { + "kind": "OBJECT", + "name": "User", + "ofType": null + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "updated_at", + "description": "The time when the Flag Action was updated.", + "args": [], + "type": { + "kind": "SCALAR", + "name": "Date", + "ofType": null + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "created_at", + "description": "The time when the Flag Action was created.", + "args": [], + "type": { + "kind": "SCALAR", + "name": "Date", + "ofType": null + }, + "isDeprecated": false, + "deprecationReason": null + } + ], + "inputFields": null, + "interfaces": [ + { + "kind": "INTERFACE", + "name": "Action", + "ofType": null + } + ], + "enumValues": null, + "possibleTypes": null + }, + { + "kind": "INPUT_OBJECT", + "name": "CreateDontAgreeInput", + "description": "", + "fields": null, + "inputFields": [ + { + "name": "item_id", + "description": "The item's id for which we are to create a don't agree.", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "ID", + "ofType": null + } + }, + "defaultValue": null + }, + { + "name": "item_type", + "description": "The type of the item for which we are to create the don't agree.", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "ENUM", + "name": "ACTION_ITEM_TYPE", + "ofType": null + } + }, + "defaultValue": null + }, + { + "name": "message", + "description": "An optional message sent with the don't agree action by the user.", + "type": { + "kind": "SCALAR", + "name": "String", + "ofType": null + }, + "defaultValue": null + } + ], + "interfaces": null, + "enumValues": null, + "possibleTypes": null + }, + { + "kind": "OBJECT", + "name": "CreateDontAgreeResponse", + "description": "CreateDontAgreeResponse is the response returned with possibly some errors\nrelating to the creating the don't agree action attempt and possibly the don't agree that\nwas created.", + "fields": [ + { + "name": "dontagree", + "description": "The don't agree that was created.", + "args": [], + "type": { + "kind": "OBJECT", + "name": "DontAgreeAction", + "ofType": null + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "errors", + "description": "An array of errors relating to the mutation that occurred.", + "args": [], + "type": { + "kind": "LIST", + "name": null, + "ofType": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "INTERFACE", + "name": "UserError", + "ofType": null + } + } + }, + "isDeprecated": false, + "deprecationReason": null + } + ], + "inputFields": null, + "interfaces": [ + { + "kind": "INTERFACE", + "name": "Response", + "ofType": null + } + ], + "enumValues": null, + "possibleTypes": null + }, + { + "kind": "OBJECT", + "name": "DontAgreeAction", + "description": "A DONTAGREE action that contains do not agree metadata.", + "fields": [ + { + "name": "id", + "description": "The ID of the DontAgree Action.", + "args": [], + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "ID", + "ofType": null + } + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "message", + "description": "An optional message sent with the flagging action by the user.", + "args": [], + "type": { + "kind": "SCALAR", + "name": "String", + "ofType": null + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "user", + "description": "The user who created the action.", + "args": [], + "type": { + "kind": "OBJECT", + "name": "User", + "ofType": null + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "updated_at", + "description": "The time when the DontAgree Action was updated.", + "args": [], + "type": { + "kind": "SCALAR", + "name": "Date", + "ofType": null + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "created_at", + "description": "The time when the DontAgree Action was created.", + "args": [], + "type": { + "kind": "SCALAR", + "name": "Date", + "ofType": null + }, + "isDeprecated": false, + "deprecationReason": null + } + ], + "inputFields": null, + "interfaces": [ + { + "kind": "INTERFACE", + "name": "Action", + "ofType": null + } + ], + "enumValues": null, + "possibleTypes": null + }, + { + "kind": "OBJECT", + "name": "DeleteActionResponse", + "description": "DeleteActionResponse is the response returned with possibly some errors\nrelating to the delete action attempt.", + "fields": [ + { + "name": "errors", + "description": "An array of errors relating to the mutation that occurred.", + "args": [], + "type": { + "kind": "LIST", + "name": null, + "ofType": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "INTERFACE", + "name": "UserError", + "ofType": null + } + } + }, + "isDeprecated": false, + "deprecationReason": null + } + ], + "inputFields": null, + "interfaces": [ + { + "kind": "INTERFACE", + "name": "Response", + "ofType": null + } + ], + "enumValues": null, + "possibleTypes": null + }, + { + "kind": "INPUT_OBJECT", + "name": "EditCommentInput", + "description": "", + "fields": null, + "inputFields": [ + { + "name": "body", + "description": "Update body of the comment", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "String", + "ofType": null + } + }, + "defaultValue": null + }, + { + "name": "richTextBody", + "description": "", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "String", + "ofType": null + } + }, + "defaultValue": null + } + ], + "interfaces": null, + "enumValues": null, + "possibleTypes": null + }, + { + "kind": "OBJECT", + "name": "EditCommentResponse", + "description": "EditCommentResponse contains the updated comment and any errors that occurred.", + "fields": [ + { + "name": "comment", + "description": "The edited comment.", + "args": [], + "type": { + "kind": "OBJECT", + "name": "Comment", + "ofType": null + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "errors", + "description": "An array of errors relating to the mutation that occurred.", + "args": [], + "type": { + "kind": "LIST", + "name": null, + "ofType": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "INTERFACE", + "name": "UserError", + "ofType": null + } + } + }, + "isDeprecated": false, + "deprecationReason": null + } + ], + "inputFields": null, + "interfaces": [ + { + "kind": "INTERFACE", + "name": "Response", + "ofType": null + } + ], + "enumValues": null, + "possibleTypes": null + }, + { + "kind": "INPUT_OBJECT", + "name": "SuspendUserInput", + "description": "", + "fields": null, + "inputFields": [ + { + "name": "id", + "description": "", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "ID", + "ofType": null + } + }, + "defaultValue": null + }, + { + "name": "message", + "description": "", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "String", + "ofType": null + } + }, + "defaultValue": null + }, + { + "name": "until", + "description": "", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "Date", + "ofType": null + } + }, + "defaultValue": null + } + ], + "interfaces": null, + "enumValues": null, + "possibleTypes": null + }, + { + "kind": "OBJECT", + "name": "SuspendUserResponse", + "description": "", + "fields": [ + { + "name": "errors", + "description": "", + "args": [], + "type": { + "kind": "LIST", + "name": null, + "ofType": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "INTERFACE", + "name": "UserError", + "ofType": null + } + } + }, + "isDeprecated": false, + "deprecationReason": null + } + ], + "inputFields": null, + "interfaces": [ + { + "kind": "INTERFACE", + "name": "Response", + "ofType": null + } + ], + "enumValues": null, + "possibleTypes": null + }, + { + "kind": "INPUT_OBJECT", + "name": "UnsuspendUserInput", + "description": "", + "fields": null, + "inputFields": [ + { + "name": "id", + "description": "", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "ID", + "ofType": null + } + }, + "defaultValue": null + } + ], + "interfaces": null, + "enumValues": null, + "possibleTypes": null + }, + { + "kind": "OBJECT", + "name": "UnsuspendUserResponse", + "description": "UnsuspendUserResponse is the response returned with possibly some\nerrors relating to the suspend action attempt.", + "fields": [ + { + "name": "errors", + "description": "An array of errors relating to the mutation that occurred.", + "args": [], + "type": { + "kind": "LIST", + "name": null, + "ofType": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "INTERFACE", + "name": "UserError", + "ofType": null + } + } + }, + "isDeprecated": false, + "deprecationReason": null + } + ], + "inputFields": null, + "interfaces": [ + { + "kind": "INTERFACE", + "name": "Response", + "ofType": null + } + ], + "enumValues": null, + "possibleTypes": null + }, + { + "kind": "INPUT_OBJECT", + "name": "BanUserInput", + "description": "", + "fields": null, + "inputFields": [ + { + "name": "id", + "description": "", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "ID", + "ofType": null + } + }, + "defaultValue": null + }, + { + "name": "message", + "description": "", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "String", + "ofType": null + } + }, + "defaultValue": null + } + ], + "interfaces": null, + "enumValues": null, + "possibleTypes": null + }, + { + "kind": "OBJECT", + "name": "BanUsersResponse", + "description": "", + "fields": [ + { + "name": "errors", + "description": "", + "args": [], + "type": { + "kind": "LIST", + "name": null, + "ofType": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "INTERFACE", + "name": "UserError", + "ofType": null + } + } + }, + "isDeprecated": false, + "deprecationReason": null + } + ], + "inputFields": null, + "interfaces": [ + { + "kind": "INTERFACE", + "name": "Response", + "ofType": null + } + ], + "enumValues": null, + "possibleTypes": null + }, + { + "kind": "INPUT_OBJECT", + "name": "UnbanUserInput", + "description": "", + "fields": null, + "inputFields": [ + { + "name": "id", + "description": "", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "ID", + "ofType": null + } + }, + "defaultValue": null + } + ], + "interfaces": null, + "enumValues": null, + "possibleTypes": null + }, + { + "kind": "OBJECT", + "name": "UnbanUserResponse", + "description": "", + "fields": [ + { + "name": "errors", + "description": "", + "args": [], + "type": { + "kind": "LIST", + "name": null, + "ofType": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "INTERFACE", + "name": "UserError", + "ofType": null + } + } + }, + "isDeprecated": false, + "deprecationReason": null + } + ], + "inputFields": null, + "interfaces": [ + { + "kind": "INTERFACE", + "name": "Response", + "ofType": null + } + ], + "enumValues": null, + "possibleTypes": null + }, + { + "kind": "OBJECT", + "name": "SetUsernameStatusResponse", + "description": "", + "fields": [ + { + "name": "errors", + "description": "An array of errors relating to the mutation that occurred.", + "args": [], + "type": { + "kind": "LIST", + "name": null, + "ofType": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "INTERFACE", + "name": "UserError", + "ofType": null + } + } + }, + "isDeprecated": false, + "deprecationReason": null + } + ], + "inputFields": null, + "interfaces": [ + { + "kind": "INTERFACE", + "name": "Response", + "ofType": null + } + ], + "enumValues": null, + "possibleTypes": null + }, + { + "kind": "OBJECT", + "name": "ChangeUsernameResponse", + "description": "", + "fields": [ + { + "name": "errors", + "description": "An array of errors relating to the mutation that occurred.", + "args": [], + "type": { + "kind": "LIST", + "name": null, + "ofType": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "INTERFACE", + "name": "UserError", + "ofType": null + } + } + }, + "isDeprecated": false, + "deprecationReason": null + } + ], + "inputFields": null, + "interfaces": [ + { + "kind": "INTERFACE", + "name": "Response", + "ofType": null + } + ], + "enumValues": null, + "possibleTypes": null + }, + { + "kind": "OBJECT", + "name": "SetUsernameResponse", + "description": "", + "fields": [ + { + "name": "errors", + "description": "An array of errors relating to the mutation that occurred.", + "args": [], + "type": { + "kind": "LIST", + "name": null, + "ofType": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "INTERFACE", + "name": "UserError", + "ofType": null + } + } + }, + "isDeprecated": false, + "deprecationReason": null + } + ], + "inputFields": null, + "interfaces": [ + { + "kind": "INTERFACE", + "name": "Response", + "ofType": null + } + ], + "enumValues": null, + "possibleTypes": null + }, + { + "kind": "OBJECT", + "name": "SetCommentStatusResponse", + "description": "SetCommentStatusResponse is the response returned with possibly some errors\nrelating to the delete action attempt.", + "fields": [ + { + "name": "errors", + "description": "An array of errors relating to the mutation that occurred.", + "args": [], + "type": { + "kind": "LIST", + "name": null, + "ofType": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "INTERFACE", + "name": "UserError", + "ofType": null + } + } + }, + "isDeprecated": false, + "deprecationReason": null + } + ], + "inputFields": null, + "interfaces": [ + { + "kind": "INTERFACE", + "name": "Response", + "ofType": null + } + ], + "enumValues": null, + "possibleTypes": null + }, + { + "kind": "INPUT_OBJECT", + "name": "ModifyTagInput", + "description": "ModifyTagInput is the input used to modify a tag.", + "fields": null, + "inputFields": [ + { + "name": "name", + "description": "name is the actual tag to add to the model.", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "String", + "ofType": null + } + }, + "defaultValue": null + }, + { + "name": "id", + "description": "id is the ID of the model in question that we are modifying the tag of.", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "ID", + "ofType": null + } + }, + "defaultValue": null + }, + { + "name": "item_type", + "description": "item_type is the type of item that we are modifying the tag if.", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "ENUM", + "name": "TAGGABLE_ITEM_TYPE", + "ofType": null + } + }, + "defaultValue": null + }, + { + "name": "asset_id", + "description": "asset_id is used when the item_type is `COMMENTS`, the is needed to rectify\nThe settings to get the asset specific tags/settings.", + "type": { + "kind": "SCALAR", + "name": "ID", + "ofType": null + }, + "defaultValue": null + } + ], + "interfaces": null, + "enumValues": null, + "possibleTypes": null + }, + { + "kind": "ENUM", + "name": "TAGGABLE_ITEM_TYPE", + "description": "Used to represent the item type for a tag.", + "fields": null, + "inputFields": null, + "interfaces": null, + "enumValues": [ + { + "name": "ASSETS", + "description": "The action references a entity of type Asset.", + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "COMMENTS", + "description": "The action references a entity of type Comment.", + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "USERS", + "description": "The action references a entity of type User.", + "isDeprecated": false, + "deprecationReason": null + } + ], + "possibleTypes": null + }, + { + "kind": "OBJECT", + "name": "ModifyTagResponse", + "description": "Response to the addTag or removeTag mutations.", + "fields": [ + { + "name": "errors", + "description": "An array of errors relating to the mutation that occurred.", + "args": [], + "type": { + "kind": "LIST", + "name": null, + "ofType": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "INTERFACE", + "name": "UserError", + "ofType": null + } + } + }, + "isDeprecated": false, + "deprecationReason": null + } + ], + "inputFields": null, + "interfaces": [ + { + "kind": "INTERFACE", + "name": "Response", + "ofType": null + } + ], + "enumValues": null, + "possibleTypes": null + }, + { + "kind": "OBJECT", + "name": "SetUserRoleResponse", + "description": "", + "fields": [ + { + "name": "errors", + "description": "An array of errors relating to the mutation that occurred.", + "args": [], + "type": { + "kind": "LIST", + "name": null, + "ofType": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "INTERFACE", + "name": "UserError", + "ofType": null + } + } + }, + "isDeprecated": false, + "deprecationReason": null + } + ], + "inputFields": null, + "interfaces": [ + { + "kind": "INTERFACE", + "name": "Response", + "ofType": null + } + ], + "enumValues": null, + "possibleTypes": null + }, + { + "kind": "INPUT_OBJECT", + "name": "AssetSettingsInput", + "description": "Configurable settings that can be overridden for the Asset. You must specify\nall fields that should be updated.", + "fields": null, + "inputFields": [ + { + "name": "premodLinksEnable", + "description": "premodLinksEnable will put all comments that contain links into premod.", + "type": { + "kind": "SCALAR", + "name": "Boolean", + "ofType": null + }, + "defaultValue": null + }, + { + "name": "moderation", + "description": "moderation is the moderation mode for the asset.", + "type": { + "kind": "ENUM", + "name": "MODERATION_MODE", + "ofType": null + }, + "defaultValue": null + }, + { + "name": "questionBoxEnable", + "description": "questionBoxEnable will enable the Question Boxs' content to be visible above\nThe comment box.", + "type": { + "kind": "SCALAR", + "name": "Boolean", + "ofType": null + }, + "defaultValue": null + }, + { + "name": "questionBoxContent", + "description": "questionBoxContent is the content of the Question Box.", + "type": { + "kind": "SCALAR", + "name": "String", + "ofType": null + }, + "defaultValue": null + }, + { + "name": "questionBoxIcon", + "description": "questionBoxIcon is the icon for the Question Box.", + "type": { + "kind": "SCALAR", + "name": "String", + "ofType": null + }, + "defaultValue": null + } + ], + "interfaces": null, + "enumValues": null, + "possibleTypes": null + }, + { + "kind": "OBJECT", + "name": "UpdateAssetSettingsResponse", + "description": "UpdateAssetSettingsResponse is the response returned with possibly some errors\nrelating to the update settings attempt.", + "fields": [ + { + "name": "errors", + "description": "An array of errors relating to the mutation that occurred.", + "args": [], + "type": { + "kind": "LIST", + "name": null, + "ofType": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "INTERFACE", + "name": "UserError", + "ofType": null + } + } + }, + "isDeprecated": false, + "deprecationReason": null + } + ], + "inputFields": null, + "interfaces": [ + { + "kind": "INTERFACE", + "name": "Response", + "ofType": null + } + ], + "enumValues": null, + "possibleTypes": null + }, + { + "kind": "INPUT_OBJECT", + "name": "UpdateAssetStatusInput", + "description": "UpdateAssetStatusInput contains the input to change the status of a comment as\nit relates to being open/closed for commenting.", + "fields": null, + "inputFields": [ + { + "name": "closedAt", + "description": "closedAt is the time that the asset will be closed for commenting. If this\nis null or in the future, it will be open for commenting.", + "type": { + "kind": "SCALAR", + "name": "Date", + "ofType": null + }, + "defaultValue": null + }, + { + "name": "closedMessage", + "description": "closedMessage is the message to be set on the asset when it is closed. If it\nis null, then the message will default to the globally set `closedMessage`.", + "type": { + "kind": "SCALAR", + "name": "String", + "ofType": null + }, + "defaultValue": null + } + ], + "interfaces": null, + "enumValues": null, + "possibleTypes": null + }, + { + "kind": "OBJECT", + "name": "UpdateAssetStatusResponse", + "description": "UpdateAssetStatusResponse is the response returned with possibly some errors\nrelating to the update status attempt.", + "fields": [ + { + "name": "errors", + "description": "An array of errors relating to the mutation that occurred.", + "args": [], + "type": { + "kind": "LIST", + "name": null, + "ofType": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "INTERFACE", + "name": "UserError", + "ofType": null + } + } + }, + "isDeprecated": false, + "deprecationReason": null + } + ], + "inputFields": null, + "interfaces": [ + { + "kind": "INTERFACE", + "name": "Response", + "ofType": null + } + ], + "enumValues": null, + "possibleTypes": null + }, + { + "kind": "OBJECT", + "name": "CloseAssetResponse", + "description": "CloseAssetResponse is the response returned with possibly some errors\nrelating to the update status attempt.", + "fields": [ + { + "name": "errors", + "description": "An array of errors relating to the mutation that occurred.", + "args": [], + "type": { + "kind": "LIST", + "name": null, + "ofType": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "INTERFACE", + "name": "UserError", + "ofType": null + } + } + }, + "isDeprecated": false, + "deprecationReason": null + } + ], + "inputFields": null, + "interfaces": [ + { + "kind": "INTERFACE", + "name": "Response", + "ofType": null + } + ], + "enumValues": null, + "possibleTypes": null + }, + { + "kind": "INPUT_OBJECT", + "name": "UpdateSettingsInput", + "description": "UpdateSettingsInput is the input used to input the global site settings. This\nwill override the existing settings, so all fields must be included.", + "fields": null, + "inputFields": [ + { + "name": "moderation", + "description": "moderation is the moderation mode for all Asset's on the site.", + "type": { + "kind": "ENUM", + "name": "MODERATION_MODE", + "ofType": null + }, + "defaultValue": null + }, + { + "name": "requireEmailConfirmation", + "description": "Enables a requirement for email confirmation before a user can login.", + "type": { + "kind": "SCALAR", + "name": "Boolean", + "ofType": null + }, + "defaultValue": null + }, + { + "name": "infoBoxEnable", + "description": "infoBoxEnable will enable the Info Box content visible above the question\nbox.", + "type": { + "kind": "SCALAR", + "name": "Boolean", + "ofType": null + }, + "defaultValue": null + }, + { + "name": "infoBoxContent", + "description": "infoBoxContent is the content of the Info Box.", + "type": { + "kind": "SCALAR", + "name": "String", + "ofType": null + }, + "defaultValue": null + }, + { + "name": "questionBoxEnable", + "description": "questionBoxEnable will enable the Question Box's content to be visible above\nThe comment box.", + "type": { + "kind": "SCALAR", + "name": "Boolean", + "ofType": null + }, + "defaultValue": null + }, + { + "name": "questionBoxContent", + "description": "questionBoxContent is the content of the Question Box.", + "type": { + "kind": "SCALAR", + "name": "String", + "ofType": null + }, + "defaultValue": null + }, + { + "name": "premodLinksEnable", + "description": "premodLinksEnable will put all comments that contain links into premod.", + "type": { + "kind": "SCALAR", + "name": "Boolean", + "ofType": null + }, + "defaultValue": null + }, + { + "name": "questionBoxIcon", + "description": "questionBoxIcon is the icon for the Question Box.", + "type": { + "kind": "SCALAR", + "name": "String", + "ofType": null + }, + "defaultValue": null + }, + { + "name": "autoCloseStream", + "description": "autoCloseStream when true will auto close the stream when the `closeTimeout`\namount of seconds have been reached.", + "type": { + "kind": "SCALAR", + "name": "Boolean", + "ofType": null + }, + "defaultValue": null + }, + { + "name": "customCssUrl", + "description": "customCssUrl is the URL of the custom CSS used to display on the frontend.", + "type": { + "kind": "SCALAR", + "name": "String", + "ofType": null + }, + "defaultValue": null + }, + { + "name": "closedTimeout", + "description": "closedTimeout is the amount of seconds from the created_at timestamp that a\ngiven asset will be considered closed.", + "type": { + "kind": "SCALAR", + "name": "Int", + "ofType": null + }, + "defaultValue": null + }, + { + "name": "closedMessage", + "description": "closedMessage is the message shown to the user when the given Asset is\nclosed.", + "type": { + "kind": "SCALAR", + "name": "String", + "ofType": null + }, + "defaultValue": null + }, + { + "name": "charCountEnable", + "description": "charCountEnable is true when the character count restriction is enabled.", + "type": { + "kind": "SCALAR", + "name": "Boolean", + "ofType": null + }, + "defaultValue": null + }, + { + "name": "charCount", + "description": "charCount is the maximum number of characters a comment may be.", + "type": { + "kind": "SCALAR", + "name": "Int", + "ofType": null + }, + "defaultValue": null + }, + { + "name": "organizationName", + "description": "organizationName is the name of the organization.", + "type": { + "kind": "SCALAR", + "name": "String", + "ofType": null + }, + "defaultValue": null + }, + { + "name": "editCommentWindowLength", + "description": "editCommentWindowLength is the length of time (in milliseconds) after a\ncomment is posted that it can still be edited by the author.", + "type": { + "kind": "SCALAR", + "name": "Int", + "ofType": null + }, + "defaultValue": null + }, + { + "name": "wordlist", + "description": "wordlist allows changing the available wordlists.", + "type": { + "kind": "INPUT_OBJECT", + "name": "UpdateWordlistInput", + "ofType": null + }, + "defaultValue": null + }, + { + "name": "domains", + "description": "domains allows changing the available lists of domains.", + "type": { + "kind": "INPUT_OBJECT", + "name": "UpdateDomainsInput", + "ofType": null + }, + "defaultValue": null + } + ], + "interfaces": null, + "enumValues": null, + "possibleTypes": null + }, + { + "kind": "INPUT_OBJECT", + "name": "UpdateWordlistInput", + "description": "UpdateWordlistInput is the list of words that composes the Wordlist.", + "fields": null, + "inputFields": [ + { + "name": "banned", + "description": "banned words will by default reject the comment if it is found.", + "type": { + "kind": "LIST", + "name": null, + "ofType": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "String", + "ofType": null + } + } + }, + "defaultValue": null + }, + { + "name": "suspect", + "description": "suspect words will simply flag the comment.", + "type": { + "kind": "LIST", + "name": null, + "ofType": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "String", + "ofType": null + } + } + }, + "defaultValue": null + } + ], + "interfaces": null, + "enumValues": null, + "possibleTypes": null + }, + { + "kind": "INPUT_OBJECT", + "name": "UpdateDomainsInput", + "description": "UpdateDomainsInput describes all the available lists of domains.", + "fields": null, + "inputFields": [ + { + "name": "whitelist", + "description": "whitelist is the list of domains that the embed is allowed to render on.", + "type": { + "kind": "LIST", + "name": null, + "ofType": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "String", + "ofType": null + } + } + }, + "defaultValue": null + } + ], + "interfaces": null, + "enumValues": null, + "possibleTypes": null + }, + { + "kind": "OBJECT", + "name": "UpdateSettingsResponse", + "description": "UpdateSettingsResponse contains any errors that were rendered as a result\nof the mutation.", + "fields": [ + { + "name": "errors", + "description": "An array of errors relating to the mutation that occurred.", + "args": [], + "type": { + "kind": "LIST", + "name": null, + "ofType": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "INTERFACE", + "name": "UserError", + "ofType": null + } + } + }, + "isDeprecated": false, + "deprecationReason": null + } + ], + "inputFields": null, + "interfaces": [ + { + "kind": "INTERFACE", + "name": "Response", + "ofType": null + } + ], + "enumValues": null, + "possibleTypes": null + }, + { + "kind": "OBJECT", + "name": "IgnoreUserResponse", + "description": "Response to ignoreUser mutation", + "fields": [ + { + "name": "errors", + "description": "An array of errors relating to the mutation that occurred.", + "args": [], + "type": { + "kind": "LIST", + "name": null, + "ofType": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "INTERFACE", + "name": "UserError", + "ofType": null + } + } + }, + "isDeprecated": false, + "deprecationReason": null + } + ], + "inputFields": null, + "interfaces": [ + { + "kind": "INTERFACE", + "name": "Response", + "ofType": null + } + ], + "enumValues": null, + "possibleTypes": null + }, + { + "kind": "INPUT_OBJECT", + "name": "CreateTokenInput", + "description": "CreateTokenInput contains the input to create the token.", + "fields": null, + "inputFields": [ + { + "name": "name", + "description": "Name is the description for the token.", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "String", + "ofType": null + } + }, + "defaultValue": null + } + ], + "interfaces": null, + "enumValues": null, + "possibleTypes": null + }, + { + "kind": "OBJECT", + "name": "CreateTokenResponse", + "description": "CreateTokenResponse contains the errors related to creating a token.", + "fields": [ + { + "name": "token", + "description": "Token is the Token that was created, or null if it failed.", + "args": [], + "type": { + "kind": "OBJECT", + "name": "Token", + "ofType": null + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "errors", + "description": "An array of errors relating to the mutation that occurred.", + "args": [], + "type": { + "kind": "LIST", + "name": null, + "ofType": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "INTERFACE", + "name": "UserError", + "ofType": null + } + } + }, + "isDeprecated": false, + "deprecationReason": null + } + ], + "inputFields": null, + "interfaces": [ + { + "kind": "INTERFACE", + "name": "Response", + "ofType": null + } + ], + "enumValues": null, + "possibleTypes": null + }, + { + "kind": "INPUT_OBJECT", + "name": "RevokeTokenInput", + "description": "RevokeTokenInput contains the input to revoke the token.", + "fields": null, + "inputFields": [ + { + "name": "id", + "description": "ID is the JTI for the token.", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "ID", + "ofType": null + } + }, + "defaultValue": null + } + ], + "interfaces": null, + "enumValues": null, + "possibleTypes": null + }, + { + "kind": "OBJECT", + "name": "RevokeTokenResponse", + "description": "RevokeTokenResponse contains the errors related to revoking a token.", + "fields": [ + { + "name": "errors", + "description": "An array of errors relating to the mutation that occurred.", + "args": [], + "type": { + "kind": "LIST", + "name": null, + "ofType": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "INTERFACE", + "name": "UserError", + "ofType": null + } + } + }, + "isDeprecated": false, + "deprecationReason": null + } + ], + "inputFields": null, + "interfaces": [ + { + "kind": "INTERFACE", + "name": "Response", + "ofType": null + } + ], + "enumValues": null, + "possibleTypes": null + }, + { + "kind": "OBJECT", + "name": "StopIgnoringUserResponse", + "description": "Response to stopIgnoringUser mutation", + "fields": [ + { + "name": "errors", + "description": "An array of errors relating to the mutation that occurred.", + "args": [], + "type": { + "kind": "LIST", + "name": null, + "ofType": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "INTERFACE", + "name": "UserError", + "ofType": null + } + } + }, + "isDeprecated": false, + "deprecationReason": null + } + ], + "inputFields": null, + "interfaces": [ + { + "kind": "INTERFACE", + "name": "Response", + "ofType": null + } + ], + "enumValues": null, + "possibleTypes": null + }, + { + "kind": "OBJECT", + "name": "ForceScrapeAssetResponse", + "description": "", + "fields": [ + { + "name": "errors", + "description": "An array of errors relating to the mutation that occurred.", + "args": [], + "type": { + "kind": "LIST", + "name": null, + "ofType": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "INTERFACE", + "name": "UserError", + "ofType": null + } + } + }, + "isDeprecated": false, + "deprecationReason": null + } + ], + "inputFields": null, + "interfaces": [ + { + "kind": "INTERFACE", + "name": "Response", + "ofType": null + } + ], + "enumValues": null, + "possibleTypes": null + }, + { + "kind": "OBJECT", + "name": "DelUserResponse", + "description": "", + "fields": [ + { + "name": "errors", + "description": "An array of errors relating to the mutation that occurred.", + "args": [], + "type": { + "kind": "LIST", + "name": null, + "ofType": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "INTERFACE", + "name": "UserError", + "ofType": null + } + } + }, + "isDeprecated": false, + "deprecationReason": null + } + ], + "inputFields": null, + "interfaces": [ + { + "kind": "INTERFACE", + "name": "Response", + "ofType": null + } + ], + "enumValues": null, + "possibleTypes": null + }, + { + "kind": "INPUT_OBJECT", + "name": "CreateRespectActionInput", + "description": "", + "fields": null, + "inputFields": [ + { + "name": "item_id", + "description": "The item's id for which we are to create a respect.", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "ID", + "ofType": null + } + }, + "defaultValue": null + } + ], + "interfaces": null, + "enumValues": null, + "possibleTypes": null + }, + { + "kind": "OBJECT", + "name": "CreateRespectActionResponse", + "description": "", + "fields": [ + { + "name": "respect", + "description": "The respect that was created.", + "args": [], + "type": { + "kind": "OBJECT", + "name": "RespectAction", + "ofType": null + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "errors", + "description": "An array of errors relating to the mutation that occurred.", + "args": [], + "type": { + "kind": "LIST", + "name": null, + "ofType": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "INTERFACE", + "name": "UserError", + "ofType": null + } + } + }, + "isDeprecated": false, + "deprecationReason": null + } + ], + "inputFields": null, + "interfaces": [ + { + "kind": "INTERFACE", + "name": "Response", + "ofType": null + } + ], + "enumValues": null, + "possibleTypes": null + }, + { + "kind": "OBJECT", + "name": "RespectAction", + "description": "RespectAction is used by users who \"respect\" a specific entity.", + "fields": [ + { + "name": "id", + "description": "The ID of the action.", + "args": [], + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "ID", + "ofType": null + } + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "user", + "description": "The author of the action.", + "args": [], + "type": { + "kind": "OBJECT", + "name": "User", + "ofType": null + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "updated_at", + "description": "The time when the Action was updated.", + "args": [], + "type": { + "kind": "SCALAR", + "name": "Date", + "ofType": null + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "created_at", + "description": "The time when the Action was created.", + "args": [], + "type": { + "kind": "SCALAR", + "name": "Date", + "ofType": null + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "item_id", + "description": "The item's id for which the Action was created.", + "args": [], + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "ID", + "ofType": null + } + }, + "isDeprecated": false, + "deprecationReason": null + } + ], + "inputFields": null, + "interfaces": [ + { + "kind": "INTERFACE", + "name": "Action", + "ofType": null + } + ], + "enumValues": null, + "possibleTypes": null + }, + { + "kind": "INPUT_OBJECT", + "name": "DeleteRespectActionInput", + "description": "", + "fields": null, + "inputFields": [ + { + "name": "id", + "description": "The item's id for which we are deleting a respect.", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "ID", + "ofType": null + } + }, + "defaultValue": null + } + ], + "interfaces": null, + "enumValues": null, + "possibleTypes": null + }, + { + "kind": "OBJECT", + "name": "DeleteRespectActionResponse", + "description": "", + "fields": [ + { + "name": "errors", + "description": "An array of errors relating to the mutation that occurred.", + "args": [], + "type": { + "kind": "LIST", + "name": null, + "ofType": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "INTERFACE", + "name": "UserError", + "ofType": null + } + } + }, + "isDeprecated": false, + "deprecationReason": null + } + ], + "inputFields": null, + "interfaces": [ + { + "kind": "INTERFACE", + "name": "Response", + "ofType": null + } + ], + "enumValues": null, + "possibleTypes": null + }, + { + "kind": "INPUT_OBJECT", + "name": "NotificationSettingsInput", + "description": "", + "fields": null, + "inputFields": [ + { + "name": "digestFrequency", + "description": "digestFrequency is the frequency to send notifications.", + "type": { + "kind": "ENUM", + "name": "DIGEST_FREQUENCY", + "ofType": null + }, + "defaultValue": null + }, + { + "name": "onReply", + "description": "", + "type": { + "kind": "SCALAR", + "name": "Boolean", + "ofType": null + }, + "defaultValue": null + }, + { + "name": "onFeatured", + "description": "", + "type": { + "kind": "SCALAR", + "name": "Boolean", + "ofType": null + }, + "defaultValue": null + }, + { + "name": "onStaffReply", + "description": "", + "type": { + "kind": "SCALAR", + "name": "Boolean", + "ofType": null + }, + "defaultValue": null + } + ], + "interfaces": null, + "enumValues": null, + "possibleTypes": null + }, + { + "kind": "OBJECT", + "name": "UpdateNotificationSettingsResponse", + "description": "", + "fields": [ + { + "name": "errors", + "description": "An array of errors relating to the mutation that occurred.", + "args": [], + "type": { + "kind": "LIST", + "name": null, + "ofType": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "INTERFACE", + "name": "UserError", + "ofType": null + } + } + }, + "isDeprecated": false, + "deprecationReason": null + } + ], + "inputFields": null, + "interfaces": [ + { + "kind": "INTERFACE", + "name": "Response", + "ofType": null + } + ], + "enumValues": null, + "possibleTypes": null + }, + { + "kind": "OBJECT", + "name": "Subscription", + "description": "", + "fields": [ + { + "name": "commentAdded", + "description": "Get an update whenever a comment was added.\n`asset_id` is required except for users with the `ADMIN` or `MODERATOR` role.\nNon privileged user can only subscribe to 'NONE' and/or 'ACCEPTED' statuses.", + "args": [ + { + "name": "asset_id", + "description": "", + "type": { + "kind": "SCALAR", + "name": "ID", + "ofType": null + }, + "defaultValue": null + }, + { + "name": "statuses", + "description": "", + "type": { + "kind": "LIST", + "name": null, + "ofType": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "ENUM", + "name": "COMMENT_STATUS", + "ofType": null + } + } + }, + "defaultValue": "[NONE, ACCEPTED]" + } + ], + "type": { + "kind": "OBJECT", + "name": "Comment", + "ofType": null + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "commentEdited", + "description": "Get an update whenever a comment was edited.\n`asset_id` is required except for users with the `ADMIN` or `MODERATOR` role.", + "args": [ + { + "name": "asset_id", + "description": "", + "type": { + "kind": "SCALAR", + "name": "ID", + "ofType": null + }, + "defaultValue": null + } + ], + "type": { + "kind": "OBJECT", + "name": "Comment", + "ofType": null + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "commentFlagged", + "description": "Get an update whenever a comment was flagged.\nRequires the `ADMIN` or `MODERATOR` role.", + "args": [ + { + "name": "asset_id", + "description": "", + "type": { + "kind": "SCALAR", + "name": "ID", + "ofType": null + }, + "defaultValue": null + } + ], + "type": { + "kind": "OBJECT", + "name": "Comment", + "ofType": null + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "commentAccepted", + "description": "Get an update whenever a comment has been accepted.\nRequires the `ADMIN` or `MODERATOR` role.", + "args": [ + { + "name": "asset_id", + "description": "", + "type": { + "kind": "SCALAR", + "name": "ID", + "ofType": null + }, + "defaultValue": null + } + ], + "type": { + "kind": "OBJECT", + "name": "Comment", + "ofType": null + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "commentRejected", + "description": "Get an update whenever a comment has been rejected.\nRequires the `ADMIN` or `MODERATOR` role.", + "args": [ + { + "name": "asset_id", + "description": "", + "type": { + "kind": "SCALAR", + "name": "ID", + "ofType": null + }, + "defaultValue": null + } + ], + "type": { + "kind": "OBJECT", + "name": "Comment", + "ofType": null + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "commentReset", + "description": "Get an update whenever the status of a comment has been reset.\nRequires the `ADMIN` or `MODERATOR` role.", + "args": [ + { + "name": "asset_id", + "description": "", + "type": { + "kind": "SCALAR", + "name": "ID", + "ofType": null + }, + "defaultValue": null + } + ], + "type": { + "kind": "OBJECT", + "name": "Comment", + "ofType": null + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "userSuspended", + "description": "Get an update whenever a user has been suspended.\n`user_id` must match id of current user except for\nusers with the `ADMIN` or `MODERATOR` role.", + "args": [ + { + "name": "user_id", + "description": "", + "type": { + "kind": "SCALAR", + "name": "ID", + "ofType": null + }, + "defaultValue": null + } + ], + "type": { + "kind": "OBJECT", + "name": "User", + "ofType": null + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "userBanned", + "description": "Get an update whenever a user has been banned.\n`user_id` must match id of current user except for\nusers with the `ADMIN` or `MODERATOR` role.", + "args": [ + { + "name": "user_id", + "description": "", + "type": { + "kind": "SCALAR", + "name": "ID", + "ofType": null + }, + "defaultValue": null + } + ], + "type": { + "kind": "OBJECT", + "name": "User", + "ofType": null + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "usernameFlagged", + "description": "Get an update whenever a username was flagged.\n`user_id` must match id of current user except for\nusers with the `ADMIN` or `MODERATOR` role.", + "args": [ + { + "name": "user_id", + "description": "", + "type": { + "kind": "SCALAR", + "name": "ID", + "ofType": null + }, + "defaultValue": null + } + ], + "type": { + "kind": "OBJECT", + "name": "User", + "ofType": null + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "usernameRejected", + "description": "Get an update whenever a username has been rejected.\n`user_id` must match id of current user except for\nusers with the `ADMIN` or `MODERATOR` role.", + "args": [ + { + "name": "user_id", + "description": "", + "type": { + "kind": "SCALAR", + "name": "ID", + "ofType": null + }, + "defaultValue": null + } + ], + "type": { + "kind": "OBJECT", + "name": "User", + "ofType": null + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "usernameApproved", + "description": "Get an update whenever a username has been approved. `user_id` must match id\nof current user except for users with the `ADMIN` or `MODERATOR` role.", + "args": [ + { + "name": "user_id", + "description": "", + "type": { + "kind": "SCALAR", + "name": "ID", + "ofType": null + }, + "defaultValue": null + } + ], + "type": { + "kind": "OBJECT", + "name": "User", + "ofType": null + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "usernameChanged", + "description": "Get an update whenever a username has been changed. `user_id` must match id\nof current user except for users with the `ADMIN` or `MODERATOR` role.", + "args": [ + { + "name": "user_id", + "description": "", + "type": { + "kind": "SCALAR", + "name": "ID", + "ofType": null + }, + "defaultValue": null + } + ], + "type": { + "kind": "OBJECT", + "name": "UsernameChangedPayload", + "ofType": null + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "userCreated", + "description": "Get an update whenever a user is created. Only accessible to users with the\n`ADMIN` or `MODERATOR` roles.", + "args": [], + "type": { + "kind": "OBJECT", + "name": "User", + "ofType": null + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "respectActionCreated", + "description": "Subscribe to respects.", + "args": [ + { + "name": "asset_id", + "description": "", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "ID", + "ofType": null + } + }, + "defaultValue": null + } + ], + "type": { + "kind": "OBJECT", + "name": "RespectAction", + "ofType": null + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "respectActionDeleted", + "description": "Subscribe to respect removals.", + "args": [ + { + "name": "asset_id", + "description": "", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "ID", + "ofType": null + } + }, + "defaultValue": null + } + ], + "type": { + "kind": "OBJECT", + "name": "RespectAction", + "ofType": null + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "commentFeatured", + "description": "Subscribe to featured comments.", + "args": [ + { + "name": "asset_id", + "description": "", + "type": { + "kind": "SCALAR", + "name": "ID", + "ofType": null + }, + "defaultValue": null + } + ], + "type": { + "kind": "OBJECT", + "name": "CommentFeaturedData", + "ofType": null + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "commentUnfeatured", + "description": "Subscribe to featured comments.", + "args": [ + { + "name": "asset_id", + "description": "", + "type": { + "kind": "SCALAR", + "name": "ID", + "ofType": null + }, + "defaultValue": null + } + ], + "type": { + "kind": "OBJECT", + "name": "CommentUnfeaturedData", + "ofType": null + }, + "isDeprecated": false, + "deprecationReason": null + } + ], + "inputFields": null, + "interfaces": [], + "enumValues": null, + "possibleTypes": null + }, + { + "kind": "OBJECT", + "name": "UsernameChangedPayload", + "description": "", + "fields": [ + { + "name": "previousUsername", + "description": "", + "args": [], + "type": { + "kind": "SCALAR", + "name": "String", + "ofType": null + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "user", + "description": "", + "args": [], + "type": { + "kind": "OBJECT", + "name": "User", + "ofType": null + }, + "isDeprecated": false, + "deprecationReason": null + } + ], + "inputFields": null, + "interfaces": [], + "enumValues": null, + "possibleTypes": null + }, + { + "kind": "OBJECT", + "name": "CommentFeaturedData", + "description": "", + "fields": [ + { + "name": "comment", + "description": "", + "args": [], + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "OBJECT", + "name": "Comment", + "ofType": null + } + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "user", + "description": "", + "args": [], + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "OBJECT", + "name": "User", + "ofType": null + } + }, + "isDeprecated": false, + "deprecationReason": null + } + ], + "inputFields": null, + "interfaces": [], + "enumValues": null, + "possibleTypes": null + }, + { + "kind": "OBJECT", + "name": "CommentUnfeaturedData", + "description": "", + "fields": [ + { + "name": "comment", + "description": "", + "args": [], + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "OBJECT", + "name": "Comment", + "ofType": null + } + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "user", + "description": "", + "args": [], + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "OBJECT", + "name": "User", + "ofType": null + } + }, + "isDeprecated": false, + "deprecationReason": null + } + ], + "inputFields": null, + "interfaces": [], + "enumValues": null, + "possibleTypes": null + }, + { + "kind": "OBJECT", + "name": "__Schema", + "description": "A GraphQL Schema defines the capabilities of a GraphQL server. It exposes all available types and directives on the server, as well as the entry points for query, mutation, and subscription operations.", + "fields": [ + { + "name": "types", + "description": "A list of all types supported by this server.", + "args": [], + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "LIST", + "name": null, + "ofType": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "OBJECT", + "name": "__Type", + "ofType": null + } + } + } + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "queryType", + "description": "The type that query operations will be rooted at.", + "args": [], + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "OBJECT", + "name": "__Type", + "ofType": null + } + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "mutationType", + "description": "If this server supports mutation, the type that mutation operations will be rooted at.", + "args": [], + "type": { + "kind": "OBJECT", + "name": "__Type", + "ofType": null + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "subscriptionType", + "description": "If this server support subscription, the type that subscription operations will be rooted at.", + "args": [], + "type": { + "kind": "OBJECT", + "name": "__Type", + "ofType": null + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "directives", + "description": "A list of all directives supported by this server.", + "args": [], + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "LIST", + "name": null, + "ofType": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "OBJECT", + "name": "__Directive", + "ofType": null + } + } + } + }, + "isDeprecated": false, + "deprecationReason": null + } + ], + "inputFields": null, + "interfaces": [], + "enumValues": null, + "possibleTypes": null + }, + { + "kind": "OBJECT", + "name": "__Type", + "description": "The fundamental unit of any GraphQL Schema is the type. There are many kinds of types in GraphQL as represented by the `__TypeKind` enum.\n\nDepending on the kind of a type, certain fields describe information about that type. Scalar types provide no information beyond a name and description, while Enum types provide their values. Object and Interface types provide the fields they describe. Abstract types, Union and Interface, provide the Object types possible at runtime. List and NonNull types compose other types.", + "fields": [ + { + "name": "kind", + "description": null, + "args": [], + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "ENUM", + "name": "__TypeKind", + "ofType": null + } + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "name", + "description": null, + "args": [], + "type": { + "kind": "SCALAR", + "name": "String", + "ofType": null + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "description", + "description": null, + "args": [], + "type": { + "kind": "SCALAR", + "name": "String", + "ofType": null + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "fields", + "description": null, + "args": [ + { + "name": "includeDeprecated", + "description": null, + "type": { + "kind": "SCALAR", + "name": "Boolean", + "ofType": null + }, + "defaultValue": "false" + } + ], + "type": { + "kind": "LIST", + "name": null, + "ofType": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "OBJECT", + "name": "__Field", + "ofType": null + } + } + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "interfaces", + "description": null, + "args": [], + "type": { + "kind": "LIST", + "name": null, + "ofType": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "OBJECT", + "name": "__Type", + "ofType": null + } + } + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "possibleTypes", + "description": null, + "args": [], + "type": { + "kind": "LIST", + "name": null, + "ofType": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "OBJECT", + "name": "__Type", + "ofType": null + } + } + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "enumValues", + "description": null, + "args": [ + { + "name": "includeDeprecated", + "description": null, + "type": { + "kind": "SCALAR", + "name": "Boolean", + "ofType": null + }, + "defaultValue": "false" + } + ], + "type": { + "kind": "LIST", + "name": null, + "ofType": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "OBJECT", + "name": "__EnumValue", + "ofType": null + } + } + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "inputFields", + "description": null, + "args": [], + "type": { + "kind": "LIST", + "name": null, + "ofType": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "OBJECT", + "name": "__InputValue", + "ofType": null + } + } + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "ofType", + "description": null, + "args": [], + "type": { + "kind": "OBJECT", + "name": "__Type", + "ofType": null + }, + "isDeprecated": false, + "deprecationReason": null + } + ], + "inputFields": null, + "interfaces": [], + "enumValues": null, + "possibleTypes": null + }, + { + "kind": "ENUM", + "name": "__TypeKind", + "description": "An enum describing what kind of type a given `__Type` is.", + "fields": null, + "inputFields": null, + "interfaces": null, + "enumValues": [ + { + "name": "SCALAR", + "description": "Indicates this type is a scalar.", + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "OBJECT", + "description": "Indicates this type is an object. `fields` and `interfaces` are valid fields.", + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "INTERFACE", + "description": "Indicates this type is an interface. `fields` and `possibleTypes` are valid fields.", + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "UNION", + "description": "Indicates this type is a union. `possibleTypes` is a valid field.", + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "ENUM", + "description": "Indicates this type is an enum. `enumValues` is a valid field.", + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "INPUT_OBJECT", + "description": "Indicates this type is an input object. `inputFields` is a valid field.", + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "LIST", + "description": "Indicates this type is a list. `ofType` is a valid field.", + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "NON_NULL", + "description": "Indicates this type is a non-null. `ofType` is a valid field.", + "isDeprecated": false, + "deprecationReason": null + } + ], + "possibleTypes": null + }, + { + "kind": "OBJECT", + "name": "__Field", + "description": "Object and Interface types are described by a list of Fields, each of which has a name, potentially a list of arguments, and a return type.", + "fields": [ + { + "name": "name", + "description": null, + "args": [], + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "String", + "ofType": null + } + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "description", + "description": null, + "args": [], + "type": { + "kind": "SCALAR", + "name": "String", + "ofType": null + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "args", + "description": null, + "args": [], + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "LIST", + "name": null, + "ofType": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "OBJECT", + "name": "__InputValue", + "ofType": null + } + } + } + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "type", + "description": null, + "args": [], + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "OBJECT", + "name": "__Type", + "ofType": null + } + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "isDeprecated", + "description": null, + "args": [], + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "Boolean", + "ofType": null + } + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "deprecationReason", + "description": null, + "args": [], + "type": { + "kind": "SCALAR", + "name": "String", + "ofType": null + }, + "isDeprecated": false, + "deprecationReason": null + } + ], + "inputFields": null, + "interfaces": [], + "enumValues": null, + "possibleTypes": null + }, + { + "kind": "OBJECT", + "name": "__InputValue", + "description": "Arguments provided to Fields or Directives and the input fields of an InputObject are represented as Input Values which describe their type and optionally a default value.", + "fields": [ + { + "name": "name", + "description": null, + "args": [], + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "String", + "ofType": null + } + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "description", + "description": null, + "args": [], + "type": { + "kind": "SCALAR", + "name": "String", + "ofType": null + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "type", + "description": null, + "args": [], + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "OBJECT", + "name": "__Type", + "ofType": null + } + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "defaultValue", + "description": "A GraphQL-formatted string representing the default value for this input value.", + "args": [], + "type": { + "kind": "SCALAR", + "name": "String", + "ofType": null + }, + "isDeprecated": false, + "deprecationReason": null + } + ], + "inputFields": null, + "interfaces": [], + "enumValues": null, + "possibleTypes": null + }, + { + "kind": "OBJECT", + "name": "__EnumValue", + "description": "One possible value for a given Enum. Enum values are unique values, not a placeholder for a string or numeric value. However an Enum value is returned in a JSON response as a string.", + "fields": [ + { + "name": "name", + "description": null, + "args": [], + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "String", + "ofType": null + } + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "description", + "description": null, + "args": [], + "type": { + "kind": "SCALAR", + "name": "String", + "ofType": null + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "isDeprecated", + "description": null, + "args": [], + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "Boolean", + "ofType": null + } + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "deprecationReason", + "description": null, + "args": [], + "type": { + "kind": "SCALAR", + "name": "String", + "ofType": null + }, + "isDeprecated": false, + "deprecationReason": null + } + ], + "inputFields": null, + "interfaces": [], + "enumValues": null, + "possibleTypes": null + }, + { + "kind": "OBJECT", + "name": "__Directive", + "description": "A Directive provides a way to describe alternate runtime execution and type validation behavior in a GraphQL document.\n\nIn some cases, you need to provide options to alter GraphQL's execution behavior in ways field arguments will not suffice, such as conditionally including or skipping a field. Directives provide this by describing additional information to the executor.", + "fields": [ + { + "name": "name", + "description": null, + "args": [], + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "String", + "ofType": null + } + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "description", + "description": null, + "args": [], + "type": { + "kind": "SCALAR", + "name": "String", + "ofType": null + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "locations", + "description": null, + "args": [], + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "LIST", + "name": null, + "ofType": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "ENUM", + "name": "__DirectiveLocation", + "ofType": null + } + } + } + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "args", + "description": null, + "args": [], + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "LIST", + "name": null, + "ofType": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "OBJECT", + "name": "__InputValue", + "ofType": null + } + } + } + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "onOperation", + "description": null, + "args": [], + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "Boolean", + "ofType": null + } + }, + "isDeprecated": true, + "deprecationReason": "Use `locations`." + }, + { + "name": "onFragment", + "description": null, + "args": [], + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "Boolean", + "ofType": null + } + }, + "isDeprecated": true, + "deprecationReason": "Use `locations`." + }, + { + "name": "onField", + "description": null, + "args": [], + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "Boolean", + "ofType": null + } + }, + "isDeprecated": true, + "deprecationReason": "Use `locations`." + } + ], + "inputFields": null, + "interfaces": [], + "enumValues": null, + "possibleTypes": null + }, + { + "kind": "ENUM", + "name": "__DirectiveLocation", + "description": "A Directive can be adjacent to many parts of the GraphQL language, a __DirectiveLocation describes one such possible adjacencies.", + "fields": null, + "inputFields": null, + "interfaces": null, + "enumValues": [ + { + "name": "QUERY", + "description": "Location adjacent to a query operation.", + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "MUTATION", + "description": "Location adjacent to a mutation operation.", + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "SUBSCRIPTION", + "description": "Location adjacent to a subscription operation.", + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "FIELD", + "description": "Location adjacent to a field.", + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "FRAGMENT_DEFINITION", + "description": "Location adjacent to a fragment definition.", + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "FRAGMENT_SPREAD", + "description": "Location adjacent to a fragment spread.", + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "INLINE_FRAGMENT", + "description": "Location adjacent to an inline fragment.", + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "SCHEMA", + "description": "Location adjacent to a schema definition.", + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "SCALAR", + "description": "Location adjacent to a scalar definition.", + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "OBJECT", + "description": "Location adjacent to an object type definition.", + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "FIELD_DEFINITION", + "description": "Location adjacent to a field definition.", + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "ARGUMENT_DEFINITION", + "description": "Location adjacent to an argument definition.", + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "INTERFACE", + "description": "Location adjacent to an interface definition.", + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "UNION", + "description": "Location adjacent to a union definition.", + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "ENUM", + "description": "Location adjacent to an enum definition.", + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "ENUM_VALUE", + "description": "Location adjacent to an enum value definition.", + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "INPUT_OBJECT", + "description": "Location adjacent to an input object type definition.", + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "INPUT_FIELD_DEFINITION", + "description": "Location adjacent to an input object field definition.", + "isDeprecated": false, + "deprecationReason": null + } + ], + "possibleTypes": null + }, + { + "kind": "OBJECT", + "name": "DefaultUserProfile", + "description": "DefaultUserProfile is a fallback if the type of UserProfile can't be\ndetermined (like if it was from a plugin that was removed).", + "fields": [ + { + "name": "id", + "description": "The id is an identifier for the user profile (email, facebook id, etc)", + "args": [], + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "String", + "ofType": null + } + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "provider", + "description": "name of the provider attached to the authentication mode", + "args": [], + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "String", + "ofType": null + } + }, + "isDeprecated": false, + "deprecationReason": null + } + ], + "inputFields": null, + "interfaces": [ + { + "kind": "INTERFACE", + "name": "UserProfile", + "ofType": null + } + ], + "enumValues": null, + "possibleTypes": null + }, + { + "kind": "OBJECT", + "name": "LocalUserProfile", + "description": "LocalUserProfile is for a User who has an authentication profile linked to\ntheir email address.", + "fields": [ + { + "name": "id", + "description": "id is the User's email address.", + "args": [], + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "String", + "ofType": null + } + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "provider", + "description": "name of the provider attached to the authentication mode, in this case,\n'local'.", + "args": [], + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "String", + "ofType": null + } + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "confirmedAt", + "description": "confirmedAt is the Date that the user had their email address confirmed,\nwhich is null if it has not been verified.", + "args": [], + "type": { + "kind": "SCALAR", + "name": "Date", + "ofType": null + }, + "isDeprecated": false, + "deprecationReason": null + } + ], + "inputFields": null, + "interfaces": [ + { + "kind": "INTERFACE", + "name": "UserProfile", + "ofType": null + } + ], + "enumValues": null, + "possibleTypes": null + }, + { + "kind": "OBJECT", + "name": "DefaultAction", + "description": "DefaultAction is the Action provided for undefined types.", + "fields": [ + { + "name": "id", + "description": "The ID of the action.", + "args": [], + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "ID", + "ofType": null + } + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "user", + "description": "The author of the action.", + "args": [], + "type": { + "kind": "OBJECT", + "name": "User", + "ofType": null + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "updated_at", + "description": "The time when the Action was updated.", + "args": [], + "type": { + "kind": "SCALAR", + "name": "Date", + "ofType": null + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "created_at", + "description": "The time when the Action was created.", + "args": [], + "type": { + "kind": "SCALAR", + "name": "Date", + "ofType": null + }, + "isDeprecated": false, + "deprecationReason": null + } + ], + "inputFields": null, + "interfaces": [ + { + "kind": "INTERFACE", + "name": "Action", + "ofType": null + } + ], + "enumValues": null, + "possibleTypes": null + }, + { + "kind": "OBJECT", + "name": "DefaultActionSummary", + "description": "DefaultActionSummary is the ActionSummary provided for undefined types.", + "fields": [ + { + "name": "count", + "description": "The count of actions with this group.", + "args": [], + "type": { + "kind": "SCALAR", + "name": "Int", + "ofType": null + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "current_user", + "description": "The current user's action.", + "args": [], + "type": { + "kind": "INTERFACE", + "name": "Action", + "ofType": null + }, + "isDeprecated": false, + "deprecationReason": null + } + ], + "inputFields": null, + "interfaces": [ + { + "kind": "INTERFACE", + "name": "ActionSummary", + "ofType": null + } + ], + "enumValues": null, + "possibleTypes": null + }, + { + "kind": "OBJECT", + "name": "DefaultAssetActionSummary", + "description": "DefaultAssetActionSummary is the AssetActionSummary provided for undefined types.", + "fields": [ + { + "name": "actionCount", + "description": "Number of actions associated with actionable types on this this Asset.", + "args": [], + "type": { + "kind": "SCALAR", + "name": "Int", + "ofType": null + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "actionableItemCount", + "description": "Number of unique actionable types that are referenced by the actions.", + "args": [], + "type": { + "kind": "SCALAR", + "name": "Int", + "ofType": null + }, + "isDeprecated": false, + "deprecationReason": null + } + ], + "inputFields": null, + "interfaces": [ + { + "kind": "INTERFACE", + "name": "AssetActionSummary", + "ofType": null + } + ], + "enumValues": null, + "possibleTypes": null + }, + { + "kind": "OBJECT", + "name": "FlagAssetActionSummary", + "description": "A summary of counts related to all the Flags on an Asset.", + "fields": [ + { + "name": "actionCount", + "description": "Number of flags associated with actionable types on this this Asset.", + "args": [], + "type": { + "kind": "SCALAR", + "name": "Int", + "ofType": null + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "actionableItemCount", + "description": "Number of unique actionable types that are referenced by the flags.", + "args": [], + "type": { + "kind": "SCALAR", + "name": "Int", + "ofType": null + }, + "isDeprecated": false, + "deprecationReason": null + } + ], + "inputFields": null, + "interfaces": [ + { + "kind": "INTERFACE", + "name": "AssetActionSummary", + "ofType": null + } + ], + "enumValues": null, + "possibleTypes": null + }, + { + "kind": "OBJECT", + "name": "FlagActionSummary", + "description": "Summary for Flag Action with a a unique reason.", + "fields": [ + { + "name": "count", + "description": "The total count of flags with this reason.", + "args": [], + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "Int", + "ofType": null + } + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "reason", + "description": "The reason for which the Flag Action was created.", + "args": [], + "type": { + "kind": "SCALAR", + "name": "String", + "ofType": null + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "current_user", + "description": "The flag by the current user against the parent entity with this reason.", + "args": [], + "type": { + "kind": "OBJECT", + "name": "FlagAction", + "ofType": null + }, + "isDeprecated": false, + "deprecationReason": null + } + ], + "inputFields": null, + "interfaces": [ + { + "kind": "INTERFACE", + "name": "ActionSummary", + "ofType": null + } + ], + "enumValues": null, + "possibleTypes": null + }, + { + "kind": "OBJECT", + "name": "DontAgreeActionSummary", + "description": "Summary for Don't Agree Action with a a unique reason.", + "fields": [ + { + "name": "count", + "description": "The total count of flags with this reason.", + "args": [], + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "Int", + "ofType": null + } + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "current_user", + "description": "The don't agree action by the current user against the parent entity with this reason.", + "args": [], + "type": { + "kind": "OBJECT", + "name": "DontAgreeAction", + "ofType": null + }, + "isDeprecated": false, + "deprecationReason": null + } + ], + "inputFields": null, + "interfaces": [ + { + "kind": "INTERFACE", + "name": "ActionSummary", + "ofType": null + } + ], + "enumValues": null, + "possibleTypes": null + }, + { + "kind": "OBJECT", + "name": "GenericUserError", + "description": "A generic error not related to validation reasons.", + "fields": [ + { + "name": "translation_key", + "description": "Translation key relating to a translatable string containing details to be\ndisplayed to the end user.", + "args": [], + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "String", + "ofType": null + } + }, + "isDeprecated": false, + "deprecationReason": null + } + ], + "inputFields": null, + "interfaces": [ + { + "kind": "INTERFACE", + "name": "UserError", + "ofType": null + } + ], + "enumValues": null, + "possibleTypes": null + }, + { + "kind": "OBJECT", + "name": "ValidationUserError", + "description": "A validation error that affects the input.", + "fields": [ + { + "name": "translation_key", + "description": "Translation key relating to a translatable string containing details to be\ndisplayed to the end user.", + "args": [], + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "String", + "ofType": null + } + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "field_name", + "description": "The field in question that caused the error.", + "args": [], + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "String", + "ofType": null + } + }, + "isDeprecated": false, + "deprecationReason": null + } + ], + "inputFields": null, + "interfaces": [ + { + "kind": "INTERFACE", + "name": "UserError", + "ofType": null + } + ], + "enumValues": null, + "possibleTypes": null + }, + { + "kind": "ENUM", + "name": "ASSET_METRICS_SORT", + "description": "", + "fields": null, + "inputFields": null, + "interfaces": null, + "enumValues": [ + { + "name": "RESPECT", + "description": "Represents a RespectAction.", + "isDeprecated": false, + "deprecationReason": null + } + ], + "possibleTypes": null + }, + { + "kind": "OBJECT", + "name": "RespectActionSummary", + "description": "", + "fields": [ + { + "name": "count", + "description": "The count of actions with this group.", + "args": [], + "type": { + "kind": "SCALAR", + "name": "Int", + "ofType": null + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "current_user", + "description": "The current user's action.", + "args": [], + "type": { + "kind": "OBJECT", + "name": "RespectAction", + "ofType": null + }, + "isDeprecated": false, + "deprecationReason": null + } + ], + "inputFields": null, + "interfaces": [ + { + "kind": "INTERFACE", + "name": "ActionSummary", + "ofType": null + } + ], + "enumValues": null, + "possibleTypes": null + }, + { + "kind": "OBJECT", + "name": "RespectAssetActionSummary", + "description": "A summary of counts related to all the Respects on an Asset.", + "fields": [ + { + "name": "actionCount", + "description": "Number of respects associated with actionable types on this this Asset.", + "args": [], + "type": { + "kind": "SCALAR", + "name": "Int", + "ofType": null + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "actionableItemCount", + "description": "Number of unique actionable types that are referenced by the respects.", + "args": [], + "type": { + "kind": "SCALAR", + "name": "Int", + "ofType": null + }, + "isDeprecated": false, + "deprecationReason": null + } + ], + "inputFields": null, + "interfaces": [ + { + "kind": "INTERFACE", + "name": "AssetActionSummary", + "ofType": null + } + ], + "enumValues": null, + "possibleTypes": null + } + ], + "directives": [ + { + "name": "skip", + "description": "Directs the executor to skip this field or fragment when the `if` argument is true.", + "locations": [ + "FIELD", + "FRAGMENT_SPREAD", + "INLINE_FRAGMENT" + ], + "args": [ + { + "name": "if", + "description": "Skipped when true.", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "Boolean", + "ofType": null + } + }, + "defaultValue": null + } + ] + }, + { + "name": "include", + "description": "Directs the executor to include this field or fragment only when the `if` argument is true.", + "locations": [ + "FIELD", + "FRAGMENT_SPREAD", + "INLINE_FRAGMENT" + ], + "args": [ + { + "name": "if", + "description": "Included when true.", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "Boolean", + "ofType": null + } + }, + "defaultValue": null + } + ] + }, + { + "name": "deprecated", + "description": "Marks an element of a GraphQL schema as no longer supported.", + "locations": [ + "FIELD_DEFINITION", + "ENUM_VALUE" + ], + "args": [ + { + "name": "reason", + "description": "Explains why this element was deprecated, usually also including a suggestion for how to access supported similar data. Formatted in [Markdown](https://daringfireball.net/projects/markdown/).", + "type": { + "kind": "SCALAR", + "name": "String", + "ofType": null + }, + "defaultValue": "\"No longer supported\"" + } + ] + } + ] + } +} \ No newline at end of file diff --git a/plugins/talk-plugin-auth/client/login/constants.js b/plugins/talk-plugin-auth/client/login/constants.js index 3997c11f4..bfac90839 100644 --- a/plugins/talk-plugin-auth/client/login/constants.js +++ b/plugins/talk-plugin-auth/client/login/constants.js @@ -3,3 +3,5 @@ const prefix = 'TALK_AUTH'; export const SET_VIEW = `${prefix}_SET_VIEW`; export const SET_EMAIL = `${prefix}_SET_EMAIL`; export const SET_PASSWORD = `${prefix}_SET_PASSWORD`; +export const ENABLE_SUBMIT = `${prefix}_ENABLE_SUBMIT`; +export const DISABLE_SUBMIT = `${prefix}_DISABLE_SUBMIT`; diff --git a/plugins/talk-plugin-auth/client/login/reducer.js b/plugins/talk-plugin-auth/client/login/reducer.js index 004aff8f8..a8dad11eb 100644 --- a/plugins/talk-plugin-auth/client/login/reducer.js +++ b/plugins/talk-plugin-auth/client/login/reducer.js @@ -5,10 +5,21 @@ const initialState = { view: views.SIGN_IN, email: '', password: '', + enableSubmit: true, }; export default function reducer(state = initialState, action) { switch (action.type) { + case actions.ENABLE_SUBMIT: + return { + ...state, + enableSubmit: true, + }; + case actions.DISABLE_SUBMIT: + return { + ...state, + enableSubmit: false, + }; case actions.SET_VIEW: return { ...state, diff --git a/plugins/talk-plugin-auth/client/stream/components/SetUsernameDialog.css b/plugins/talk-plugin-auth/client/stream/components/SetUsernameDialog.css index 856e95642..c1947ca7d 100644 --- a/plugins/talk-plugin-auth/client/stream/components/SetUsernameDialog.css +++ b/plugins/talk-plugin-auth/client/stream/components/SetUsernameDialog.css @@ -1,3 +1,10 @@ +.dialogusername { + border: none; + box-shadow: 0 9px 46px 8px rgba(0, 0, 0, 0.14), 0 11px 15px -7px rgba(0, 0, 0, 0.12), 0 24px 38px 3px rgba(0, 0, 0, 0.2); + width: 400px; + top: 10px; +} + .yourusername { display: block; } diff --git a/plugins/talk-plugin-auth/client/stream/components/SetUsernameDialog.js b/plugins/talk-plugin-auth/client/stream/components/SetUsernameDialog.js index 9cacc70b8..0fd742879 100644 --- a/plugins/talk-plugin-auth/client/stream/components/SetUsernameDialog.js +++ b/plugins/talk-plugin-auth/client/stream/components/SetUsernameDialog.js @@ -1,7 +1,12 @@ import React from 'react'; import PropTypes from 'prop-types'; import styles from './SetUsernameDialog.css'; -import { Alert, TextField, Button } from 'plugin-api/beta/client/components/ui'; +import { + Dialog, + Alert, + TextField, + Button, +} from 'plugin-api/beta/client/components/ui'; import { FakeComment } from './FakeComment'; import { t } from 'plugin-api/beta/client/services'; @@ -17,50 +22,52 @@ class SetUsernameDialog extends React.Component { const { username, usernameError, errorMessage } = this.props; return ( -
-
-

- {t('talk-plugin-auth.set_username_dialog.write_your_username')} -

-
+
-

- {t('talk-plugin-auth.set_username_dialog.your_username')} -

- - {errorMessage && {errorMessage}} -
- {usernameError && ( - - {' '} - {t( - 'talk-plugin-auth.set_username_dialog.special_characters' - )}{' '} - - )} -
- - -
-
+
+

+ {t('talk-plugin-auth.set_username_dialog.write_your_username')} +

+
+
+

+ {t('talk-plugin-auth.set_username_dialog.your_username')} +

+ + {errorMessage && {errorMessage}} +
+ {usernameError && ( + + {' '} + {t( + 'talk-plugin-auth.set_username_dialog.special_characters' + )}{' '} + + )} +
+ + +
+
+
-
+ ); } } From ab96417d3d5089965994e9be2f3bd870eba8120f Mon Sep 17 00:00:00 2001 From: okbel Date: Fri, 23 Mar 2018 17:27:23 -0300 Subject: [PATCH 03/84] wip --- .../talk-plugin-auth/client/login/actions.js | 8 ++++++++ .../client/login/components/SignUp.js | 14 ++++++++++++++ .../talk-plugin-auth/client/login/constants.js | 4 ++-- .../client/login/containers/SignUp.js | 17 ++++++++++++++++- .../talk-plugin-auth/client/login/reducer.js | 10 +++++----- 5 files changed, 45 insertions(+), 8 deletions(-) diff --git a/plugins/talk-plugin-auth/client/login/actions.js b/plugins/talk-plugin-auth/client/login/actions.js index 593556507..9cfbfb399 100644 --- a/plugins/talk-plugin-auth/client/login/actions.js +++ b/plugins/talk-plugin-auth/client/login/actions.js @@ -14,3 +14,11 @@ export const setPassword = password => ({ type: actions.SET_PASSWORD, password, }); + +export const enableSubmitSignUpForm = () => ({ + type: actions.ENABLE_SUBMIT_SIGNUP_FORM, +}); + +export const disableSubmitSignUpForm = () => ({ + type: actions.DISABLE_SUBMIT_SIGNUP_FORM, +}); diff --git a/plugins/talk-plugin-auth/client/login/components/SignUp.js b/plugins/talk-plugin-auth/client/login/components/SignUp.js index 61c4d0b1d..930d41819 100644 --- a/plugins/talk-plugin-auth/client/login/components/SignUp.js +++ b/plugins/talk-plugin-auth/client/login/components/SignUp.js @@ -1,5 +1,6 @@ import React from 'react'; import PropTypes from 'prop-types'; +import { Slot } from 'plugin-api/beta/client/components'; import { Button, TextField, @@ -42,8 +43,15 @@ class SignUp extends React.Component { errorMessage, requireEmailConfirmation, success, + enableSubmitSignUpForm, + disableSubmitSignUpForm, } = this.props; + const slotPassthrough = { + enableSubmitSignUpForm, + disableSubmitSignUpForm, + }; + return (
@@ -103,6 +111,10 @@ class SignUp extends React.Component { onChange={this.handlePasswordRepeatChange} minLength="8" /> +
@@ -175,6 +176,7 @@ SignUp.propTypes = { success: PropTypes.bool.isRequired, enableSubmitSignUpForm: PropTypes.func.isRequired, disableSubmitSignUpForm: PropTypes.func.isRequired, + submitSignUpForm: PropTypes.bool.isRequired, }; export default SignUp; diff --git a/plugins/talk-plugin-health-report/client/.eslintrc.json b/plugins/talk-plugin-health-report/client/.eslintrc.json new file mode 100644 index 000000000..9fe56bd14 --- /dev/null +++ b/plugins/talk-plugin-health-report/client/.eslintrc.json @@ -0,0 +1,23 @@ +{ + "env": { + "browser": true, + "es6": true, + "mocha": true + }, + "parserOptions": { + "sourceType": "module", + "ecmaFeatures": { + "experimentalObjectRestSpread": true, + "jsx": true + } + }, + "parser": "babel-eslint", + "plugins": [ + "react" + ], + "rules": { + "react/jsx-uses-react": "error", + "react/jsx-uses-vars": "error", + "no-console": ["warn", { "allow": ["warn", "error"] }] + } +} diff --git a/plugins/talk-plugin-health-report/client/components/TermsAndConditionsField.js b/plugins/talk-plugin-health-report/client/components/TermsAndConditionsField.js new file mode 100644 index 000000000..79d079d7f --- /dev/null +++ b/plugins/talk-plugin-health-report/client/components/TermsAndConditionsField.js @@ -0,0 +1,45 @@ +import React from 'react'; +import { Checkbox } from 'plugin-api/beta/client/components/ui'; +import { t } from 'plugin-api/beta/client/services'; + +const TermsLink = () => ( + + {t('talk-plugin-health-report.terms')} + +); + +class TermsAndConditionsField extends React.Component { + state = { checked: false }; + id = 'terms-and-conditions'; + + componentWillMount() { + this.props.disableSubmitSignUpForm(); + } + + onChange = ({ target: { checked } }) => { + if (checked) { + this.setState(() => ({ checked })); + this.props.enableSubmitSignUpForm(); + } else { + this.setState(() => ({ checked })); + this.props.disableSubmitSignUpForm(); + } + }; + + render() { + return ( +
+ + +
+ ); + } +} + +export default TermsAndConditionsField; diff --git a/plugins/talk-plugin-health-report/client/index.js b/plugins/talk-plugin-health-report/client/index.js new file mode 100644 index 000000000..d2aaaa1cb --- /dev/null +++ b/plugins/talk-plugin-health-report/client/index.js @@ -0,0 +1,9 @@ +import TermsAndConditionsField from './components/TermsAndConditionsField'; +import translations from './translations.yml'; + +export default { + slots: { + 'talkPluginAuth-formField': [TermsAndConditionsField], + }, + translations, +}; diff --git a/plugins/talk-plugin-health-report/client/translations.yml b/plugins/talk-plugin-health-report/client/translations.yml new file mode 100644 index 000000000..cfff60419 --- /dev/null +++ b/plugins/talk-plugin-health-report/client/translations.yml @@ -0,0 +1,21 @@ +en: + talk-plugin-health-report: + copy: I agree to the Mozilla {0} and {1}. + terms: Terms of Service + termslink: https://www.mozilla.org/en-US/about/legal/terms/mozilla/ + privacy: https://www.mozilla.org/en-US/privacy/websites/ +es: + talk-plugin-health-report: + copy: Estoy de acuerdo con los {0} de Mozilla y {1}. + terms: https://www.mozilla.org/en-US/about/legal/terms/mozilla/ + privacy: https://www.mozilla.org/en-US/privacy/websites/ +fr: + talk-plugin-health-report: + copy: J'accepte les conditions d'utilisation et la politique de confidentialité de Mozilla. + terms: https://www.mozilla.org/en-US/about/legal/terms/mozilla/ + privacy: https://www.mozilla.org/en-US/privacy/websites/ +de: + talk-plugin-health-report: + copy: J'accepte les conditions d'utilisation et la politique de confidentialité de Mozilla. + terms: https://www.mozilla.org/en-US/about/legal/terms/mozilla/ + privacy: https://www.mozilla.org/en-US/privacy/websites/ \ No newline at end of file diff --git a/plugins/talk-plugin-health-report/index.js b/plugins/talk-plugin-health-report/index.js new file mode 100644 index 000000000..f053ebf79 --- /dev/null +++ b/plugins/talk-plugin-health-report/index.js @@ -0,0 +1 @@ +module.exports = {}; From f8fbedd72776cc375dcc7f4bac52dc39438db063 Mon Sep 17 00:00:00 2001 From: okbel Date: Mon, 26 Mar 2018 12:06:38 -0300 Subject: [PATCH 06/84] Styling and translations --- client/coral-framework/services/i18n.js | 1 + .../components/TermsAndConditionsField.css | 13 +++++++ .../components/TermsAndConditionsField.js | 34 ++++++++++++++-- .../client/translations.yml | 39 +++++++++++++------ 4 files changed, 71 insertions(+), 16 deletions(-) create mode 100644 plugins/talk-plugin-health-report/client/components/TermsAndConditionsField.css diff --git a/client/coral-framework/services/i18n.js b/client/coral-framework/services/i18n.js index 1468f92eb..9c65c9c4e 100644 --- a/client/coral-framework/services/i18n.js +++ b/client/coral-framework/services/i18n.js @@ -136,6 +136,7 @@ export function t(key, ...replacements) { replacements.forEach((str, i) => { translation = translation.replace(new RegExp(`\\{${i}\\}`, 'g'), str); }); + return translation; } else { console.warn(`${lang}.${key} and en.${key} language key not set`); diff --git a/plugins/talk-plugin-health-report/client/components/TermsAndConditionsField.css b/plugins/talk-plugin-health-report/client/components/TermsAndConditionsField.css new file mode 100644 index 000000000..e05170b46 --- /dev/null +++ b/plugins/talk-plugin-health-report/client/components/TermsAndConditionsField.css @@ -0,0 +1,13 @@ +.link { + color: #2c69b6; + cursor: pointer; + margin: 0 3px; + + &:last-child { + margin-right: 0; + } +} + +.fieldContainer { + padding: 5px 0; +} \ No newline at end of file diff --git a/plugins/talk-plugin-health-report/client/components/TermsAndConditionsField.js b/plugins/talk-plugin-health-report/client/components/TermsAndConditionsField.js index 79d079d7f..3fc2f6304 100644 --- a/plugins/talk-plugin-health-report/client/components/TermsAndConditionsField.js +++ b/plugins/talk-plugin-health-report/client/components/TermsAndConditionsField.js @@ -1,13 +1,29 @@ import React from 'react'; import { Checkbox } from 'plugin-api/beta/client/components/ui'; import { t } from 'plugin-api/beta/client/services'; +import styles from './TermsAndConditionsField.css'; +import cn from 'classnames'; const TermsLink = () => ( - + {t('talk-plugin-health-report.terms')} ); +const PrivacyLink = () => ( + + {t('talk-plugin-health-report.privacy-policy')} + +); + class TermsAndConditionsField extends React.Component { state = { checked: false }; id = 'terms-and-conditions'; @@ -28,14 +44,24 @@ class TermsAndConditionsField extends React.Component { render() { return ( -
+
-
); diff --git a/plugins/talk-plugin-health-report/client/translations.yml b/plugins/talk-plugin-health-report/client/translations.yml index cfff60419..66bf7379c 100644 --- a/plugins/talk-plugin-health-report/client/translations.yml +++ b/plugins/talk-plugin-health-report/client/translations.yml @@ -1,21 +1,36 @@ en: talk-plugin-health-report: - copy: I agree to the Mozilla {0} and {1}. + copy: I agree to the Mozilla + and: and terms: Terms of Service - termslink: https://www.mozilla.org/en-US/about/legal/terms/mozilla/ - privacy: https://www.mozilla.org/en-US/privacy/websites/ + privacy-policy: Privacy Policy + from: . + terms-link: https://www.mozilla.org/en-US/about/legal/terms/mozilla/ + privacy-policy-link: https://www.mozilla.org/en-US/privacy/websites/ es: talk-plugin-health-report: - copy: Estoy de acuerdo con los {0} de Mozilla y {1}. - terms: https://www.mozilla.org/en-US/about/legal/terms/mozilla/ - privacy: https://www.mozilla.org/en-US/privacy/websites/ + copy: Estoy de acuerdo con + and: y + terms: los Terminos y Condiciones + privacy-policy: la Política de Privacidad + from: de Mozilla. + terms-link: https://www.mozilla.org/es-ES/about/legal/terms/mozilla/ + privacy-policy-link: https://www.mozilla.org/es-ES/privacy/websites/ fr: talk-plugin-health-report: - copy: J'accepte les conditions d'utilisation et la politique de confidentialité de Mozilla. - terms: https://www.mozilla.org/en-US/about/legal/terms/mozilla/ - privacy: https://www.mozilla.org/en-US/privacy/websites/ + copy: J'accepte + and: et + terms: les conditions d'utilisation + privacy-policy: la politique de confidentialité + from: de Mozilla. + terms-link: https://www.mozilla.org/fr/about/legal/terms/mozilla/ + privacy-policy-link: https://www.mozilla.org/fr/privacy/websites/ de: talk-plugin-health-report: - copy: J'accepte les conditions d'utilisation et la politique de confidentialité de Mozilla. - terms: https://www.mozilla.org/en-US/about/legal/terms/mozilla/ - privacy: https://www.mozilla.org/en-US/privacy/websites/ \ No newline at end of file + copy: Ich stimme + and: und + terms: den Allgemeinen Geschäftsbedingungen + privacy-policy: der Datenschutzrichtlinie + from: von Mozilla zu. + terms-link: https://www.mozilla.org/de/about/legal/terms/mozilla/ + privacy-policy-link: https://www.mozilla.org/de/privacy/websites/ \ No newline at end of file From cf0551d3e7ab92b4ba687423fed2eeae40fd7b7f Mon Sep 17 00:00:00 2001 From: Wyatt Johnson Date: Tue, 27 Mar 2018 14:10:53 -0600 Subject: [PATCH 07/84] Docs cleanup - Removed references to Facebook being a default plugin --- docs/source/01-01-talk-quickstart.md | 6 +----- docs/source/01-03-installation-from-source.md | 5 +---- docs/source/03-02-product-guide-commenter-features.md | 4 ++-- 3 files changed, 4 insertions(+), 11 deletions(-) diff --git a/docs/source/01-01-talk-quickstart.md b/docs/source/01-01-talk-quickstart.md index c18aea638..7e2adb2fe 100644 --- a/docs/source/01-01-talk-quickstart.md +++ b/docs/source/01-01-talk-quickstart.md @@ -167,14 +167,10 @@ TALK_REDIS_URL=redis://127.0.0.1:6379 TALK_ROOT_URL=http://127.0.0.1:3000 TALK_PORT=3000 TALK_JWT_SECRET=password -TALK_FACEBOOK_APP_ID=A-Facebook-App-ID -TALK_FACEBOOK_APP_SECRET=A-Facebook-App-Secret ``` This is only the bare minimum needed to run the demo, for more configuration -variables, check out the [Configuration](/talk/configuration/) section. Facebook login above -will definitely not work unless you change those values as well. - +variables, check out the [Configuration](/talk/configuration/) section. You can now start the application by running: diff --git a/docs/source/01-03-installation-from-source.md b/docs/source/01-03-installation-from-source.md index a460751f7..9f4b96d93 100644 --- a/docs/source/01-03-installation-from-source.md +++ b/docs/source/01-03-installation-from-source.md @@ -57,14 +57,11 @@ TALK_REDIS_URL=redis://127.0.0.1:6379 TALK_ROOT_URL=http://127.0.0.1:3000 TALK_PORT=3000 TALK_JWT_SECRET=password -TALK_FACEBOOK_APP_ID=A-Facebook-App-ID -TALK_FACEBOOK_APP_SECRET=A-Facebook-App-Secret ``` This is the bare minimum needed to start Talk, for more configuration variables, check out the [Configuration](/talk/configuration/) -section. Facebook login above will definitely not work unless you change those -values as well. +section. You can now start the application by running: diff --git a/docs/source/03-02-product-guide-commenter-features.md b/docs/source/03-02-product-guide-commenter-features.md index 390fdce50..3d60eeda9 100644 --- a/docs/source/03-02-product-guide-commenter-features.md +++ b/docs/source/03-02-product-guide-commenter-features.md @@ -24,7 +24,7 @@ All levels of comments and replies are able to be linked to via permalink. Perma ```text https://?commentId= ``` -{:.no-copy} + ### Threading @@ -44,7 +44,7 @@ talk-stream-comment-level-${depth} talk-stream-highlighted-comment talk-stream-pending-comment ``` -{:.no-copy} + ### Automatic Updates From bad72e0ef49932ec253ba66a02f9e391f1394cd6 Mon Sep 17 00:00:00 2001 From: okbel Date: Wed, 28 Mar 2018 09:27:50 -0300 Subject: [PATCH 08/84] fix install buttons - making the click area bigger --- .../routes/Install/components/Steps/FinalStep.js | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/client/coral-admin/src/routes/Install/components/Steps/FinalStep.js b/client/coral-admin/src/routes/Install/components/Steps/FinalStep.js index 72fd67a34..b0a0c43ef 100644 --- a/client/coral-admin/src/routes/Install/components/Steps/FinalStep.js +++ b/client/coral-admin/src/routes/Install/components/Steps/FinalStep.js @@ -10,12 +10,14 @@ const InitialStep = () => { return (

{t('install.final.description')}

- - + + + + + +
); }; From 217c2f1bfae6a4ca2e4f807da533253654e3e31a Mon Sep 17 00:00:00 2001 From: Kim Gardner Date: Wed, 28 Mar 2018 08:55:55 -0400 Subject: [PATCH 09/84] Add back missing tutorial --- docs/_config.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/_config.yml b/docs/_config.yml index deb14ced4..c61fdeb21 100644 --- a/docs/_config.yml +++ b/docs/_config.yml @@ -154,6 +154,8 @@ sidebar: url: /building-basic-plugin/ - title: Customizing Plugins with Coral UI url: /customizing-plugins-coral-ui/ + - title: When You've Installed Talk + url: /when-youve-installed-talk/ - title: Migrating children: - title: Migrating to v4.0.0 From 0b3b252fc3975cbd05bdfaf80b35159bea1c29da Mon Sep 17 00:00:00 2001 From: okbel Date: Thu, 29 Mar 2018 10:56:18 -0300 Subject: [PATCH 10/84] changes --- .gitignore | 2 +- plugins/talk-plugin-auth-checkbox | 1 + .../talk-plugin-auth/client/login/actions.js | 8 --- .../client/login/constants.js | 2 - .../client/login/containers/SignUp.js | 13 +++- .../talk-plugin-auth/client/login/reducer.js | 11 --- .../client/.eslintrc.json | 23 ------ .../components/TermsAndConditionsField.css | 13 ---- .../components/TermsAndConditionsField.js | 71 ------------------- .../talk-plugin-health-report/client/index.js | 9 --- .../client/translations.yml | 36 ---------- plugins/talk-plugin-health-report/index.js | 1 - 12 files changed, 14 insertions(+), 176 deletions(-) create mode 160000 plugins/talk-plugin-auth-checkbox delete mode 100644 plugins/talk-plugin-health-report/client/.eslintrc.json delete mode 100644 plugins/talk-plugin-health-report/client/components/TermsAndConditionsField.css delete mode 100644 plugins/talk-plugin-health-report/client/components/TermsAndConditionsField.js delete mode 100644 plugins/talk-plugin-health-report/client/index.js delete mode 100644 plugins/talk-plugin-health-report/client/translations.yml delete mode 100644 plugins/talk-plugin-health-report/index.js diff --git a/.gitignore b/.gitignore index fbeb12d8c..5bf0d6527 100644 --- a/.gitignore +++ b/.gitignore @@ -63,7 +63,7 @@ plugins/* !plugins/talk-plugin-viewing-options !plugins/talk-plugin-rich-text !plugins/talk-plugin-rich-text-pell -!plugins/talk-plugin-health-report +!plugins/talk-plugin-auth-checkbox **/node_modules/* yarn-error.log diff --git a/plugins/talk-plugin-auth-checkbox b/plugins/talk-plugin-auth-checkbox new file mode 160000 index 000000000..73cf96e85 --- /dev/null +++ b/plugins/talk-plugin-auth-checkbox @@ -0,0 +1 @@ +Subproject commit 73cf96e8565c6651a10f75f99704db9479bb7d13 diff --git a/plugins/talk-plugin-auth/client/login/actions.js b/plugins/talk-plugin-auth/client/login/actions.js index 9cfbfb399..593556507 100644 --- a/plugins/talk-plugin-auth/client/login/actions.js +++ b/plugins/talk-plugin-auth/client/login/actions.js @@ -14,11 +14,3 @@ export const setPassword = password => ({ type: actions.SET_PASSWORD, password, }); - -export const enableSubmitSignUpForm = () => ({ - type: actions.ENABLE_SUBMIT_SIGNUP_FORM, -}); - -export const disableSubmitSignUpForm = () => ({ - type: actions.DISABLE_SUBMIT_SIGNUP_FORM, -}); diff --git a/plugins/talk-plugin-auth/client/login/constants.js b/plugins/talk-plugin-auth/client/login/constants.js index f44ad5a0f..3997c11f4 100644 --- a/plugins/talk-plugin-auth/client/login/constants.js +++ b/plugins/talk-plugin-auth/client/login/constants.js @@ -3,5 +3,3 @@ const prefix = 'TALK_AUTH'; export const SET_VIEW = `${prefix}_SET_VIEW`; export const SET_EMAIL = `${prefix}_SET_EMAIL`; export const SET_PASSWORD = `${prefix}_SET_PASSWORD`; -export const ENABLE_SUBMIT_SIGNUP_FORM = `${prefix}_ENABLE_SUBMIT_SIGNUP_FORM`; -export const DISABLE_SUBMIT_SIGNUP_FORM = `${prefix}_DISABLE_SUBMIT_SIGNUP_FOR`; diff --git a/plugins/talk-plugin-auth/client/login/containers/SignUp.js b/plugins/talk-plugin-auth/client/login/containers/SignUp.js index 902d35105..7e6face74 100644 --- a/plugins/talk-plugin-auth/client/login/containers/SignUp.js +++ b/plugins/talk-plugin-auth/client/login/containers/SignUp.js @@ -22,8 +22,19 @@ class SignUpContainer extends Component { emailError: null, passwordError: null, passwordRepeatError: null, + hasBlockers: [], }; + indicateBlockerOn = plugin => + this.setState(state => ({ + hasBlockers: state.hasBlockers.concat(plugin), + })); + + indicateBlockerOff = plugin => + this.setState(state => ({ + hasNotifications: state.hasNotifications.filter(i => i !== plugin), + })); + validate = data => { let valid = true; const changes = {}; @@ -54,7 +65,7 @@ class SignUpContainer extends Component { passwordRepeat: this.state.passwordRepeat, }; - if (this.validate(data)) { + if (this.validate(data) && !this.state.hasBlockers.length) { this.props.signUp(data); } }; diff --git a/plugins/talk-plugin-auth/client/login/reducer.js b/plugins/talk-plugin-auth/client/login/reducer.js index 0822d1d06..004aff8f8 100644 --- a/plugins/talk-plugin-auth/client/login/reducer.js +++ b/plugins/talk-plugin-auth/client/login/reducer.js @@ -5,21 +5,10 @@ const initialState = { view: views.SIGN_IN, email: '', password: '', - submitSignUpForm: true, }; export default function reducer(state = initialState, action) { switch (action.type) { - case actions.ENABLE_SUBMIT_SIGNUP_FORM: - return { - ...state, - submitSignUpForm: true, - }; - case actions.DISABLE_SUBMIT_SIGNUP_FORM: - return { - ...state, - submitSignUpForm: false, - }; case actions.SET_VIEW: return { ...state, diff --git a/plugins/talk-plugin-health-report/client/.eslintrc.json b/plugins/talk-plugin-health-report/client/.eslintrc.json deleted file mode 100644 index 9fe56bd14..000000000 --- a/plugins/talk-plugin-health-report/client/.eslintrc.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "env": { - "browser": true, - "es6": true, - "mocha": true - }, - "parserOptions": { - "sourceType": "module", - "ecmaFeatures": { - "experimentalObjectRestSpread": true, - "jsx": true - } - }, - "parser": "babel-eslint", - "plugins": [ - "react" - ], - "rules": { - "react/jsx-uses-react": "error", - "react/jsx-uses-vars": "error", - "no-console": ["warn", { "allow": ["warn", "error"] }] - } -} diff --git a/plugins/talk-plugin-health-report/client/components/TermsAndConditionsField.css b/plugins/talk-plugin-health-report/client/components/TermsAndConditionsField.css deleted file mode 100644 index e05170b46..000000000 --- a/plugins/talk-plugin-health-report/client/components/TermsAndConditionsField.css +++ /dev/null @@ -1,13 +0,0 @@ -.link { - color: #2c69b6; - cursor: pointer; - margin: 0 3px; - - &:last-child { - margin-right: 0; - } -} - -.fieldContainer { - padding: 5px 0; -} \ No newline at end of file diff --git a/plugins/talk-plugin-health-report/client/components/TermsAndConditionsField.js b/plugins/talk-plugin-health-report/client/components/TermsAndConditionsField.js deleted file mode 100644 index 3fc2f6304..000000000 --- a/plugins/talk-plugin-health-report/client/components/TermsAndConditionsField.js +++ /dev/null @@ -1,71 +0,0 @@ -import React from 'react'; -import { Checkbox } from 'plugin-api/beta/client/components/ui'; -import { t } from 'plugin-api/beta/client/services'; -import styles from './TermsAndConditionsField.css'; -import cn from 'classnames'; - -const TermsLink = () => ( - - {t('talk-plugin-health-report.terms')} - -); - -const PrivacyLink = () => ( - - {t('talk-plugin-health-report.privacy-policy')} - -); - -class TermsAndConditionsField extends React.Component { - state = { checked: false }; - id = 'terms-and-conditions'; - - componentWillMount() { - this.props.disableSubmitSignUpForm(); - } - - onChange = ({ target: { checked } }) => { - if (checked) { - this.setState(() => ({ checked })); - this.props.enableSubmitSignUpForm(); - } else { - this.setState(() => ({ checked })); - this.props.disableSubmitSignUpForm(); - } - }; - - render() { - return ( -
- - -
- ); - } -} - -export default TermsAndConditionsField; diff --git a/plugins/talk-plugin-health-report/client/index.js b/plugins/talk-plugin-health-report/client/index.js deleted file mode 100644 index d2aaaa1cb..000000000 --- a/plugins/talk-plugin-health-report/client/index.js +++ /dev/null @@ -1,9 +0,0 @@ -import TermsAndConditionsField from './components/TermsAndConditionsField'; -import translations from './translations.yml'; - -export default { - slots: { - 'talkPluginAuth-formField': [TermsAndConditionsField], - }, - translations, -}; diff --git a/plugins/talk-plugin-health-report/client/translations.yml b/plugins/talk-plugin-health-report/client/translations.yml deleted file mode 100644 index 66bf7379c..000000000 --- a/plugins/talk-plugin-health-report/client/translations.yml +++ /dev/null @@ -1,36 +0,0 @@ -en: - talk-plugin-health-report: - copy: I agree to the Mozilla - and: and - terms: Terms of Service - privacy-policy: Privacy Policy - from: . - terms-link: https://www.mozilla.org/en-US/about/legal/terms/mozilla/ - privacy-policy-link: https://www.mozilla.org/en-US/privacy/websites/ -es: - talk-plugin-health-report: - copy: Estoy de acuerdo con - and: y - terms: los Terminos y Condiciones - privacy-policy: la Política de Privacidad - from: de Mozilla. - terms-link: https://www.mozilla.org/es-ES/about/legal/terms/mozilla/ - privacy-policy-link: https://www.mozilla.org/es-ES/privacy/websites/ -fr: - talk-plugin-health-report: - copy: J'accepte - and: et - terms: les conditions d'utilisation - privacy-policy: la politique de confidentialité - from: de Mozilla. - terms-link: https://www.mozilla.org/fr/about/legal/terms/mozilla/ - privacy-policy-link: https://www.mozilla.org/fr/privacy/websites/ -de: - talk-plugin-health-report: - copy: Ich stimme - and: und - terms: den Allgemeinen Geschäftsbedingungen - privacy-policy: der Datenschutzrichtlinie - from: von Mozilla zu. - terms-link: https://www.mozilla.org/de/about/legal/terms/mozilla/ - privacy-policy-link: https://www.mozilla.org/de/privacy/websites/ \ No newline at end of file diff --git a/plugins/talk-plugin-health-report/index.js b/plugins/talk-plugin-health-report/index.js deleted file mode 100644 index f053ebf79..000000000 --- a/plugins/talk-plugin-health-report/index.js +++ /dev/null @@ -1 +0,0 @@ -module.exports = {}; From ff9a212cfbf2e53d6efd8784fb053645241e51bb Mon Sep 17 00:00:00 2001 From: okbel Date: Thu, 29 Mar 2018 11:10:22 -0300 Subject: [PATCH 11/84] with hasBlockers --- .../client/login/components/SignUp.js | 18 ++++++++------- .../client/login/containers/SignUp.js | 22 +++++-------------- 2 files changed, 15 insertions(+), 25 deletions(-) diff --git a/plugins/talk-plugin-auth/client/login/components/SignUp.js b/plugins/talk-plugin-auth/client/login/components/SignUp.js index a66e46dee..3dc648de6 100644 --- a/plugins/talk-plugin-auth/client/login/components/SignUp.js +++ b/plugins/talk-plugin-auth/client/login/components/SignUp.js @@ -43,13 +43,15 @@ class SignUp extends React.Component { errorMessage, requireEmailConfirmation, success, - enableSubmitSignUpForm, - disableSubmitSignUpForm, + indicateBlockerOn, + indicateBlockerOff, + hasBlockers, } = this.props; const slotPassthrough = { - enableSubmitSignUpForm, - disableSubmitSignUpForm, + indicateBlockerOn, + indicateBlockerOff, + hasBlockers, }; return ( @@ -122,7 +124,7 @@ class SignUp extends React.Component { id="coralSignUpButton" className={styles.button} full - disabled={!this.props.submitSignUpForm} + disabled={hasBlockers.length} > {t('talk-plugin-auth.login.sign_up')} @@ -174,9 +176,9 @@ SignUp.propTypes = { errorMessage: PropTypes.string, requireEmailConfirmation: PropTypes.bool.isRequired, success: PropTypes.bool.isRequired, - enableSubmitSignUpForm: PropTypes.func.isRequired, - disableSubmitSignUpForm: PropTypes.func.isRequired, - submitSignUpForm: PropTypes.bool.isRequired, + hasBlockers: PropTypes.array.isRequired, + indicateBlockerOn: PropTypes.func.isRequired, + indicateBlockerOff: PropTypes.func.isRequired, }; export default SignUp; diff --git a/plugins/talk-plugin-auth/client/login/containers/SignUp.js b/plugins/talk-plugin-auth/client/login/containers/SignUp.js index 7e6face74..2b2a7d979 100644 --- a/plugins/talk-plugin-auth/client/login/containers/SignUp.js +++ b/plugins/talk-plugin-auth/client/login/containers/SignUp.js @@ -5,13 +5,7 @@ import { compose } from 'recompose'; import SignUp from '../components/SignUp'; import { bindActionCreators } from 'redux'; import * as views from '../enums/views'; -import { - setView, - setEmail, - setPassword, - enableSubmitSignUpForm, - disableSubmitSignUpForm, -} from '../actions'; +import { setView, setEmail, setPassword } from '../actions'; import { t } from 'plugin-api/beta/client/services'; class SignUpContainer extends Component { @@ -32,7 +26,7 @@ class SignUpContainer extends Component { indicateBlockerOff = plugin => this.setState(state => ({ - hasNotifications: state.hasNotifications.filter(i => i !== plugin), + hasBlockers: state.hasBlockers.filter(i => i !== plugin), })); validate = data => { @@ -93,9 +87,9 @@ class SignUpContainer extends Component { render() { return ( ({ email: state.email, password: state.password, - submitSignUpForm: state.submitSignUpForm, }); const mapDispatchToProps = dispatch => @@ -150,8 +140,6 @@ const mapDispatchToProps = dispatch => setView, setEmail, setPassword, - enableSubmitSignUpForm, - disableSubmitSignUpForm, }, dispatch ); From c5dc8454a2700c64557a4c6b1f375de8d2e2ef5c Mon Sep 17 00:00:00 2001 From: okbel Date: Thu, 29 Mar 2018 11:15:34 -0300 Subject: [PATCH 12/84] Ignoring --- .gitignore | 1 - 1 file changed, 1 deletion(-) diff --git a/.gitignore b/.gitignore index 5bf0d6527..7c0007dc9 100644 --- a/.gitignore +++ b/.gitignore @@ -63,7 +63,6 @@ plugins/* !plugins/talk-plugin-viewing-options !plugins/talk-plugin-rich-text !plugins/talk-plugin-rich-text-pell -!plugins/talk-plugin-auth-checkbox **/node_modules/* yarn-error.log From 94dbfd68027240284e4ba50c1a8c1f870bb14f81 Mon Sep 17 00:00:00 2001 From: okbel Date: Thu, 29 Mar 2018 11:15:48 -0300 Subject: [PATCH 13/84] re entering --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 7c0007dc9..5bf0d6527 100644 --- a/.gitignore +++ b/.gitignore @@ -63,6 +63,7 @@ plugins/* !plugins/talk-plugin-viewing-options !plugins/talk-plugin-rich-text !plugins/talk-plugin-rich-text-pell +!plugins/talk-plugin-auth-checkbox **/node_modules/* yarn-error.log From bfea8b00e157511920474d27c3005ff471c02569 Mon Sep 17 00:00:00 2001 From: okbel Date: Thu, 29 Mar 2018 11:18:29 -0300 Subject: [PATCH 14/84] adding it back --- plugins/talk-plugin-auth-checkbox | 1 - plugins/talk-plugin-auth-checkbox/README.md | 1 + .../client/.eslintrc.json | 23 ++++++ .../components/TermsAndConditionsField.css | 13 ++++ .../components/TermsAndConditionsField.js | 73 +++++++++++++++++++ .../talk-plugin-auth-checkbox/client/index.js | 9 +++ .../client/translations.yml | 36 +++++++++ plugins/talk-plugin-auth-checkbox/index.js | 1 + 8 files changed, 156 insertions(+), 1 deletion(-) delete mode 160000 plugins/talk-plugin-auth-checkbox create mode 100644 plugins/talk-plugin-auth-checkbox/README.md create mode 100644 plugins/talk-plugin-auth-checkbox/client/.eslintrc.json create mode 100644 plugins/talk-plugin-auth-checkbox/client/components/TermsAndConditionsField.css create mode 100644 plugins/talk-plugin-auth-checkbox/client/components/TermsAndConditionsField.js create mode 100644 plugins/talk-plugin-auth-checkbox/client/index.js create mode 100644 plugins/talk-plugin-auth-checkbox/client/translations.yml create mode 100644 plugins/talk-plugin-auth-checkbox/index.js diff --git a/plugins/talk-plugin-auth-checkbox b/plugins/talk-plugin-auth-checkbox deleted file mode 160000 index 73cf96e85..000000000 --- a/plugins/talk-plugin-auth-checkbox +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 73cf96e8565c6651a10f75f99704db9479bb7d13 diff --git a/plugins/talk-plugin-auth-checkbox/README.md b/plugins/talk-plugin-auth-checkbox/README.md new file mode 100644 index 000000000..9b70bde6c --- /dev/null +++ b/plugins/talk-plugin-auth-checkbox/README.md @@ -0,0 +1 @@ +# talk-plugin-auth-checkbox diff --git a/plugins/talk-plugin-auth-checkbox/client/.eslintrc.json b/plugins/talk-plugin-auth-checkbox/client/.eslintrc.json new file mode 100644 index 000000000..9fe56bd14 --- /dev/null +++ b/plugins/talk-plugin-auth-checkbox/client/.eslintrc.json @@ -0,0 +1,23 @@ +{ + "env": { + "browser": true, + "es6": true, + "mocha": true + }, + "parserOptions": { + "sourceType": "module", + "ecmaFeatures": { + "experimentalObjectRestSpread": true, + "jsx": true + } + }, + "parser": "babel-eslint", + "plugins": [ + "react" + ], + "rules": { + "react/jsx-uses-react": "error", + "react/jsx-uses-vars": "error", + "no-console": ["warn", { "allow": ["warn", "error"] }] + } +} diff --git a/plugins/talk-plugin-auth-checkbox/client/components/TermsAndConditionsField.css b/plugins/talk-plugin-auth-checkbox/client/components/TermsAndConditionsField.css new file mode 100644 index 000000000..e05170b46 --- /dev/null +++ b/plugins/talk-plugin-auth-checkbox/client/components/TermsAndConditionsField.css @@ -0,0 +1,13 @@ +.link { + color: #2c69b6; + cursor: pointer; + margin: 0 3px; + + &:last-child { + margin-right: 0; + } +} + +.fieldContainer { + padding: 5px 0; +} \ No newline at end of file diff --git a/plugins/talk-plugin-auth-checkbox/client/components/TermsAndConditionsField.js b/plugins/talk-plugin-auth-checkbox/client/components/TermsAndConditionsField.js new file mode 100644 index 000000000..25f97dc09 --- /dev/null +++ b/plugins/talk-plugin-auth-checkbox/client/components/TermsAndConditionsField.js @@ -0,0 +1,73 @@ +import React from 'react'; +import { Checkbox } from 'plugin-api/beta/client/components/ui'; +import { t } from 'plugin-api/beta/client/services'; +import styles from './TermsAndConditionsField.css'; +import cn from 'classnames'; + +const pluginName = 'talk-plugin-auth-checkbox'; + +const TermsLink = () => ( + + {t('talk-plugin-health-report.terms')} + +); + +const PrivacyLink = () => ( + + {t('talk-plugin-health-report.privacy-policy')} + +); + +class TermsAndConditionsField extends React.Component { + state = { checked: false }; + id = 'terms-and-conditions'; + + componentWillMount() { + this.props.indicateBlockerOn(pluginName); + } + + onChange = ({ target: { checked } }) => { + if (checked) { + this.setState(() => ({ checked })); + this.props.indicateBlockerOff(pluginName); + } else { + this.setState(() => ({ checked })); + this.props.indicateBlockerOn(pluginName); + } + }; + + render() { + return ( +
+ + +
+ ); + } +} + +export default TermsAndConditionsField; diff --git a/plugins/talk-plugin-auth-checkbox/client/index.js b/plugins/talk-plugin-auth-checkbox/client/index.js new file mode 100644 index 000000000..d2aaaa1cb --- /dev/null +++ b/plugins/talk-plugin-auth-checkbox/client/index.js @@ -0,0 +1,9 @@ +import TermsAndConditionsField from './components/TermsAndConditionsField'; +import translations from './translations.yml'; + +export default { + slots: { + 'talkPluginAuth-formField': [TermsAndConditionsField], + }, + translations, +}; diff --git a/plugins/talk-plugin-auth-checkbox/client/translations.yml b/plugins/talk-plugin-auth-checkbox/client/translations.yml new file mode 100644 index 000000000..66bf7379c --- /dev/null +++ b/plugins/talk-plugin-auth-checkbox/client/translations.yml @@ -0,0 +1,36 @@ +en: + talk-plugin-health-report: + copy: I agree to the Mozilla + and: and + terms: Terms of Service + privacy-policy: Privacy Policy + from: . + terms-link: https://www.mozilla.org/en-US/about/legal/terms/mozilla/ + privacy-policy-link: https://www.mozilla.org/en-US/privacy/websites/ +es: + talk-plugin-health-report: + copy: Estoy de acuerdo con + and: y + terms: los Terminos y Condiciones + privacy-policy: la Política de Privacidad + from: de Mozilla. + terms-link: https://www.mozilla.org/es-ES/about/legal/terms/mozilla/ + privacy-policy-link: https://www.mozilla.org/es-ES/privacy/websites/ +fr: + talk-plugin-health-report: + copy: J'accepte + and: et + terms: les conditions d'utilisation + privacy-policy: la politique de confidentialité + from: de Mozilla. + terms-link: https://www.mozilla.org/fr/about/legal/terms/mozilla/ + privacy-policy-link: https://www.mozilla.org/fr/privacy/websites/ +de: + talk-plugin-health-report: + copy: Ich stimme + and: und + terms: den Allgemeinen Geschäftsbedingungen + privacy-policy: der Datenschutzrichtlinie + from: von Mozilla zu. + terms-link: https://www.mozilla.org/de/about/legal/terms/mozilla/ + privacy-policy-link: https://www.mozilla.org/de/privacy/websites/ \ No newline at end of file diff --git a/plugins/talk-plugin-auth-checkbox/index.js b/plugins/talk-plugin-auth-checkbox/index.js new file mode 100644 index 000000000..f053ebf79 --- /dev/null +++ b/plugins/talk-plugin-auth-checkbox/index.js @@ -0,0 +1 @@ +module.exports = {}; From 8d147c9e7b406d1932ae96b01fb83206ebb451a8 Mon Sep 17 00:00:00 2001 From: okbel Date: Thu, 29 Mar 2018 13:27:35 -0300 Subject: [PATCH 15/84] Styling --- .../client/components/TermsAndConditionsField.css | 7 ++++++- .../client/components/TermsAndConditionsField.js | 10 ++++++++-- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/plugins/talk-plugin-auth-checkbox/client/components/TermsAndConditionsField.css b/plugins/talk-plugin-auth-checkbox/client/components/TermsAndConditionsField.css index e05170b46..c11f408a3 100644 --- a/plugins/talk-plugin-auth-checkbox/client/components/TermsAndConditionsField.css +++ b/plugins/talk-plugin-auth-checkbox/client/components/TermsAndConditionsField.css @@ -9,5 +9,10 @@ } .fieldContainer { - padding: 5px 0; + padding: 10px 0; + display: flex; +} + +.textLabel { + padding-left: 10px; } \ No newline at end of file diff --git a/plugins/talk-plugin-auth-checkbox/client/components/TermsAndConditionsField.js b/plugins/talk-plugin-auth-checkbox/client/components/TermsAndConditionsField.js index 25f97dc09..dec0d86ce 100644 --- a/plugins/talk-plugin-auth-checkbox/client/components/TermsAndConditionsField.js +++ b/plugins/talk-plugin-auth-checkbox/client/components/TermsAndConditionsField.js @@ -58,13 +58,19 @@ class TermsAndConditionsField extends React.Component { onChange={this.onChange} id={this.id} /> -
); } From 761e372016e26c53d31023a27df368e9e41e9ec8 Mon Sep 17 00:00:00 2001 From: Chi Vinh Le Date: Thu, 29 Mar 2018 22:47:27 +0200 Subject: [PATCH 16/84] Give descriptive key for slot elements and use it instead of talkPluginName --- .gitignore | 1 + .../src/containers/ExtendableTabPanel.js | 9 +- client/coral-framework/services/plugins.js | 23 +- docs/source/_data/introspection.json | 9203 ----------------- .../components/TermsAndConditionsField.js | 11 +- .../client/login/components/SignUp.js | 29 +- .../client/login/containers/SignUp.js | 16 +- .../client/components/Settings.js | 6 +- .../client/containers/Settings.js | 8 +- 9 files changed, 59 insertions(+), 9247 deletions(-) delete mode 100644 docs/source/_data/introspection.json diff --git a/.gitignore b/.gitignore index 2c8dc71ca..a660453ea 100644 --- a/.gitignore +++ b/.gitignore @@ -6,6 +6,7 @@ npm-debug.log* dump.rdb client/coral-framework/graphql/introspection.json +docs/source/_data/introspection.json .env *.cfg diff --git a/client/coral-embed-stream/src/containers/ExtendableTabPanel.js b/client/coral-embed-stream/src/containers/ExtendableTabPanel.js index 678981a8e..c2935b152 100644 --- a/client/coral-embed-stream/src/containers/ExtendableTabPanel.js +++ b/client/coral-embed-stream/src/containers/ExtendableTabPanel.js @@ -35,12 +35,9 @@ class ExtendableTabPanelContainer extends React.Component { createPluginTabFactory = (props = this.props) => el => { return ( - + {React.cloneElement(el, { - active: props.activeTab === el.type.talkPluginName, + active: props.activeTab === el.key, })} ); @@ -59,7 +56,7 @@ class ExtendableTabPanelContainer extends React.Component { createPluginTabPane(el) { return ( - + {el} ); diff --git a/client/coral-framework/services/plugins.js b/client/coral-framework/services/plugins.js index bb16fc5e7..a25e27b76 100644 --- a/client/coral-framework/services/plugins.js +++ b/client/coral-framework/services/plugins.js @@ -173,11 +173,30 @@ class PluginsService { ); } + /** + * This adds a consistent keying for the slot elements. + * It uses the plugin name as the key. If the same plugin inserts + * multiple elements it will append `.${noOfOccurence}` to the + * key starting with the second element. + */ + const getKey = (() => { + const map = {}; + return component => { + if (map[component.talkPluginName] === undefined) { + map[component.talkPluginName] = 0; + } else { + map[component.talkPluginName]++; + } + const i = map[component.talkPluginName]; + return `${component.talkPluginName}${i > 0 ? `.${i}` : ''}`; + }; + })(); + return (size > 0 ? slots.slice(0, size) : slots) - .map((component, i) => ({ + .map(component => ({ component, disabled: isDisabled(component), - key: i, + key: getKey(component), })) .filter(o => !o.disabled) .map(({ component, key }) => diff --git a/docs/source/_data/introspection.json b/docs/source/_data/introspection.json deleted file mode 100644 index 4e5390999..000000000 --- a/docs/source/_data/introspection.json +++ /dev/null @@ -1,9203 +0,0 @@ -{ - "__schema": { - "queryType": { - "name": "RootQuery" - }, - "mutationType": { - "name": "RootMutation" - }, - "subscriptionType": { - "name": "Subscription" - }, - "types": [ - { - "kind": "OBJECT", - "name": "RootQuery", - "description": "", - "fields": [ - { - "name": "settings", - "description": "Site wide settings and defaults.", - "args": [], - "type": { - "kind": "OBJECT", - "name": "Settings", - "ofType": null - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "comment", - "description": "Finds a specific comment based on it's id.", - "args": [ - { - "name": "id", - "description": "", - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "SCALAR", - "name": "ID", - "ofType": null - } - }, - "defaultValue": null - } - ], - "type": { - "kind": "OBJECT", - "name": "Comment", - "ofType": null - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "assets", - "description": "All assets. Requires the `ADMIN` role.", - "args": [ - { - "name": "query", - "description": "", - "type": { - "kind": "INPUT_OBJECT", - "name": "AssetsQuery", - "ofType": null - }, - "defaultValue": null - } - ], - "type": { - "kind": "OBJECT", - "name": "AssetConnection", - "ofType": null - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "asset", - "description": "Find or create an asset by url, or just find with the ID.", - "args": [ - { - "name": "id", - "description": "", - "type": { - "kind": "SCALAR", - "name": "ID", - "ofType": null - }, - "defaultValue": null - }, - { - "name": "url", - "description": "", - "type": { - "kind": "SCALAR", - "name": "String", - "ofType": null - }, - "defaultValue": null - } - ], - "type": { - "kind": "OBJECT", - "name": "Asset", - "ofType": null - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "comments", - "description": "Comments returned based on a query.", - "args": [ - { - "name": "query", - "description": "", - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "INPUT_OBJECT", - "name": "CommentsQuery", - "ofType": null - } - }, - "defaultValue": null - } - ], - "type": { - "kind": "OBJECT", - "name": "CommentConnection", - "ofType": null - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "commentCount", - "description": "Return the count of comments satisfied by the query. Note that this edge is\nexpensive as it is not batched. Requires the `ADMIN` role.", - "args": [ - { - "name": "query", - "description": "", - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "INPUT_OBJECT", - "name": "CommentCountQuery", - "ofType": null - } - }, - "defaultValue": null - } - ], - "type": { - "kind": "SCALAR", - "name": "Int", - "ofType": null - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "userCount", - "description": "Return the count of users satisfied by the query. Note that this edge is\nexpensive as it is not batched. This field is restricted.", - "args": [ - { - "name": "query", - "description": "", - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "INPUT_OBJECT", - "name": "UserCountQuery", - "ofType": null - } - }, - "defaultValue": null - } - ], - "type": { - "kind": "SCALAR", - "name": "Int", - "ofType": null - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "me", - "description": "The currently logged in user based on the request. Requires any logged in\nrole.", - "args": [], - "type": { - "kind": "OBJECT", - "name": "User", - "ofType": null - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "users", - "description": "Users returned based on a query. Requires the `ADMIN` role.", - "args": [ - { - "name": "query", - "description": "", - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "INPUT_OBJECT", - "name": "UsersQuery", - "ofType": null - } - }, - "defaultValue": null - } - ], - "type": { - "kind": "OBJECT", - "name": "UserConnection", - "ofType": null - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "user", - "description": "a single User by id", - "args": [ - { - "name": "id", - "description": "", - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "SCALAR", - "name": "ID", - "ofType": null - } - }, - "defaultValue": null - } - ], - "type": { - "kind": "OBJECT", - "name": "User", - "ofType": null - }, - "isDeprecated": false, - "deprecationReason": null - } - ], - "inputFields": null, - "interfaces": [], - "enumValues": null, - "possibleTypes": null - }, - { - "kind": "OBJECT", - "name": "Settings", - "description": "", - "fields": [ - { - "name": "moderation", - "description": "moderation is the moderation mode for all Asset's on the site.", - "args": [], - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "ENUM", - "name": "MODERATION_MODE", - "ofType": null - } - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "requireEmailConfirmation", - "description": "Enables a requirement for email confirmation before a user can login.", - "args": [], - "type": { - "kind": "SCALAR", - "name": "Boolean", - "ofType": null - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "infoBoxEnable", - "description": "infoBoxEnable will enable the Info Box content visible above the question\nbox.", - "args": [], - "type": { - "kind": "SCALAR", - "name": "Boolean", - "ofType": null - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "infoBoxContent", - "description": "infoBoxContent is the content of the Info Box.", - "args": [], - "type": { - "kind": "SCALAR", - "name": "String", - "ofType": null - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "questionBoxEnable", - "description": "questionBoxEnable will enable the Question Box's content to be visible above\nThe comment box.", - "args": [], - "type": { - "kind": "SCALAR", - "name": "Boolean", - "ofType": null - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "questionBoxContent", - "description": "questionBoxContent is the content of the Question Box.", - "args": [], - "type": { - "kind": "SCALAR", - "name": "String", - "ofType": null - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "premodLinksEnable", - "description": "premodLinksEnable will put all comments that contain links into premod.", - "args": [], - "type": { - "kind": "SCALAR", - "name": "Boolean", - "ofType": null - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "questionBoxIcon", - "description": "questionBoxIcon is the icon for the Question Box.", - "args": [], - "type": { - "kind": "SCALAR", - "name": "String", - "ofType": null - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "autoCloseStream", - "description": "autoCloseStream when true will auto close the stream when the `closeTimeout`\namount of seconds have been reached.", - "args": [], - "type": { - "kind": "SCALAR", - "name": "Boolean", - "ofType": null - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "customCssUrl", - "description": "customCssUrl is the URL of the custom CSS used to display on the frontend.", - "args": [], - "type": { - "kind": "SCALAR", - "name": "String", - "ofType": null - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "closedTimeout", - "description": "closedTimeout is the amount of seconds from the created_at timestamp that a\ngiven asset will be considered closed.", - "args": [], - "type": { - "kind": "SCALAR", - "name": "Int", - "ofType": null - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "closedMessage", - "description": "closedMessage is the message shown to the user when the given Asset is\nclosed.", - "args": [], - "type": { - "kind": "SCALAR", - "name": "String", - "ofType": null - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "editCommentWindowLength", - "description": "editCommentWindowLength is the length of time (in milliseconds) after a\ncomment is posted that it can still be edited by the author.", - "args": [], - "type": { - "kind": "SCALAR", - "name": "Int", - "ofType": null - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "charCountEnable", - "description": "charCountEnable is true when the character count restriction is enabled.", - "args": [], - "type": { - "kind": "SCALAR", - "name": "Boolean", - "ofType": null - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "charCount", - "description": "charCount is the maximum number of characters a comment may be.", - "args": [], - "type": { - "kind": "SCALAR", - "name": "Int", - "ofType": null - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "organizationName", - "description": "organizationName is the name of the organization.", - "args": [], - "type": { - "kind": "SCALAR", - "name": "String", - "ofType": null - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "wordlist", - "description": "wordlist will return a given list of words.", - "args": [], - "type": { - "kind": "OBJECT", - "name": "Wordlist", - "ofType": null - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "domains", - "description": "domains will return a given list of domains.", - "args": [], - "type": { - "kind": "OBJECT", - "name": "Domains", - "ofType": null - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "notificationsRequireConfirmation", - "description": "notificationsRequireConfirmation when true indicates that User's must have\ntheir email address confirmed/verified before they can receive\nnotifications.", - "args": [], - "type": { - "kind": "SCALAR", - "name": "Boolean", - "ofType": null - }, - "isDeprecated": false, - "deprecationReason": null - } - ], - "inputFields": null, - "interfaces": [], - "enumValues": null, - "possibleTypes": null - }, - { - "kind": "ENUM", - "name": "MODERATION_MODE", - "description": "The moderation mode of the site.", - "fields": null, - "inputFields": null, - "interfaces": null, - "enumValues": [ - { - "name": "PRE", - "description": "Comments posted while in `PRE` mode will be labeled with a `PREMOD`\nstatus and will require a moderator decision before being visible.", - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "POST", - "description": "Comments posted while in `POST` will be visible immediately.", - "isDeprecated": false, - "deprecationReason": null - } - ], - "possibleTypes": null - }, - { - "kind": "SCALAR", - "name": "Boolean", - "description": "The `Boolean` scalar type represents `true` or `false`.", - "fields": null, - "inputFields": null, - "interfaces": null, - "enumValues": null, - "possibleTypes": null - }, - { - "kind": "SCALAR", - "name": "String", - "description": "The `String` scalar type represents textual data, represented as UTF-8 character sequences. The String type is most often used by GraphQL to represent free-form human-readable text.", - "fields": null, - "inputFields": null, - "interfaces": null, - "enumValues": null, - "possibleTypes": null - }, - { - "kind": "SCALAR", - "name": "Int", - "description": "The `Int` scalar type represents non-fractional signed whole numeric values. Int can represent values between -(2^31) and 2^31 - 1. ", - "fields": null, - "inputFields": null, - "interfaces": null, - "enumValues": null, - "possibleTypes": null - }, - { - "kind": "OBJECT", - "name": "Wordlist", - "description": "Wordlist describes all the available wordlists.", - "fields": [ - { - "name": "banned", - "description": "banned words will by default reject the comment if it is found.", - "args": [], - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "LIST", - "name": null, - "ofType": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "SCALAR", - "name": "String", - "ofType": null - } - } - } - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "suspect", - "description": "suspect words will simply flag the comment.", - "args": [], - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "LIST", - "name": null, - "ofType": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "SCALAR", - "name": "String", - "ofType": null - } - } - } - }, - "isDeprecated": false, - "deprecationReason": null - } - ], - "inputFields": null, - "interfaces": [], - "enumValues": null, - "possibleTypes": null - }, - { - "kind": "OBJECT", - "name": "Domains", - "description": "Domains describes all the available lists of domains.", - "fields": [ - { - "name": "whitelist", - "description": "whitelist is the list of domains that the embed is allowed to render on.", - "args": [], - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "LIST", - "name": null, - "ofType": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "SCALAR", - "name": "String", - "ofType": null - } - } - } - }, - "isDeprecated": false, - "deprecationReason": null - } - ], - "inputFields": null, - "interfaces": [], - "enumValues": null, - "possibleTypes": null - }, - { - "kind": "SCALAR", - "name": "ID", - "description": "The `ID` scalar type represents a unique identifier, often used to refetch an object or as key for a cache. The ID type appears in a JSON response as a String; however, it is not intended to be human-readable. When expected as an input type, any string (such as `\"4\"`) or integer (such as `4`) input value will be accepted as an ID.", - "fields": null, - "inputFields": null, - "interfaces": null, - "enumValues": null, - "possibleTypes": null - }, - { - "kind": "OBJECT", - "name": "Comment", - "description": "", - "fields": [ - { - "name": "parent", - "description": "The parent of the comment (if there is one).", - "args": [], - "type": { - "kind": "OBJECT", - "name": "Comment", - "ofType": null - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "id", - "description": "The ID of the comment.", - "args": [], - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "SCALAR", - "name": "ID", - "ofType": null - } - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "body", - "description": "The actual comment data.", - "args": [], - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "SCALAR", - "name": "String", - "ofType": null - } - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "body_history", - "description": "The body history of the comment.", - "args": [], - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "LIST", - "name": null, - "ofType": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "OBJECT", - "name": "CommentBodyHistory", - "ofType": null - } - } - } - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "tags", - "description": "The tags on the comment", - "args": [], - "type": { - "kind": "LIST", - "name": null, - "ofType": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "OBJECT", - "name": "TagLink", - "ofType": null - } - } - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "user", - "description": "The user who authored the comment.", - "args": [], - "type": { - "kind": "OBJECT", - "name": "User", - "ofType": null - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "replies", - "description": "The replies that were made to the comment.", - "args": [ - { - "name": "query", - "description": "", - "type": { - "kind": "INPUT_OBJECT", - "name": "RepliesQuery", - "ofType": null - }, - "defaultValue": "{sortOrder: ASC, sortBy: CREATED_AT, limit: 3}" - } - ], - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "OBJECT", - "name": "CommentConnection", - "ofType": null - } - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "replyCount", - "description": "replyCount is the number of replies with a depth of 1. Only direct replies\nto this comment are counted.", - "args": [], - "type": { - "kind": "SCALAR", - "name": "Int", - "ofType": null - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "actions", - "description": "Actions completed on the parent. Requires the `ADMIN` role.", - "args": [], - "type": { - "kind": "LIST", - "name": null, - "ofType": { - "kind": "INTERFACE", - "name": "Action", - "ofType": null - } - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "action_summaries", - "description": "Action summaries against a comment.", - "args": [], - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "LIST", - "name": null, - "ofType": { - "kind": "INTERFACE", - "name": "ActionSummary", - "ofType": null - } - } - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "asset", - "description": "The asset that a comment was made on.", - "args": [], - "type": { - "kind": "OBJECT", - "name": "Asset", - "ofType": null - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "status", - "description": "The current status of a comment.", - "args": [], - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "ENUM", - "name": "COMMENT_STATUS", - "ofType": null - } - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "status_history", - "description": "The status history of the comment. Requires the `ADMIN` or `MODERATOR` role.", - "args": [], - "type": { - "kind": "LIST", - "name": null, - "ofType": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "OBJECT", - "name": "CommentStatusHistory", - "ofType": null - } - } - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "created_at", - "description": "The time when the comment was created", - "args": [], - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "SCALAR", - "name": "Date", - "ofType": null - } - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "updated_at", - "description": "The time when the comment was updated.", - "args": [], - "type": { - "kind": "SCALAR", - "name": "Date", - "ofType": null - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "editing", - "description": "describes how the comment can be edited", - "args": [], - "type": { - "kind": "OBJECT", - "name": "EditInfo", - "ofType": null - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "hasParent", - "description": "Indicates if it has a parent", - "args": [], - "type": { - "kind": "SCALAR", - "name": "Boolean", - "ofType": null - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "richTextBody", - "description": "", - "args": [], - "type": { - "kind": "SCALAR", - "name": "String", - "ofType": null - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "toxicity", - "description": "", - "args": [], - "type": { - "kind": "SCALAR", - "name": "Float", - "ofType": null - }, - "isDeprecated": false, - "deprecationReason": null - } - ], - "inputFields": null, - "interfaces": [], - "enumValues": null, - "possibleTypes": null - }, - { - "kind": "OBJECT", - "name": "CommentBodyHistory", - "description": "", - "fields": [ - { - "name": "body", - "description": "", - "args": [], - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "SCALAR", - "name": "String", - "ofType": null - } - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "created_at", - "description": "", - "args": [], - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "SCALAR", - "name": "Date", - "ofType": null - } - }, - "isDeprecated": false, - "deprecationReason": null - } - ], - "inputFields": null, - "interfaces": [], - "enumValues": null, - "possibleTypes": null - }, - { - "kind": "SCALAR", - "name": "Date", - "description": "Date represented as an ISO8601 string", - "fields": null, - "inputFields": null, - "interfaces": null, - "enumValues": null, - "possibleTypes": null - }, - { - "kind": "OBJECT", - "name": "TagLink", - "description": "TagLink is used to associate a given Tag with a Model via a TagLink.", - "fields": [ - { - "name": "tag", - "description": "The underlying Tag that is either duplicated from the global list or created\nuniquely for this specific model.", - "args": [], - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "OBJECT", - "name": "Tag", - "ofType": null - } - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "assigned_by", - "description": "The user that assigned the tag. This TagLink could have been created by the\nsystem, in which case this will be null. It could also be null if the\ncurrent user is not an Admin/Moderator.", - "args": [], - "type": { - "kind": "OBJECT", - "name": "User", - "ofType": null - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "created_at", - "description": "The date that the TagLink was created.", - "args": [], - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "SCALAR", - "name": "Date", - "ofType": null - } - }, - "isDeprecated": false, - "deprecationReason": null - } - ], - "inputFields": null, - "interfaces": [], - "enumValues": null, - "possibleTypes": null - }, - { - "kind": "OBJECT", - "name": "Tag", - "description": "Tag represents the underlying Tag that can be either stored in a global list\nor added uniquely to the entity.", - "fields": [ - { - "name": "name", - "description": "The actual name of the tag entry.", - "args": [], - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "SCALAR", - "name": "String", - "ofType": null - } - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "created_at", - "description": "The time that this Tag was created.", - "args": [], - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "SCALAR", - "name": "Date", - "ofType": null - } - }, - "isDeprecated": false, - "deprecationReason": null - } - ], - "inputFields": null, - "interfaces": [], - "enumValues": null, - "possibleTypes": null - }, - { - "kind": "OBJECT", - "name": "User", - "description": "", - "fields": [ - { - "name": "id", - "description": "The ID of the User.", - "args": [], - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "SCALAR", - "name": "ID", - "ofType": null - } - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "username", - "description": "Username of a user.", - "args": [], - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "SCALAR", - "name": "String", - "ofType": null - } - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "created_at", - "description": "creation date of user", - "args": [], - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "SCALAR", - "name": "Date", - "ofType": null - } - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "action_summaries", - "description": "Action summaries against the user.", - "args": [], - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "LIST", - "name": null, - "ofType": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "INTERFACE", - "name": "ActionSummary", - "ofType": null - } - } - } - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "actions", - "description": "Actions completed on the parent.", - "args": [], - "type": { - "kind": "LIST", - "name": null, - "ofType": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "INTERFACE", - "name": "Action", - "ofType": null - } - } - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "role", - "description": "The current roles of the user.", - "args": [], - "type": { - "kind": "ENUM", - "name": "USER_ROLES", - "ofType": null - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "profiles", - "description": "The current profiles of the user.", - "args": [], - "type": { - "kind": "LIST", - "name": null, - "ofType": { - "kind": "INTERFACE", - "name": "UserProfile", - "ofType": null - } - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "email", - "description": "The primary email address of the user. Only accessible to Administrators or\nthe current user.", - "args": [], - "type": { - "kind": "SCALAR", - "name": "String", - "ofType": null - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "tags", - "description": "The tags on the user.", - "args": [], - "type": { - "kind": "LIST", - "name": null, - "ofType": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "OBJECT", - "name": "TagLink", - "ofType": null - } - } - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "ignoredUsers", - "description": "ignored users.", - "args": [], - "type": { - "kind": "LIST", - "name": null, - "ofType": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "OBJECT", - "name": "User", - "ofType": null - } - } - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "tokens", - "description": "Tokens are the personal access tokens for a given user.", - "args": [], - "type": { - "kind": "LIST", - "name": null, - "ofType": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "OBJECT", - "name": "Token", - "ofType": null - } - } - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "comments", - "description": "returns all comments based on a query.", - "args": [ - { - "name": "query", - "description": "", - "type": { - "kind": "INPUT_OBJECT", - "name": "CommentsQuery", - "ofType": null - }, - "defaultValue": null - } - ], - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "OBJECT", - "name": "CommentConnection", - "ofType": null - } - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "reliable", - "description": "reliable is the reference to a given user's Reliability. If the requesting\nuser does not have permission to access the reliability, null will be\nreturned.", - "args": [], - "type": { - "kind": "OBJECT", - "name": "Reliability", - "ofType": null - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "state", - "description": "returns user status", - "args": [], - "type": { - "kind": "OBJECT", - "name": "UserState", - "ofType": null - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "notificationSettings", - "description": "", - "args": [], - "type": { - "kind": "OBJECT", - "name": "NotificationSettings", - "ofType": null - }, - "isDeprecated": false, - "deprecationReason": null - } - ], - "inputFields": null, - "interfaces": [], - "enumValues": null, - "possibleTypes": null - }, - { - "kind": "INTERFACE", - "name": "ActionSummary", - "description": "A summary of actions based on the specific grouping of the group_id.", - "fields": [ - { - "name": "count", - "description": "The count of actions with this group.", - "args": [], - "type": { - "kind": "SCALAR", - "name": "Int", - "ofType": null - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "current_user", - "description": "The current user's action.", - "args": [], - "type": { - "kind": "INTERFACE", - "name": "Action", - "ofType": null - }, - "isDeprecated": false, - "deprecationReason": null - } - ], - "inputFields": null, - "interfaces": null, - "enumValues": null, - "possibleTypes": [ - { - "kind": "OBJECT", - "name": "DefaultActionSummary", - "ofType": null - }, - { - "kind": "OBJECT", - "name": "FlagActionSummary", - "ofType": null - }, - { - "kind": "OBJECT", - "name": "DontAgreeActionSummary", - "ofType": null - }, - { - "kind": "OBJECT", - "name": "RespectActionSummary", - "ofType": null - } - ] - }, - { - "kind": "INTERFACE", - "name": "Action", - "description": "An action rendered against a parent entity item.", - "fields": [ - { - "name": "id", - "description": "The ID of the action.", - "args": [], - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "SCALAR", - "name": "ID", - "ofType": null - } - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "user", - "description": "The author of the action.", - "args": [], - "type": { - "kind": "OBJECT", - "name": "User", - "ofType": null - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "updated_at", - "description": "The time when the Action was updated.", - "args": [], - "type": { - "kind": "SCALAR", - "name": "Date", - "ofType": null - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "created_at", - "description": "The time when the Action was created.", - "args": [], - "type": { - "kind": "SCALAR", - "name": "Date", - "ofType": null - }, - "isDeprecated": false, - "deprecationReason": null - } - ], - "inputFields": null, - "interfaces": null, - "enumValues": null, - "possibleTypes": [ - { - "kind": "OBJECT", - "name": "FlagAction", - "ofType": null - }, - { - "kind": "OBJECT", - "name": "DontAgreeAction", - "ofType": null - }, - { - "kind": "OBJECT", - "name": "RespectAction", - "ofType": null - }, - { - "kind": "OBJECT", - "name": "DefaultAction", - "ofType": null - } - ] - }, - { - "kind": "ENUM", - "name": "USER_ROLES", - "description": "Roles that a user can have, these can be combined.", - "fields": null, - "inputFields": null, - "interfaces": null, - "enumValues": [ - { - "name": "ADMIN", - "description": "an administrator of the site", - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "MODERATOR", - "description": "a moderator of the site", - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "STAFF", - "description": "a staff of the site", - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "COMMENTER", - "description": "a user without administrative roles.", - "isDeprecated": false, - "deprecationReason": null - } - ], - "possibleTypes": null - }, - { - "kind": "INTERFACE", - "name": "UserProfile", - "description": "", - "fields": [ - { - "name": "id", - "description": "The id is an identifier for the user profile (email, facebook id, etc)", - "args": [], - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "SCALAR", - "name": "String", - "ofType": null - } - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "provider", - "description": "name of the provider attached to the authentication mode", - "args": [], - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "SCALAR", - "name": "String", - "ofType": null - } - }, - "isDeprecated": false, - "deprecationReason": null - } - ], - "inputFields": null, - "interfaces": null, - "enumValues": null, - "possibleTypes": [ - { - "kind": "OBJECT", - "name": "DefaultUserProfile", - "ofType": null - }, - { - "kind": "OBJECT", - "name": "LocalUserProfile", - "ofType": null - } - ] - }, - { - "kind": "OBJECT", - "name": "Token", - "description": "Token is a personal access token associated with a given user.", - "fields": [ - { - "name": "id", - "description": "ID is the unique identifier for the token.", - "args": [], - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "SCALAR", - "name": "ID", - "ofType": null - } - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "name", - "description": "Name is the description for the token.", - "args": [], - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "SCALAR", - "name": "String", - "ofType": null - } - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "active", - "description": "Active determines if the token is available to hit the API.", - "args": [], - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "SCALAR", - "name": "Boolean", - "ofType": null - } - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "jwt", - "description": "JWT is the actual token to use for authentication, this is only available\non token creation, otherwise it will be null.", - "args": [], - "type": { - "kind": "SCALAR", - "name": "String", - "ofType": null - }, - "isDeprecated": false, - "deprecationReason": null - } - ], - "inputFields": null, - "interfaces": [], - "enumValues": null, - "possibleTypes": null - }, - { - "kind": "INPUT_OBJECT", - "name": "CommentsQuery", - "description": "CommentsQuery allows the ability to query comments by a specific methods.", - "fields": null, - "inputFields": [ - { - "name": "author_id", - "description": "Author of the comments.", - "type": { - "kind": "SCALAR", - "name": "ID", - "ofType": null - }, - "defaultValue": null - }, - { - "name": "statuses", - "description": "Current status of a comment.\nThis field is restricted.", - "type": { - "kind": "LIST", - "name": null, - "ofType": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "ENUM", - "name": "COMMENT_STATUS", - "ofType": null - } - } - }, - "defaultValue": "[NONE, ACCEPTED]" - }, - { - "name": "asset_id", - "description": "Asset that a comment is on.", - "type": { - "kind": "SCALAR", - "name": "ID", - "ofType": null - }, - "defaultValue": null - }, - { - "name": "parent_id", - "description": "The parent of the comment that we want to retrieve.", - "type": { - "kind": "SCALAR", - "name": "ID", - "ofType": null - }, - "defaultValue": null - }, - { - "name": "action_type", - "description": "Comments returned will only be ones which have at least one action of this\ntype. Requires the `ADMIN` role.", - "type": { - "kind": "ENUM", - "name": "ACTION_TYPE", - "ofType": null - }, - "defaultValue": null - }, - { - "name": "limit", - "description": "Limit the number of results to be returned.", - "type": { - "kind": "SCALAR", - "name": "Int", - "ofType": null - }, - "defaultValue": "10" - }, - { - "name": "cursor", - "description": "Skip results from the last created_at timestamp.", - "type": { - "kind": "SCALAR", - "name": "Cursor", - "ofType": null - }, - "defaultValue": null - }, - { - "name": "sortOrder", - "description": "Sort the results by from largest first.", - "type": { - "kind": "ENUM", - "name": "SORT_ORDER", - "ofType": null - }, - "defaultValue": "DESC" - }, - { - "name": "sortBy", - "description": "The order to sort the comments by, sorting by default the created at\ntimestamp.", - "type": { - "kind": "ENUM", - "name": "SORT_COMMENTS_BY", - "ofType": null - }, - "defaultValue": "CREATED_AT" - }, - { - "name": "tags", - "description": "Filter by a specific tag name.", - "type": { - "kind": "LIST", - "name": null, - "ofType": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "SCALAR", - "name": "String", - "ofType": null - } - } - }, - "defaultValue": null - }, - { - "name": "excludeIgnored", - "description": "Exclude comments ignored by the requesting user", - "type": { - "kind": "SCALAR", - "name": "Boolean", - "ofType": null - }, - "defaultValue": null - } - ], - "interfaces": null, - "enumValues": null, - "possibleTypes": null - }, - { - "kind": "ENUM", - "name": "COMMENT_STATUS", - "description": "The statuses that a comment may have.", - "fields": null, - "inputFields": null, - "interfaces": null, - "enumValues": [ - { - "name": "NONE", - "description": "The comment is not PREMOD, but was not applied a moderation status by a\nmoderator.", - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "ACCEPTED", - "description": "The comment has been accepted by a moderator.", - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "REJECTED", - "description": "The comment has been rejected by a moderator.", - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "PREMOD", - "description": "The comment was created while the asset's premoderation option was on, and\nnew comments that haven't been moderated yet are referred to as\n\"premoderated\" or \"premod\" comments.", - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "SYSTEM_WITHHELD", - "description": "SYSTEM_WITHHELD represents a comment that was withheld by the system because\nit was flagged by an internal process for further review.", - "isDeprecated": false, - "deprecationReason": null - } - ], - "possibleTypes": null - }, - { - "kind": "ENUM", - "name": "ACTION_TYPE", - "description": "", - "fields": null, - "inputFields": null, - "interfaces": null, - "enumValues": [ - { - "name": "FLAG", - "description": "Represents a FlagAction.", - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "DONTAGREE", - "description": "Represents a don't agree action", - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "RESPECT", - "description": "Represents a Respect.", - "isDeprecated": false, - "deprecationReason": null - } - ], - "possibleTypes": null - }, - { - "kind": "SCALAR", - "name": "Cursor", - "description": "Cursor represents a paginating cursor.", - "fields": null, - "inputFields": null, - "interfaces": null, - "enumValues": null, - "possibleTypes": null - }, - { - "kind": "ENUM", - "name": "SORT_ORDER", - "description": "Establishes the ordering of the content by their created_at time stamp.", - "fields": null, - "inputFields": null, - "interfaces": null, - "enumValues": [ - { - "name": "DESC", - "description": "newest to oldest order.", - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "ASC", - "description": "oldest to newer order.", - "isDeprecated": false, - "deprecationReason": null - } - ], - "possibleTypes": null - }, - { - "kind": "ENUM", - "name": "SORT_COMMENTS_BY", - "description": "", - "fields": null, - "inputFields": null, - "interfaces": null, - "enumValues": [ - { - "name": "CREATED_AT", - "description": "Comments will be sorted by their created at date.", - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "REPLIES", - "description": "Comments will be sorted by their immediate reply count (replies to the comment\nin question only, not including descendants).", - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "RESPECTS", - "description": "Comments will be sorted by their count of respects\non the comment.", - "isDeprecated": false, - "deprecationReason": null - } - ], - "possibleTypes": null - }, - { - "kind": "OBJECT", - "name": "CommentConnection", - "description": "CommentConnection represents a paginable subset of a comment list.", - "fields": [ - { - "name": "hasNextPage", - "description": "Indicates that there are more comments after this subset.", - "args": [], - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "SCALAR", - "name": "Boolean", - "ofType": null - } - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "startCursor", - "description": "Cursor of first comment in subset.", - "args": [], - "type": { - "kind": "SCALAR", - "name": "Cursor", - "ofType": null - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "endCursor", - "description": "Cursor of last comment in subset.", - "args": [], - "type": { - "kind": "SCALAR", - "name": "Cursor", - "ofType": null - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "nodes", - "description": "Subset of comments.", - "args": [], - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "LIST", - "name": null, - "ofType": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "OBJECT", - "name": "Comment", - "ofType": null - } - } - } - }, - "isDeprecated": false, - "deprecationReason": null - } - ], - "inputFields": null, - "interfaces": [], - "enumValues": null, - "possibleTypes": null - }, - { - "kind": "OBJECT", - "name": "Reliability", - "description": "Reliability defines how a given user should be considered reliable for their\ncomment or flag activity.", - "fields": [ - { - "name": "flagger", - "description": "flagger will be `true` when the flagger is reliable, `false` if not, or\n`null` if the reliability cannot be determined.", - "args": [], - "type": { - "kind": "SCALAR", - "name": "Boolean", - "ofType": null - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "commenter", - "description": "Commenter will be `true` when the commenter is reliable, `false` if not, or\n`null` if the reliability cannot be determined.", - "args": [], - "type": { - "kind": "SCALAR", - "name": "Boolean", - "ofType": null - }, - "isDeprecated": false, - "deprecationReason": null - } - ], - "inputFields": null, - "interfaces": [], - "enumValues": null, - "possibleTypes": null - }, - { - "kind": "OBJECT", - "name": "UserState", - "description": "UserState describes the different permission based details for a user.", - "fields": [ - { - "name": "status", - "description": "status describes the statuses of different aspects of the user's details.", - "args": [], - "type": { - "kind": "OBJECT", - "name": "UserStatus", - "ofType": null - }, - "isDeprecated": false, - "deprecationReason": null - } - ], - "inputFields": null, - "interfaces": [], - "enumValues": null, - "possibleTypes": null - }, - { - "kind": "OBJECT", - "name": "UserStatus", - "description": "", - "fields": [ - { - "name": "username", - "description": "username is the status of the username.", - "args": [], - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "OBJECT", - "name": "UsernameStatus", - "ofType": null - } - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "banned", - "description": "banned is the bool that determines if the user is banned or not.", - "args": [], - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "OBJECT", - "name": "BannedStatus", - "ofType": null - } - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "suspension", - "description": "suspension is the date that the user is suspended until.", - "args": [], - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "OBJECT", - "name": "SuspensionStatus", - "ofType": null - } - }, - "isDeprecated": false, - "deprecationReason": null - } - ], - "inputFields": null, - "interfaces": [], - "enumValues": null, - "possibleTypes": null - }, - { - "kind": "OBJECT", - "name": "UsernameStatus", - "description": "", - "fields": [ - { - "name": "status", - "description": "", - "args": [], - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "ENUM", - "name": "USER_STATUS_USERNAME", - "ofType": null - } - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "history", - "description": "", - "args": [], - "type": { - "kind": "LIST", - "name": null, - "ofType": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "OBJECT", - "name": "UsernameStatusHistory", - "ofType": null - } - } - }, - "isDeprecated": false, - "deprecationReason": null - } - ], - "inputFields": null, - "interfaces": [], - "enumValues": null, - "possibleTypes": null - }, - { - "kind": "ENUM", - "name": "USER_STATUS_USERNAME", - "description": "USER_STATUS_USERNAME is the different states that a username can be in.", - "fields": null, - "inputFields": null, - "interfaces": null, - "enumValues": [ - { - "name": "UNSET", - "description": "UNSET is used when the username can be changed, and does not necessarily\nrequire moderator action to become active. This can be used when the user\nsigns up with a social login and has the option of setting their own\nusername.", - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "SET", - "description": "SET is used when the username has been set for the first time, but cannot\nchange without the username being rejected by a moderator and that moderator\nagreeing that the username should be allowed to change.", - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "APPROVED", - "description": "APPROVED is used when the username was changed, and subsequently approved by\nsaid moderator.", - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "REJECTED", - "description": "REJECTED is used when the username was changed, and subsequently rejected by\nsaid moderator.", - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "CHANGED", - "description": "CHANGED is used after a user has changed their username after it was\nrejected.", - "isDeprecated": false, - "deprecationReason": null - } - ], - "possibleTypes": null - }, - { - "kind": "OBJECT", - "name": "UsernameStatusHistory", - "description": "", - "fields": [ - { - "name": "status", - "description": "", - "args": [], - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "ENUM", - "name": "USER_STATUS_USERNAME", - "ofType": null - } - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "assigned_by", - "description": "", - "args": [], - "type": { - "kind": "OBJECT", - "name": "User", - "ofType": null - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "created_at", - "description": "", - "args": [], - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "SCALAR", - "name": "Date", - "ofType": null - } - }, - "isDeprecated": false, - "deprecationReason": null - } - ], - "inputFields": null, - "interfaces": [], - "enumValues": null, - "possibleTypes": null - }, - { - "kind": "OBJECT", - "name": "BannedStatus", - "description": "", - "fields": [ - { - "name": "status", - "description": "", - "args": [], - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "SCALAR", - "name": "Boolean", - "ofType": null - } - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "history", - "description": "", - "args": [], - "type": { - "kind": "LIST", - "name": null, - "ofType": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "OBJECT", - "name": "BannedStatusHistory", - "ofType": null - } - } - }, - "isDeprecated": false, - "deprecationReason": null - } - ], - "inputFields": null, - "interfaces": [], - "enumValues": null, - "possibleTypes": null - }, - { - "kind": "OBJECT", - "name": "BannedStatusHistory", - "description": "", - "fields": [ - { - "name": "status", - "description": "", - "args": [], - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "SCALAR", - "name": "Boolean", - "ofType": null - } - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "assigned_by", - "description": "", - "args": [], - "type": { - "kind": "OBJECT", - "name": "User", - "ofType": null - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "created_at", - "description": "", - "args": [], - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "SCALAR", - "name": "Date", - "ofType": null - } - }, - "isDeprecated": false, - "deprecationReason": null - } - ], - "inputFields": null, - "interfaces": [], - "enumValues": null, - "possibleTypes": null - }, - { - "kind": "OBJECT", - "name": "SuspensionStatus", - "description": "", - "fields": [ - { - "name": "until", - "description": "", - "args": [], - "type": { - "kind": "SCALAR", - "name": "Date", - "ofType": null - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "history", - "description": "", - "args": [], - "type": { - "kind": "LIST", - "name": null, - "ofType": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "OBJECT", - "name": "SuspensionStatusHistory", - "ofType": null - } - } - }, - "isDeprecated": false, - "deprecationReason": null - } - ], - "inputFields": null, - "interfaces": [], - "enumValues": null, - "possibleTypes": null - }, - { - "kind": "OBJECT", - "name": "SuspensionStatusHistory", - "description": "", - "fields": [ - { - "name": "until", - "description": "", - "args": [], - "type": { - "kind": "SCALAR", - "name": "Date", - "ofType": null - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "assigned_by", - "description": "", - "args": [], - "type": { - "kind": "OBJECT", - "name": "User", - "ofType": null - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "created_at", - "description": "", - "args": [], - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "SCALAR", - "name": "Date", - "ofType": null - } - }, - "isDeprecated": false, - "deprecationReason": null - } - ], - "inputFields": null, - "interfaces": [], - "enumValues": null, - "possibleTypes": null - }, - { - "kind": "OBJECT", - "name": "NotificationSettings", - "description": "", - "fields": [ - { - "name": "digestFrequency", - "description": "", - "args": [], - "type": { - "kind": "ENUM", - "name": "DIGEST_FREQUENCY", - "ofType": null - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "onReply", - "description": "", - "args": [], - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "SCALAR", - "name": "Boolean", - "ofType": null - } - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "onFeatured", - "description": "", - "args": [], - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "SCALAR", - "name": "Boolean", - "ofType": null - } - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "onStaffReply", - "description": "", - "args": [], - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "SCALAR", - "name": "Boolean", - "ofType": null - } - }, - "isDeprecated": false, - "deprecationReason": null - } - ], - "inputFields": null, - "interfaces": [], - "enumValues": null, - "possibleTypes": null - }, - { - "kind": "ENUM", - "name": "DIGEST_FREQUENCY", - "description": "", - "fields": null, - "inputFields": null, - "interfaces": null, - "enumValues": [ - { - "name": "NONE", - "description": "NONE will have the notifications send immediatly rather than bundling\nfor digesting.", - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "HOURLY", - "description": "HOURLY will queue up the notifications and send them hourly.", - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "DAILY", - "description": "DAILY will queue up the notifications and send them daily.", - "isDeprecated": false, - "deprecationReason": null - } - ], - "possibleTypes": null - }, - { - "kind": "INPUT_OBJECT", - "name": "RepliesQuery", - "description": "", - "fields": null, - "inputFields": [ - { - "name": "sortOrder", - "description": "Sort the results by from smallest first.", - "type": { - "kind": "ENUM", - "name": "SORT_ORDER", - "ofType": null - }, - "defaultValue": "ASC" - }, - { - "name": "sortBy", - "description": "The order to sort the comments by, sorting by default the created at\ntimestamp.", - "type": { - "kind": "ENUM", - "name": "SORT_COMMENTS_BY", - "ofType": null - }, - "defaultValue": "CREATED_AT" - }, - { - "name": "limit", - "description": "Limit the number of results to be returned.", - "type": { - "kind": "SCALAR", - "name": "Int", - "ofType": null - }, - "defaultValue": "3" - }, - { - "name": "excludeIgnored", - "description": "Exclude comments ignored by the requesting user", - "type": { - "kind": "SCALAR", - "name": "Boolean", - "ofType": null - }, - "defaultValue": null - } - ], - "interfaces": null, - "enumValues": null, - "possibleTypes": null - }, - { - "kind": "OBJECT", - "name": "Asset", - "description": "Where comments are made on.", - "fields": [ - { - "name": "id", - "description": "The current ID of the asset.", - "args": [], - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "SCALAR", - "name": "ID", - "ofType": null - } - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "title", - "description": "The scraped title of the asset.", - "args": [], - "type": { - "kind": "SCALAR", - "name": "String", - "ofType": null - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "url", - "description": "The URL that the asset is located on.", - "args": [], - "type": { - "kind": "SCALAR", - "name": "String", - "ofType": null - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "comments", - "description": "The comments that are attached to the asset. When `deep` is true, the\ncomments returned will be at all depths.", - "args": [ - { - "name": "query", - "description": "", - "type": { - "kind": "INPUT_OBJECT", - "name": "CommentsQuery", - "ofType": null - }, - "defaultValue": "{statuses: [NONE, ACCEPTED], limit: 10, sortOrder: DESC, sortBy: CREATED_AT}" - }, - { - "name": "deep", - "description": "", - "type": { - "kind": "SCALAR", - "name": "Boolean", - "ofType": null - }, - "defaultValue": "false" - } - ], - "type": { - "kind": "OBJECT", - "name": "CommentConnection", - "ofType": null - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "comment", - "description": "A Comment from the Asset by comment's ID", - "args": [ - { - "name": "id", - "description": "", - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "SCALAR", - "name": "ID", - "ofType": null - } - }, - "defaultValue": null - } - ], - "type": { - "kind": "OBJECT", - "name": "Comment", - "ofType": null - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "commentCount", - "description": "The count of top level comments on the asset.", - "args": [ - { - "name": "tags", - "description": "", - "type": { - "kind": "LIST", - "name": null, - "ofType": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "SCALAR", - "name": "String", - "ofType": null - } - } - }, - "defaultValue": null - } - ], - "type": { - "kind": "SCALAR", - "name": "Int", - "ofType": null - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "totalCommentCount", - "description": "The total count of all comments made on the asset.", - "args": [ - { - "name": "tags", - "description": "", - "type": { - "kind": "LIST", - "name": null, - "ofType": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "SCALAR", - "name": "String", - "ofType": null - } - } - }, - "defaultValue": null - } - ], - "type": { - "kind": "SCALAR", - "name": "Int", - "ofType": null - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "settings", - "description": "The settings (rectified with the global settings) that should be applied to\nthis asset.", - "args": [], - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "OBJECT", - "name": "Settings", - "ofType": null - } - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "closedAt", - "description": "The date that the asset was closed at.", - "args": [], - "type": { - "kind": "SCALAR", - "name": "Date", - "ofType": null - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "isClosed", - "description": "True if asset is closed.", - "args": [], - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "SCALAR", - "name": "Boolean", - "ofType": null - } - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "action_summaries", - "description": "Summary of all Actions against all entities associated with the Asset.\n(likes, flags, etc.). Requires the `ADMIN` role.", - "args": [], - "type": { - "kind": "LIST", - "name": null, - "ofType": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "INTERFACE", - "name": "AssetActionSummary", - "ofType": null - } - } - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "created_at", - "description": "The date that the asset was created.", - "args": [], - "type": { - "kind": "SCALAR", - "name": "Date", - "ofType": null - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "tags", - "description": "The tags on the asset", - "args": [], - "type": { - "kind": "LIST", - "name": null, - "ofType": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "OBJECT", - "name": "TagLink", - "ofType": null - } - } - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "author", - "description": "The author(s) of the asset.", - "args": [], - "type": { - "kind": "SCALAR", - "name": "String", - "ofType": null - }, - "isDeprecated": false, - "deprecationReason": null - } - ], - "inputFields": null, - "interfaces": [], - "enumValues": null, - "possibleTypes": null - }, - { - "kind": "INTERFACE", - "name": "AssetActionSummary", - "description": "A summary of actions for a specific action type on an Asset.", - "fields": [ - { - "name": "actionCount", - "description": "Number of actions associated with actionable types on this this Asset.", - "args": [], - "type": { - "kind": "SCALAR", - "name": "Int", - "ofType": null - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "actionableItemCount", - "description": "Number of unique actionable types that are referenced by the actions.", - "args": [], - "type": { - "kind": "SCALAR", - "name": "Int", - "ofType": null - }, - "isDeprecated": false, - "deprecationReason": null - } - ], - "inputFields": null, - "interfaces": null, - "enumValues": null, - "possibleTypes": [ - { - "kind": "OBJECT", - "name": "DefaultAssetActionSummary", - "ofType": null - }, - { - "kind": "OBJECT", - "name": "FlagAssetActionSummary", - "ofType": null - }, - { - "kind": "OBJECT", - "name": "RespectAssetActionSummary", - "ofType": null - } - ] - }, - { - "kind": "OBJECT", - "name": "CommentStatusHistory", - "description": "", - "fields": [ - { - "name": "type", - "description": "", - "args": [], - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "ENUM", - "name": "COMMENT_STATUS", - "ofType": null - } - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "created_at", - "description": "", - "args": [], - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "SCALAR", - "name": "Date", - "ofType": null - } - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "assigned_by", - "description": "", - "args": [], - "type": { - "kind": "OBJECT", - "name": "User", - "ofType": null - }, - "isDeprecated": false, - "deprecationReason": null - } - ], - "inputFields": null, - "interfaces": [], - "enumValues": null, - "possibleTypes": null - }, - { - "kind": "OBJECT", - "name": "EditInfo", - "description": "", - "fields": [ - { - "name": "edited", - "description": "", - "args": [], - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "SCALAR", - "name": "Boolean", - "ofType": null - } - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "editableUntil", - "description": "", - "args": [], - "type": { - "kind": "SCALAR", - "name": "Date", - "ofType": null - }, - "isDeprecated": false, - "deprecationReason": null - } - ], - "inputFields": null, - "interfaces": [], - "enumValues": null, - "possibleTypes": null - }, - { - "kind": "SCALAR", - "name": "Float", - "description": "The `Float` scalar type represents signed double-precision fractional values as specified by [IEEE 754](http://en.wikipedia.org/wiki/IEEE_floating_point). ", - "fields": null, - "inputFields": null, - "interfaces": null, - "enumValues": null, - "possibleTypes": null - }, - { - "kind": "INPUT_OBJECT", - "name": "AssetsQuery", - "description": "AssetsQuery allows teh ability to query assets by specific fields", - "fields": null, - "inputFields": [ - { - "name": "value", - "description": "a search string to match against titles, authors, urls, etc.", - "type": { - "kind": "SCALAR", - "name": "String", - "ofType": null - }, - "defaultValue": "\"\"" - }, - { - "name": "limit", - "description": "Limit the number of results to be returned", - "type": { - "kind": "SCALAR", - "name": "Int", - "ofType": null - }, - "defaultValue": "10" - }, - { - "name": "open", - "description": "open filters assets that are open/closed/all. Not providing this parameter\nwill return all the assets, true will return assets that are open, and false\nwill return assets that are closed.", - "type": { - "kind": "SCALAR", - "name": "Boolean", - "ofType": null - }, - "defaultValue": null - }, - { - "name": "sortOrder", - "description": "sortOrder specifies the order of the sort for the returned Assets.", - "type": { - "kind": "ENUM", - "name": "SORT_ORDER", - "ofType": null - }, - "defaultValue": "DESC" - }, - { - "name": "cursor", - "description": "Skip results from the last created_at timestamp.", - "type": { - "kind": "SCALAR", - "name": "Cursor", - "ofType": null - }, - "defaultValue": null - } - ], - "interfaces": null, - "enumValues": null, - "possibleTypes": null - }, - { - "kind": "OBJECT", - "name": "AssetConnection", - "description": "AssetConnection represents a paginable subset of a asset list.", - "fields": [ - { - "name": "hasNextPage", - "description": "Indicates that there are more assets after this subset.", - "args": [], - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "SCALAR", - "name": "Boolean", - "ofType": null - } - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "startCursor", - "description": "Cursor of first asset in subset.", - "args": [], - "type": { - "kind": "SCALAR", - "name": "Cursor", - "ofType": null - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "endCursor", - "description": "Cursor of last asset in subset.", - "args": [], - "type": { - "kind": "SCALAR", - "name": "Cursor", - "ofType": null - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "nodes", - "description": "Subset of assets.", - "args": [], - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "LIST", - "name": null, - "ofType": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "OBJECT", - "name": "Asset", - "ofType": null - } - } - } - }, - "isDeprecated": false, - "deprecationReason": null - } - ], - "inputFields": null, - "interfaces": [], - "enumValues": null, - "possibleTypes": null - }, - { - "kind": "INPUT_OBJECT", - "name": "CommentCountQuery", - "description": "CommentCountQuery allows the ability to query comment counts by specific\nmethods.", - "fields": null, - "inputFields": [ - { - "name": "statuses", - "description": "Current status of a comment.\nThis field is restricted.", - "type": { - "kind": "LIST", - "name": null, - "ofType": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "ENUM", - "name": "COMMENT_STATUS", - "ofType": null - } - } - }, - "defaultValue": "[NONE, ACCEPTED]" - }, - { - "name": "asset_id", - "description": "Asset that a comment is on.", - "type": { - "kind": "SCALAR", - "name": "ID", - "ofType": null - }, - "defaultValue": null - }, - { - "name": "asset_url", - "description": "The URL that the asset is located on.", - "type": { - "kind": "SCALAR", - "name": "String", - "ofType": null - }, - "defaultValue": null - }, - { - "name": "parent_id", - "description": "The parent of the comment that we want to retrieve.", - "type": { - "kind": "SCALAR", - "name": "ID", - "ofType": null - }, - "defaultValue": null - }, - { - "name": "action_type", - "description": "comments returned will only be ones which have at least one action of this\ntype.", - "type": { - "kind": "ENUM", - "name": "ACTION_TYPE", - "ofType": null - }, - "defaultValue": null - }, - { - "name": "author_id", - "description": "author_id allows the querying of comment counts based on the author of the\ncomments.", - "type": { - "kind": "SCALAR", - "name": "ID", - "ofType": null - }, - "defaultValue": null - }, - { - "name": "tags", - "description": "Filter by a specific tag name.", - "type": { - "kind": "LIST", - "name": null, - "ofType": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "SCALAR", - "name": "String", - "ofType": null - } - } - }, - "defaultValue": null - } - ], - "interfaces": null, - "enumValues": null, - "possibleTypes": null - }, - { - "kind": "INPUT_OBJECT", - "name": "UserCountQuery", - "description": "UserCountQuery allows the ability to query user counts by specific\nmethods.", - "fields": null, - "inputFields": [ - { - "name": "action_type", - "description": "comments returned will only be ones which have at least one action of this\ntype.", - "type": { - "kind": "ENUM", - "name": "ACTION_TYPE", - "ofType": null - }, - "defaultValue": null - }, - { - "name": "state", - "description": "state queries for a specific subset of users with the given state query.", - "type": { - "kind": "INPUT_OBJECT", - "name": "UserStateInput", - "ofType": null - }, - "defaultValue": null - } - ], - "interfaces": null, - "enumValues": null, - "possibleTypes": null - }, - { - "kind": "INPUT_OBJECT", - "name": "UserStateInput", - "description": "", - "fields": null, - "inputFields": [ - { - "name": "status", - "description": "", - "type": { - "kind": "INPUT_OBJECT", - "name": "UserStatusInput", - "ofType": null - }, - "defaultValue": null - } - ], - "interfaces": null, - "enumValues": null, - "possibleTypes": null - }, - { - "kind": "INPUT_OBJECT", - "name": "UserStatusInput", - "description": "UserStatusInput describes the queryable components of the UserStatus.", - "fields": null, - "inputFields": [ - { - "name": "username", - "description": "username will restrict the returned users to only those with the given\nusername status's. If not provided, no filtering will be performed.", - "type": { - "kind": "LIST", - "name": null, - "ofType": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "ENUM", - "name": "USER_STATUS_USERNAME", - "ofType": null - } - } - }, - "defaultValue": null - }, - { - "name": "banned", - "description": "banned will restrict the returned users to only those that are, or are not\nbanned. If not provided, no filtering will be performed.", - "type": { - "kind": "SCALAR", - "name": "Boolean", - "ofType": null - }, - "defaultValue": null - }, - { - "name": "suspended", - "description": "suspended will restrict the returned users to only those that are, or are not\nsuspended. If not provided, no filtering will be performed.", - "type": { - "kind": "SCALAR", - "name": "Boolean", - "ofType": null - }, - "defaultValue": null - } - ], - "interfaces": null, - "enumValues": null, - "possibleTypes": null - }, - { - "kind": "INPUT_OBJECT", - "name": "UsersQuery", - "description": "UsersQuery allows the ability to query users by a specific fields.", - "fields": null, - "inputFields": [ - { - "name": "action_type", - "description": "Users returned will only be ones which have at least one action of this.", - "type": { - "kind": "ENUM", - "name": "ACTION_TYPE", - "ofType": null - }, - "defaultValue": null - }, - { - "name": "state", - "description": "state will filter the users to a specific set of users that meet.", - "type": { - "kind": "INPUT_OBJECT", - "name": "UserStateInput", - "ofType": null - }, - "defaultValue": null - }, - { - "name": "value", - "description": "value is the search string to use to search for a pa", - "type": { - "kind": "SCALAR", - "name": "String", - "ofType": null - }, - "defaultValue": "\"\"" - }, - { - "name": "limit", - "description": "Limit the number of results to be returned.", - "type": { - "kind": "SCALAR", - "name": "Int", - "ofType": null - }, - "defaultValue": "10" - }, - { - "name": "cursor", - "description": "Skip results from the last created_at timestamp.", - "type": { - "kind": "SCALAR", - "name": "Cursor", - "ofType": null - }, - "defaultValue": null - }, - { - "name": "sortOrder", - "description": "Sort the results by created_at.", - "type": { - "kind": "ENUM", - "name": "SORT_ORDER", - "ofType": null - }, - "defaultValue": "DESC" - } - ], - "interfaces": null, - "enumValues": null, - "possibleTypes": null - }, - { - "kind": "OBJECT", - "name": "UserConnection", - "description": "UserConnection represents a paginable subset of a user list.", - "fields": [ - { - "name": "hasNextPage", - "description": "Indicates that there are more users after this subset.", - "args": [], - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "SCALAR", - "name": "Boolean", - "ofType": null - } - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "startCursor", - "description": "Cursor of first user in subset.", - "args": [], - "type": { - "kind": "SCALAR", - "name": "Cursor", - "ofType": null - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "endCursor", - "description": "Cursor of last user in subset.", - "args": [], - "type": { - "kind": "SCALAR", - "name": "Cursor", - "ofType": null - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "nodes", - "description": "Subset of users.", - "args": [], - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "LIST", - "name": null, - "ofType": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "OBJECT", - "name": "User", - "ofType": null - } - } - } - }, - "isDeprecated": false, - "deprecationReason": null - } - ], - "inputFields": null, - "interfaces": [], - "enumValues": null, - "possibleTypes": null - }, - { - "kind": "OBJECT", - "name": "RootMutation", - "description": "", - "fields": [ - { - "name": "createComment", - "description": "Creates a comment on the asset.", - "args": [ - { - "name": "input", - "description": "", - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "INPUT_OBJECT", - "name": "CreateCommentInput", - "ofType": null - } - }, - "defaultValue": null - } - ], - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "OBJECT", - "name": "CreateCommentResponse", - "ofType": null - } - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "createFlag", - "description": "Creates a flag on an entity.", - "args": [ - { - "name": "flag", - "description": "", - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "INPUT_OBJECT", - "name": "CreateFlagInput", - "ofType": null - } - }, - "defaultValue": null - } - ], - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "OBJECT", - "name": "CreateFlagResponse", - "ofType": null - } - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "createDontAgree", - "description": "Creates a don't agree action on an entity.", - "args": [ - { - "name": "dontagree", - "description": "", - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "INPUT_OBJECT", - "name": "CreateDontAgreeInput", - "ofType": null - } - }, - "defaultValue": null - } - ], - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "OBJECT", - "name": "CreateDontAgreeResponse", - "ofType": null - } - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "deleteAction", - "description": "Delete an action based on the action id.", - "args": [ - { - "name": "id", - "description": "", - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "SCALAR", - "name": "ID", - "ofType": null - } - }, - "defaultValue": null - } - ], - "type": { - "kind": "OBJECT", - "name": "DeleteActionResponse", - "ofType": null - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "editComment", - "description": "Edit a comment", - "args": [ - { - "name": "id", - "description": "", - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "SCALAR", - "name": "ID", - "ofType": null - } - }, - "defaultValue": null - }, - { - "name": "asset_id", - "description": "", - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "SCALAR", - "name": "ID", - "ofType": null - } - }, - "defaultValue": null - }, - { - "name": "edit", - "description": "", - "type": { - "kind": "INPUT_OBJECT", - "name": "EditCommentInput", - "ofType": null - }, - "defaultValue": null - } - ], - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "OBJECT", - "name": "EditCommentResponse", - "ofType": null - } - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "suspendUser", - "description": "Sets the suspension status on a given user. Requires the `MODERATOR` role.\nMutation is restricted.", - "args": [ - { - "name": "input", - "description": "", - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "INPUT_OBJECT", - "name": "SuspendUserInput", - "ofType": null - } - }, - "defaultValue": null - } - ], - "type": { - "kind": "OBJECT", - "name": "SuspendUserResponse", - "ofType": null - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "unsuspendUser", - "description": "Sets the suspension status on a given user. Requires the `MODERATOR` role.\nMutation is restricted.", - "args": [ - { - "name": "input", - "description": "", - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "INPUT_OBJECT", - "name": "UnsuspendUserInput", - "ofType": null - } - }, - "defaultValue": null - } - ], - "type": { - "kind": "OBJECT", - "name": "UnsuspendUserResponse", - "ofType": null - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "banUser", - "description": "Sets the ban status on a given user. Requires the `MODERATOR` role.\nMutation is restricted.", - "args": [ - { - "name": "input", - "description": "", - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "INPUT_OBJECT", - "name": "BanUserInput", - "ofType": null - } - }, - "defaultValue": null - } - ], - "type": { - "kind": "OBJECT", - "name": "BanUsersResponse", - "ofType": null - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "unbanUser", - "description": "Sets the ban status on a given user. Requires the `MODERATOR` role.\nMutation is restricted.", - "args": [ - { - "name": "input", - "description": "", - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "INPUT_OBJECT", - "name": "UnbanUserInput", - "ofType": null - } - }, - "defaultValue": null - } - ], - "type": { - "kind": "OBJECT", - "name": "UnbanUserResponse", - "ofType": null - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "approveUsername", - "description": "Sets the username status on a given user to `APPROVED`. Requires the\n`MODERATOR` role. Mutation is restricted.", - "args": [ - { - "name": "id", - "description": "", - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "SCALAR", - "name": "ID", - "ofType": null - } - }, - "defaultValue": null - } - ], - "type": { - "kind": "OBJECT", - "name": "SetUsernameStatusResponse", - "ofType": null - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "rejectUsername", - "description": "Sets the username status on a given user to `REJECTED`. Requires the\n`MODERATOR` role. Mutation is restricted.", - "args": [ - { - "name": "id", - "description": "", - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "SCALAR", - "name": "ID", - "ofType": null - } - }, - "defaultValue": null - } - ], - "type": { - "kind": "OBJECT", - "name": "SetUsernameStatusResponse", - "ofType": null - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "changeUsername", - "description": "Changes the username to the desired username. Mutation is restricted to\nthose users with permission do to so.", - "args": [ - { - "name": "id", - "description": "", - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "SCALAR", - "name": "ID", - "ofType": null - } - }, - "defaultValue": null - }, - { - "name": "username", - "description": "", - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "SCALAR", - "name": "String", - "ofType": null - } - }, - "defaultValue": null - } - ], - "type": { - "kind": "OBJECT", - "name": "ChangeUsernameResponse", - "ofType": null - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "setUsername", - "description": "Sets the username to the desired username if the user has not had a chance\nto set their username. Mutation is restricted to those users with permission\ndo to so that have not done so before.", - "args": [ - { - "name": "id", - "description": "", - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "SCALAR", - "name": "ID", - "ofType": null - } - }, - "defaultValue": null - }, - { - "name": "username", - "description": "", - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "SCALAR", - "name": "String", - "ofType": null - } - }, - "defaultValue": null - } - ], - "type": { - "kind": "OBJECT", - "name": "SetUsernameResponse", - "ofType": null - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "setCommentStatus", - "description": "Sets Comment status. Requires the `ADMIN` role.\nMutation is restricted.", - "args": [ - { - "name": "id", - "description": "", - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "SCALAR", - "name": "ID", - "ofType": null - } - }, - "defaultValue": null - }, - { - "name": "status", - "description": "", - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "ENUM", - "name": "COMMENT_STATUS", - "ofType": null - } - }, - "defaultValue": null - } - ], - "type": { - "kind": "OBJECT", - "name": "SetCommentStatusResponse", - "ofType": null - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "addTag", - "description": "Add a tag.", - "args": [ - { - "name": "tag", - "description": "", - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "INPUT_OBJECT", - "name": "ModifyTagInput", - "ofType": null - } - }, - "defaultValue": null - } - ], - "type": { - "kind": "OBJECT", - "name": "ModifyTagResponse", - "ofType": null - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "removeTag", - "description": "Removes a tag.", - "args": [ - { - "name": "tag", - "description": "", - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "INPUT_OBJECT", - "name": "ModifyTagInput", - "ofType": null - } - }, - "defaultValue": null - } - ], - "type": { - "kind": "OBJECT", - "name": "ModifyTagResponse", - "ofType": null - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "setUserRole", - "description": "Set's a given users role to the one provided. If `null` is passed, the user\nwill not have any role.", - "args": [ - { - "name": "id", - "description": "", - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "SCALAR", - "name": "ID", - "ofType": null - } - }, - "defaultValue": null - }, - { - "name": "role", - "description": "", - "type": { - "kind": "ENUM", - "name": "USER_ROLES", - "ofType": null - }, - "defaultValue": null - } - ], - "type": { - "kind": "OBJECT", - "name": "SetUserRoleResponse", - "ofType": null - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "updateAssetSettings", - "description": "Updates settings on a given asset.\nMutation is restricted.", - "args": [ - { - "name": "id", - "description": "", - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "SCALAR", - "name": "ID", - "ofType": null - } - }, - "defaultValue": null - }, - { - "name": "input", - "description": "", - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "INPUT_OBJECT", - "name": "AssetSettingsInput", - "ofType": null - } - }, - "defaultValue": null - } - ], - "type": { - "kind": "OBJECT", - "name": "UpdateAssetSettingsResponse", - "ofType": null - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "updateAssetStatus", - "description": "Updates the status of an asset allowing you to close/reopen an asset for\ncommenting.\nMutation is restricted.", - "args": [ - { - "name": "id", - "description": "", - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "SCALAR", - "name": "ID", - "ofType": null - } - }, - "defaultValue": null - }, - { - "name": "input", - "description": "", - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "INPUT_OBJECT", - "name": "UpdateAssetStatusInput", - "ofType": null - } - }, - "defaultValue": null - } - ], - "type": { - "kind": "OBJECT", - "name": "UpdateAssetStatusResponse", - "ofType": null - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "closeAsset", - "description": "closeAsset will close the asset for commenting based on server time.", - "args": [ - { - "name": "id", - "description": "", - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "SCALAR", - "name": "ID", - "ofType": null - } - }, - "defaultValue": null - } - ], - "type": { - "kind": "OBJECT", - "name": "CloseAssetResponse", - "ofType": null - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "updateSettings", - "description": "updateSettings will update the global settings.\nMutation is restricted.", - "args": [ - { - "name": "input", - "description": "", - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "INPUT_OBJECT", - "name": "UpdateSettingsInput", - "ofType": null - } - }, - "defaultValue": null - } - ], - "type": { - "kind": "OBJECT", - "name": "UpdateSettingsResponse", - "ofType": null - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "ignoreUser", - "description": "Ignore comments by another user", - "args": [ - { - "name": "id", - "description": "", - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "SCALAR", - "name": "ID", - "ofType": null - } - }, - "defaultValue": null - } - ], - "type": { - "kind": "OBJECT", - "name": "IgnoreUserResponse", - "ofType": null - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "createToken", - "description": "CreateToken will create a token that is attached to the current user.\nMutation is restricted.", - "args": [ - { - "name": "input", - "description": "", - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "INPUT_OBJECT", - "name": "CreateTokenInput", - "ofType": null - } - }, - "defaultValue": null - } - ], - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "OBJECT", - "name": "CreateTokenResponse", - "ofType": null - } - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "revokeToken", - "description": "RevokeToken will revoke an existing token.\nMutation is restricted.", - "args": [ - { - "name": "input", - "description": "", - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "INPUT_OBJECT", - "name": "RevokeTokenInput", - "ofType": null - } - }, - "defaultValue": null - } - ], - "type": { - "kind": "OBJECT", - "name": "RevokeTokenResponse", - "ofType": null - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "stopIgnoringUser", - "description": "Stop Ignoring comments by another user.", - "args": [ - { - "name": "id", - "description": "", - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "SCALAR", - "name": "ID", - "ofType": null - } - }, - "defaultValue": null - } - ], - "type": { - "kind": "OBJECT", - "name": "StopIgnoringUserResponse", - "ofType": null - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "forceScrapeAsset", - "description": "forceScrapeAsset will force scrape the Asset with the given ID.", - "args": [ - { - "name": "id", - "description": "", - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "SCALAR", - "name": "ID", - "ofType": null - } - }, - "defaultValue": null - } - ], - "type": { - "kind": "OBJECT", - "name": "ForceScrapeAssetResponse", - "ofType": null - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "delUser", - "description": "delUser will delete the user with the specified id.", - "args": [ - { - "name": "id", - "description": "", - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "SCALAR", - "name": "ID", - "ofType": null - } - }, - "defaultValue": null - } - ], - "type": { - "kind": "OBJECT", - "name": "DelUserResponse", - "ofType": null - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "createRespectAction", - "description": "Creates a respect on an entity.", - "args": [ - { - "name": "input", - "description": "", - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "INPUT_OBJECT", - "name": "CreateRespectActionInput", - "ofType": null - } - }, - "defaultValue": null - } - ], - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "OBJECT", - "name": "CreateRespectActionResponse", - "ofType": null - } - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "deleteRespectAction", - "description": "", - "args": [ - { - "name": "input", - "description": "", - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "INPUT_OBJECT", - "name": "DeleteRespectActionInput", - "ofType": null - } - }, - "defaultValue": null - } - ], - "type": { - "kind": "OBJECT", - "name": "DeleteRespectActionResponse", - "ofType": null - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "updateNotificationSettings", - "description": "updateNotificationSettings will update the current user's notification\nsettings.", - "args": [ - { - "name": "input", - "description": "", - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "INPUT_OBJECT", - "name": "NotificationSettingsInput", - "ofType": null - } - }, - "defaultValue": null - } - ], - "type": { - "kind": "OBJECT", - "name": "UpdateNotificationSettingsResponse", - "ofType": null - }, - "isDeprecated": false, - "deprecationReason": null - } - ], - "inputFields": null, - "interfaces": [], - "enumValues": null, - "possibleTypes": null - }, - { - "kind": "INPUT_OBJECT", - "name": "CreateCommentInput", - "description": "", - "fields": null, - "inputFields": [ - { - "name": "asset_id", - "description": "The asset id", - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "SCALAR", - "name": "ID", - "ofType": null - } - }, - "defaultValue": null - }, - { - "name": "parent_id", - "description": "The id of the parent comment", - "type": { - "kind": "SCALAR", - "name": "ID", - "ofType": null - }, - "defaultValue": null - }, - { - "name": "body", - "description": "The body of the comment", - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "SCALAR", - "name": "String", - "ofType": null - } - }, - "defaultValue": null - }, - { - "name": "tags", - "description": "Tags", - "type": { - "kind": "LIST", - "name": null, - "ofType": { - "kind": "SCALAR", - "name": "String", - "ofType": null - } - }, - "defaultValue": null - }, - { - "name": "richTextBody", - "description": "", - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "SCALAR", - "name": "String", - "ofType": null - } - }, - "defaultValue": null - }, - { - "name": "checkToxicity", - "description": "If true, the mutation will fail when the\nbody contains toxic language.", - "type": { - "kind": "SCALAR", - "name": "Boolean", - "ofType": null - }, - "defaultValue": null - } - ], - "interfaces": null, - "enumValues": null, - "possibleTypes": null - }, - { - "kind": "OBJECT", - "name": "CreateCommentResponse", - "description": "CreateCommentResponse is returned with the comment that was created and any\nerrors that may have occurred in the attempt to create it.", - "fields": [ - { - "name": "comment", - "description": "The comment that was created.", - "args": [], - "type": { - "kind": "OBJECT", - "name": "Comment", - "ofType": null - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "actions", - "description": "Actions that was assigned during creation of the comment.", - "args": [], - "type": { - "kind": "LIST", - "name": null, - "ofType": { - "kind": "INTERFACE", - "name": "Action", - "ofType": null - } - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "errors", - "description": "An array of errors relating to the mutation that occurred.", - "args": [], - "type": { - "kind": "LIST", - "name": null, - "ofType": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "INTERFACE", - "name": "UserError", - "ofType": null - } - } - }, - "isDeprecated": false, - "deprecationReason": null - } - ], - "inputFields": null, - "interfaces": [ - { - "kind": "INTERFACE", - "name": "Response", - "ofType": null - } - ], - "enumValues": null, - "possibleTypes": null - }, - { - "kind": "INTERFACE", - "name": "Response", - "description": "Response defines what can be expected from any response to a mutation action.", - "fields": [ - { - "name": "errors", - "description": "An array of errors relating to the mutation that occurred.", - "args": [], - "type": { - "kind": "LIST", - "name": null, - "ofType": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "INTERFACE", - "name": "UserError", - "ofType": null - } - } - }, - "isDeprecated": false, - "deprecationReason": null - } - ], - "inputFields": null, - "interfaces": null, - "enumValues": null, - "possibleTypes": [ - { - "kind": "OBJECT", - "name": "CreateCommentResponse", - "ofType": null - }, - { - "kind": "OBJECT", - "name": "CreateFlagResponse", - "ofType": null - }, - { - "kind": "OBJECT", - "name": "CreateDontAgreeResponse", - "ofType": null - }, - { - "kind": "OBJECT", - "name": "DeleteActionResponse", - "ofType": null - }, - { - "kind": "OBJECT", - "name": "EditCommentResponse", - "ofType": null - }, - { - "kind": "OBJECT", - "name": "SuspendUserResponse", - "ofType": null - }, - { - "kind": "OBJECT", - "name": "UnsuspendUserResponse", - "ofType": null - }, - { - "kind": "OBJECT", - "name": "BanUsersResponse", - "ofType": null - }, - { - "kind": "OBJECT", - "name": "UnbanUserResponse", - "ofType": null - }, - { - "kind": "OBJECT", - "name": "SetUsernameStatusResponse", - "ofType": null - }, - { - "kind": "OBJECT", - "name": "ChangeUsernameResponse", - "ofType": null - }, - { - "kind": "OBJECT", - "name": "SetUsernameResponse", - "ofType": null - }, - { - "kind": "OBJECT", - "name": "SetCommentStatusResponse", - "ofType": null - }, - { - "kind": "OBJECT", - "name": "ModifyTagResponse", - "ofType": null - }, - { - "kind": "OBJECT", - "name": "SetUserRoleResponse", - "ofType": null - }, - { - "kind": "OBJECT", - "name": "UpdateAssetSettingsResponse", - "ofType": null - }, - { - "kind": "OBJECT", - "name": "UpdateAssetStatusResponse", - "ofType": null - }, - { - "kind": "OBJECT", - "name": "CloseAssetResponse", - "ofType": null - }, - { - "kind": "OBJECT", - "name": "UpdateSettingsResponse", - "ofType": null - }, - { - "kind": "OBJECT", - "name": "IgnoreUserResponse", - "ofType": null - }, - { - "kind": "OBJECT", - "name": "CreateTokenResponse", - "ofType": null - }, - { - "kind": "OBJECT", - "name": "RevokeTokenResponse", - "ofType": null - }, - { - "kind": "OBJECT", - "name": "StopIgnoringUserResponse", - "ofType": null - }, - { - "kind": "OBJECT", - "name": "ForceScrapeAssetResponse", - "ofType": null - }, - { - "kind": "OBJECT", - "name": "DelUserResponse", - "ofType": null - }, - { - "kind": "OBJECT", - "name": "CreateRespectActionResponse", - "ofType": null - }, - { - "kind": "OBJECT", - "name": "DeleteRespectActionResponse", - "ofType": null - }, - { - "kind": "OBJECT", - "name": "UpdateNotificationSettingsResponse", - "ofType": null - } - ] - }, - { - "kind": "INTERFACE", - "name": "UserError", - "description": "Any error rendered due to the user's input.", - "fields": [ - { - "name": "translation_key", - "description": "Translation key relating to a translatable string containing details to be\ndisplayed to the end user.", - "args": [], - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "SCALAR", - "name": "String", - "ofType": null - } - }, - "isDeprecated": false, - "deprecationReason": null - } - ], - "inputFields": null, - "interfaces": null, - "enumValues": null, - "possibleTypes": [ - { - "kind": "OBJECT", - "name": "GenericUserError", - "ofType": null - }, - { - "kind": "OBJECT", - "name": "ValidationUserError", - "ofType": null - } - ] - }, - { - "kind": "INPUT_OBJECT", - "name": "CreateFlagInput", - "description": "", - "fields": null, - "inputFields": [ - { - "name": "item_id", - "description": "The item's id for which we are to create a flag.", - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "SCALAR", - "name": "ID", - "ofType": null - } - }, - "defaultValue": null - }, - { - "name": "item_type", - "description": "The type of the item for which we are to create the flag.", - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "ENUM", - "name": "ACTION_ITEM_TYPE", - "ofType": null - } - }, - "defaultValue": null - }, - { - "name": "reason", - "description": "The reason for flagging the item.", - "type": { - "kind": "ENUM", - "name": "FLAG_REASON", - "ofType": null - }, - "defaultValue": null - }, - { - "name": "message", - "description": "An optional message sent with the flagging action by the user.", - "type": { - "kind": "SCALAR", - "name": "String", - "ofType": null - }, - "defaultValue": null - } - ], - "interfaces": null, - "enumValues": null, - "possibleTypes": null - }, - { - "kind": "ENUM", - "name": "ACTION_ITEM_TYPE", - "description": "Used to represent the item type for an action.", - "fields": null, - "inputFields": null, - "interfaces": null, - "enumValues": [ - { - "name": "ASSETS", - "description": "The action references a entity of type Asset.", - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "COMMENTS", - "description": "The action references a entity of type Comment.", - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "USERS", - "description": "The action references a entity of type User.", - "isDeprecated": false, - "deprecationReason": null - } - ], - "possibleTypes": null - }, - { - "kind": "ENUM", - "name": "FLAG_REASON", - "description": "", - "fields": null, - "inputFields": null, - "interfaces": null, - "enumValues": [ - { - "name": "USERNAME_OFFENSIVE", - "description": "The current user thinks that the flagged username is offensive.", - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "USERNAME_NOLIKE", - "description": "The current user does not like the flagged username.", - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "USERNAME_IMPERSONATING", - "description": "The current user thinks that the flagged username is being used to\nimpersonate another user.", - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "USERNAME_SPAM", - "description": "The current user thinks that the flagged username is spam.", - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "USERNAME_OTHER", - "description": "The current user thinks that the flagged username is wrong for another\nreason.", - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "COMMENT_OFFENSIVE", - "description": "The current user thinks that the flagged comment is offensive.", - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "COMMENT_SPAM", - "description": "The current user thinks that the flagged comment is spam.", - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "COMMENT_OTHER", - "description": "The current user thinks that the flagged comment is wrong for another\nreason.", - "isDeprecated": false, - "deprecationReason": null - } - ], - "possibleTypes": null - }, - { - "kind": "OBJECT", - "name": "CreateFlagResponse", - "description": "CreateFlagResponse is the response returned with possibly some errors\nrelating to the creating the flag action attempt and possibly the flag that\nwas created.", - "fields": [ - { - "name": "flag", - "description": "The flag that was created.", - "args": [], - "type": { - "kind": "OBJECT", - "name": "FlagAction", - "ofType": null - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "errors", - "description": "An array of errors relating to the mutation that occurred.", - "args": [], - "type": { - "kind": "LIST", - "name": null, - "ofType": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "INTERFACE", - "name": "UserError", - "ofType": null - } - } - }, - "isDeprecated": false, - "deprecationReason": null - } - ], - "inputFields": null, - "interfaces": [ - { - "kind": "INTERFACE", - "name": "Response", - "ofType": null - } - ], - "enumValues": null, - "possibleTypes": null - }, - { - "kind": "OBJECT", - "name": "FlagAction", - "description": "A FLAG action that contains flag metadata.", - "fields": [ - { - "name": "id", - "description": "The ID of the Flag Action.", - "args": [], - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "SCALAR", - "name": "ID", - "ofType": null - } - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "reason", - "description": "The reason for which the Flag Action was created.", - "args": [], - "type": { - "kind": "SCALAR", - "name": "String", - "ofType": null - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "message", - "description": "An optional message sent with the flagging action by the user.", - "args": [], - "type": { - "kind": "SCALAR", - "name": "String", - "ofType": null - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "user", - "description": "The user who created the action.", - "args": [], - "type": { - "kind": "OBJECT", - "name": "User", - "ofType": null - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "updated_at", - "description": "The time when the Flag Action was updated.", - "args": [], - "type": { - "kind": "SCALAR", - "name": "Date", - "ofType": null - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "created_at", - "description": "The time when the Flag Action was created.", - "args": [], - "type": { - "kind": "SCALAR", - "name": "Date", - "ofType": null - }, - "isDeprecated": false, - "deprecationReason": null - } - ], - "inputFields": null, - "interfaces": [ - { - "kind": "INTERFACE", - "name": "Action", - "ofType": null - } - ], - "enumValues": null, - "possibleTypes": null - }, - { - "kind": "INPUT_OBJECT", - "name": "CreateDontAgreeInput", - "description": "", - "fields": null, - "inputFields": [ - { - "name": "item_id", - "description": "The item's id for which we are to create a don't agree.", - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "SCALAR", - "name": "ID", - "ofType": null - } - }, - "defaultValue": null - }, - { - "name": "item_type", - "description": "The type of the item for which we are to create the don't agree.", - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "ENUM", - "name": "ACTION_ITEM_TYPE", - "ofType": null - } - }, - "defaultValue": null - }, - { - "name": "message", - "description": "An optional message sent with the don't agree action by the user.", - "type": { - "kind": "SCALAR", - "name": "String", - "ofType": null - }, - "defaultValue": null - } - ], - "interfaces": null, - "enumValues": null, - "possibleTypes": null - }, - { - "kind": "OBJECT", - "name": "CreateDontAgreeResponse", - "description": "CreateDontAgreeResponse is the response returned with possibly some errors\nrelating to the creating the don't agree action attempt and possibly the don't agree that\nwas created.", - "fields": [ - { - "name": "dontagree", - "description": "The don't agree that was created.", - "args": [], - "type": { - "kind": "OBJECT", - "name": "DontAgreeAction", - "ofType": null - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "errors", - "description": "An array of errors relating to the mutation that occurred.", - "args": [], - "type": { - "kind": "LIST", - "name": null, - "ofType": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "INTERFACE", - "name": "UserError", - "ofType": null - } - } - }, - "isDeprecated": false, - "deprecationReason": null - } - ], - "inputFields": null, - "interfaces": [ - { - "kind": "INTERFACE", - "name": "Response", - "ofType": null - } - ], - "enumValues": null, - "possibleTypes": null - }, - { - "kind": "OBJECT", - "name": "DontAgreeAction", - "description": "A DONTAGREE action that contains do not agree metadata.", - "fields": [ - { - "name": "id", - "description": "The ID of the DontAgree Action.", - "args": [], - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "SCALAR", - "name": "ID", - "ofType": null - } - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "message", - "description": "An optional message sent with the flagging action by the user.", - "args": [], - "type": { - "kind": "SCALAR", - "name": "String", - "ofType": null - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "user", - "description": "The user who created the action.", - "args": [], - "type": { - "kind": "OBJECT", - "name": "User", - "ofType": null - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "updated_at", - "description": "The time when the DontAgree Action was updated.", - "args": [], - "type": { - "kind": "SCALAR", - "name": "Date", - "ofType": null - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "created_at", - "description": "The time when the DontAgree Action was created.", - "args": [], - "type": { - "kind": "SCALAR", - "name": "Date", - "ofType": null - }, - "isDeprecated": false, - "deprecationReason": null - } - ], - "inputFields": null, - "interfaces": [ - { - "kind": "INTERFACE", - "name": "Action", - "ofType": null - } - ], - "enumValues": null, - "possibleTypes": null - }, - { - "kind": "OBJECT", - "name": "DeleteActionResponse", - "description": "DeleteActionResponse is the response returned with possibly some errors\nrelating to the delete action attempt.", - "fields": [ - { - "name": "errors", - "description": "An array of errors relating to the mutation that occurred.", - "args": [], - "type": { - "kind": "LIST", - "name": null, - "ofType": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "INTERFACE", - "name": "UserError", - "ofType": null - } - } - }, - "isDeprecated": false, - "deprecationReason": null - } - ], - "inputFields": null, - "interfaces": [ - { - "kind": "INTERFACE", - "name": "Response", - "ofType": null - } - ], - "enumValues": null, - "possibleTypes": null - }, - { - "kind": "INPUT_OBJECT", - "name": "EditCommentInput", - "description": "", - "fields": null, - "inputFields": [ - { - "name": "body", - "description": "Update body of the comment", - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "SCALAR", - "name": "String", - "ofType": null - } - }, - "defaultValue": null - }, - { - "name": "richTextBody", - "description": "", - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "SCALAR", - "name": "String", - "ofType": null - } - }, - "defaultValue": null - } - ], - "interfaces": null, - "enumValues": null, - "possibleTypes": null - }, - { - "kind": "OBJECT", - "name": "EditCommentResponse", - "description": "EditCommentResponse contains the updated comment and any errors that occurred.", - "fields": [ - { - "name": "comment", - "description": "The edited comment.", - "args": [], - "type": { - "kind": "OBJECT", - "name": "Comment", - "ofType": null - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "errors", - "description": "An array of errors relating to the mutation that occurred.", - "args": [], - "type": { - "kind": "LIST", - "name": null, - "ofType": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "INTERFACE", - "name": "UserError", - "ofType": null - } - } - }, - "isDeprecated": false, - "deprecationReason": null - } - ], - "inputFields": null, - "interfaces": [ - { - "kind": "INTERFACE", - "name": "Response", - "ofType": null - } - ], - "enumValues": null, - "possibleTypes": null - }, - { - "kind": "INPUT_OBJECT", - "name": "SuspendUserInput", - "description": "", - "fields": null, - "inputFields": [ - { - "name": "id", - "description": "", - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "SCALAR", - "name": "ID", - "ofType": null - } - }, - "defaultValue": null - }, - { - "name": "message", - "description": "", - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "SCALAR", - "name": "String", - "ofType": null - } - }, - "defaultValue": null - }, - { - "name": "until", - "description": "", - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "SCALAR", - "name": "Date", - "ofType": null - } - }, - "defaultValue": null - } - ], - "interfaces": null, - "enumValues": null, - "possibleTypes": null - }, - { - "kind": "OBJECT", - "name": "SuspendUserResponse", - "description": "", - "fields": [ - { - "name": "errors", - "description": "", - "args": [], - "type": { - "kind": "LIST", - "name": null, - "ofType": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "INTERFACE", - "name": "UserError", - "ofType": null - } - } - }, - "isDeprecated": false, - "deprecationReason": null - } - ], - "inputFields": null, - "interfaces": [ - { - "kind": "INTERFACE", - "name": "Response", - "ofType": null - } - ], - "enumValues": null, - "possibleTypes": null - }, - { - "kind": "INPUT_OBJECT", - "name": "UnsuspendUserInput", - "description": "", - "fields": null, - "inputFields": [ - { - "name": "id", - "description": "", - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "SCALAR", - "name": "ID", - "ofType": null - } - }, - "defaultValue": null - } - ], - "interfaces": null, - "enumValues": null, - "possibleTypes": null - }, - { - "kind": "OBJECT", - "name": "UnsuspendUserResponse", - "description": "UnsuspendUserResponse is the response returned with possibly some\nerrors relating to the suspend action attempt.", - "fields": [ - { - "name": "errors", - "description": "An array of errors relating to the mutation that occurred.", - "args": [], - "type": { - "kind": "LIST", - "name": null, - "ofType": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "INTERFACE", - "name": "UserError", - "ofType": null - } - } - }, - "isDeprecated": false, - "deprecationReason": null - } - ], - "inputFields": null, - "interfaces": [ - { - "kind": "INTERFACE", - "name": "Response", - "ofType": null - } - ], - "enumValues": null, - "possibleTypes": null - }, - { - "kind": "INPUT_OBJECT", - "name": "BanUserInput", - "description": "", - "fields": null, - "inputFields": [ - { - "name": "id", - "description": "", - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "SCALAR", - "name": "ID", - "ofType": null - } - }, - "defaultValue": null - }, - { - "name": "message", - "description": "", - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "SCALAR", - "name": "String", - "ofType": null - } - }, - "defaultValue": null - } - ], - "interfaces": null, - "enumValues": null, - "possibleTypes": null - }, - { - "kind": "OBJECT", - "name": "BanUsersResponse", - "description": "", - "fields": [ - { - "name": "errors", - "description": "", - "args": [], - "type": { - "kind": "LIST", - "name": null, - "ofType": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "INTERFACE", - "name": "UserError", - "ofType": null - } - } - }, - "isDeprecated": false, - "deprecationReason": null - } - ], - "inputFields": null, - "interfaces": [ - { - "kind": "INTERFACE", - "name": "Response", - "ofType": null - } - ], - "enumValues": null, - "possibleTypes": null - }, - { - "kind": "INPUT_OBJECT", - "name": "UnbanUserInput", - "description": "", - "fields": null, - "inputFields": [ - { - "name": "id", - "description": "", - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "SCALAR", - "name": "ID", - "ofType": null - } - }, - "defaultValue": null - } - ], - "interfaces": null, - "enumValues": null, - "possibleTypes": null - }, - { - "kind": "OBJECT", - "name": "UnbanUserResponse", - "description": "", - "fields": [ - { - "name": "errors", - "description": "", - "args": [], - "type": { - "kind": "LIST", - "name": null, - "ofType": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "INTERFACE", - "name": "UserError", - "ofType": null - } - } - }, - "isDeprecated": false, - "deprecationReason": null - } - ], - "inputFields": null, - "interfaces": [ - { - "kind": "INTERFACE", - "name": "Response", - "ofType": null - } - ], - "enumValues": null, - "possibleTypes": null - }, - { - "kind": "OBJECT", - "name": "SetUsernameStatusResponse", - "description": "", - "fields": [ - { - "name": "errors", - "description": "An array of errors relating to the mutation that occurred.", - "args": [], - "type": { - "kind": "LIST", - "name": null, - "ofType": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "INTERFACE", - "name": "UserError", - "ofType": null - } - } - }, - "isDeprecated": false, - "deprecationReason": null - } - ], - "inputFields": null, - "interfaces": [ - { - "kind": "INTERFACE", - "name": "Response", - "ofType": null - } - ], - "enumValues": null, - "possibleTypes": null - }, - { - "kind": "OBJECT", - "name": "ChangeUsernameResponse", - "description": "", - "fields": [ - { - "name": "errors", - "description": "An array of errors relating to the mutation that occurred.", - "args": [], - "type": { - "kind": "LIST", - "name": null, - "ofType": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "INTERFACE", - "name": "UserError", - "ofType": null - } - } - }, - "isDeprecated": false, - "deprecationReason": null - } - ], - "inputFields": null, - "interfaces": [ - { - "kind": "INTERFACE", - "name": "Response", - "ofType": null - } - ], - "enumValues": null, - "possibleTypes": null - }, - { - "kind": "OBJECT", - "name": "SetUsernameResponse", - "description": "", - "fields": [ - { - "name": "errors", - "description": "An array of errors relating to the mutation that occurred.", - "args": [], - "type": { - "kind": "LIST", - "name": null, - "ofType": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "INTERFACE", - "name": "UserError", - "ofType": null - } - } - }, - "isDeprecated": false, - "deprecationReason": null - } - ], - "inputFields": null, - "interfaces": [ - { - "kind": "INTERFACE", - "name": "Response", - "ofType": null - } - ], - "enumValues": null, - "possibleTypes": null - }, - { - "kind": "OBJECT", - "name": "SetCommentStatusResponse", - "description": "SetCommentStatusResponse is the response returned with possibly some errors\nrelating to the delete action attempt.", - "fields": [ - { - "name": "errors", - "description": "An array of errors relating to the mutation that occurred.", - "args": [], - "type": { - "kind": "LIST", - "name": null, - "ofType": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "INTERFACE", - "name": "UserError", - "ofType": null - } - } - }, - "isDeprecated": false, - "deprecationReason": null - } - ], - "inputFields": null, - "interfaces": [ - { - "kind": "INTERFACE", - "name": "Response", - "ofType": null - } - ], - "enumValues": null, - "possibleTypes": null - }, - { - "kind": "INPUT_OBJECT", - "name": "ModifyTagInput", - "description": "ModifyTagInput is the input used to modify a tag.", - "fields": null, - "inputFields": [ - { - "name": "name", - "description": "name is the actual tag to add to the model.", - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "SCALAR", - "name": "String", - "ofType": null - } - }, - "defaultValue": null - }, - { - "name": "id", - "description": "id is the ID of the model in question that we are modifying the tag of.", - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "SCALAR", - "name": "ID", - "ofType": null - } - }, - "defaultValue": null - }, - { - "name": "item_type", - "description": "item_type is the type of item that we are modifying the tag if.", - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "ENUM", - "name": "TAGGABLE_ITEM_TYPE", - "ofType": null - } - }, - "defaultValue": null - }, - { - "name": "asset_id", - "description": "asset_id is used when the item_type is `COMMENTS`, the is needed to rectify\nThe settings to get the asset specific tags/settings.", - "type": { - "kind": "SCALAR", - "name": "ID", - "ofType": null - }, - "defaultValue": null - } - ], - "interfaces": null, - "enumValues": null, - "possibleTypes": null - }, - { - "kind": "ENUM", - "name": "TAGGABLE_ITEM_TYPE", - "description": "Used to represent the item type for a tag.", - "fields": null, - "inputFields": null, - "interfaces": null, - "enumValues": [ - { - "name": "ASSETS", - "description": "The action references a entity of type Asset.", - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "COMMENTS", - "description": "The action references a entity of type Comment.", - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "USERS", - "description": "The action references a entity of type User.", - "isDeprecated": false, - "deprecationReason": null - } - ], - "possibleTypes": null - }, - { - "kind": "OBJECT", - "name": "ModifyTagResponse", - "description": "Response to the addTag or removeTag mutations.", - "fields": [ - { - "name": "errors", - "description": "An array of errors relating to the mutation that occurred.", - "args": [], - "type": { - "kind": "LIST", - "name": null, - "ofType": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "INTERFACE", - "name": "UserError", - "ofType": null - } - } - }, - "isDeprecated": false, - "deprecationReason": null - } - ], - "inputFields": null, - "interfaces": [ - { - "kind": "INTERFACE", - "name": "Response", - "ofType": null - } - ], - "enumValues": null, - "possibleTypes": null - }, - { - "kind": "OBJECT", - "name": "SetUserRoleResponse", - "description": "", - "fields": [ - { - "name": "errors", - "description": "An array of errors relating to the mutation that occurred.", - "args": [], - "type": { - "kind": "LIST", - "name": null, - "ofType": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "INTERFACE", - "name": "UserError", - "ofType": null - } - } - }, - "isDeprecated": false, - "deprecationReason": null - } - ], - "inputFields": null, - "interfaces": [ - { - "kind": "INTERFACE", - "name": "Response", - "ofType": null - } - ], - "enumValues": null, - "possibleTypes": null - }, - { - "kind": "INPUT_OBJECT", - "name": "AssetSettingsInput", - "description": "Configurable settings that can be overridden for the Asset. You must specify\nall fields that should be updated.", - "fields": null, - "inputFields": [ - { - "name": "premodLinksEnable", - "description": "premodLinksEnable will put all comments that contain links into premod.", - "type": { - "kind": "SCALAR", - "name": "Boolean", - "ofType": null - }, - "defaultValue": null - }, - { - "name": "moderation", - "description": "moderation is the moderation mode for the asset.", - "type": { - "kind": "ENUM", - "name": "MODERATION_MODE", - "ofType": null - }, - "defaultValue": null - }, - { - "name": "questionBoxEnable", - "description": "questionBoxEnable will enable the Question Boxs' content to be visible above\nThe comment box.", - "type": { - "kind": "SCALAR", - "name": "Boolean", - "ofType": null - }, - "defaultValue": null - }, - { - "name": "questionBoxContent", - "description": "questionBoxContent is the content of the Question Box.", - "type": { - "kind": "SCALAR", - "name": "String", - "ofType": null - }, - "defaultValue": null - }, - { - "name": "questionBoxIcon", - "description": "questionBoxIcon is the icon for the Question Box.", - "type": { - "kind": "SCALAR", - "name": "String", - "ofType": null - }, - "defaultValue": null - } - ], - "interfaces": null, - "enumValues": null, - "possibleTypes": null - }, - { - "kind": "OBJECT", - "name": "UpdateAssetSettingsResponse", - "description": "UpdateAssetSettingsResponse is the response returned with possibly some errors\nrelating to the update settings attempt.", - "fields": [ - { - "name": "errors", - "description": "An array of errors relating to the mutation that occurred.", - "args": [], - "type": { - "kind": "LIST", - "name": null, - "ofType": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "INTERFACE", - "name": "UserError", - "ofType": null - } - } - }, - "isDeprecated": false, - "deprecationReason": null - } - ], - "inputFields": null, - "interfaces": [ - { - "kind": "INTERFACE", - "name": "Response", - "ofType": null - } - ], - "enumValues": null, - "possibleTypes": null - }, - { - "kind": "INPUT_OBJECT", - "name": "UpdateAssetStatusInput", - "description": "UpdateAssetStatusInput contains the input to change the status of a comment as\nit relates to being open/closed for commenting.", - "fields": null, - "inputFields": [ - { - "name": "closedAt", - "description": "closedAt is the time that the asset will be closed for commenting. If this\nis null or in the future, it will be open for commenting.", - "type": { - "kind": "SCALAR", - "name": "Date", - "ofType": null - }, - "defaultValue": null - }, - { - "name": "closedMessage", - "description": "closedMessage is the message to be set on the asset when it is closed. If it\nis null, then the message will default to the globally set `closedMessage`.", - "type": { - "kind": "SCALAR", - "name": "String", - "ofType": null - }, - "defaultValue": null - } - ], - "interfaces": null, - "enumValues": null, - "possibleTypes": null - }, - { - "kind": "OBJECT", - "name": "UpdateAssetStatusResponse", - "description": "UpdateAssetStatusResponse is the response returned with possibly some errors\nrelating to the update status attempt.", - "fields": [ - { - "name": "errors", - "description": "An array of errors relating to the mutation that occurred.", - "args": [], - "type": { - "kind": "LIST", - "name": null, - "ofType": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "INTERFACE", - "name": "UserError", - "ofType": null - } - } - }, - "isDeprecated": false, - "deprecationReason": null - } - ], - "inputFields": null, - "interfaces": [ - { - "kind": "INTERFACE", - "name": "Response", - "ofType": null - } - ], - "enumValues": null, - "possibleTypes": null - }, - { - "kind": "OBJECT", - "name": "CloseAssetResponse", - "description": "CloseAssetResponse is the response returned with possibly some errors\nrelating to the update status attempt.", - "fields": [ - { - "name": "errors", - "description": "An array of errors relating to the mutation that occurred.", - "args": [], - "type": { - "kind": "LIST", - "name": null, - "ofType": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "INTERFACE", - "name": "UserError", - "ofType": null - } - } - }, - "isDeprecated": false, - "deprecationReason": null - } - ], - "inputFields": null, - "interfaces": [ - { - "kind": "INTERFACE", - "name": "Response", - "ofType": null - } - ], - "enumValues": null, - "possibleTypes": null - }, - { - "kind": "INPUT_OBJECT", - "name": "UpdateSettingsInput", - "description": "UpdateSettingsInput is the input used to input the global site settings. This\nwill override the existing settings, so all fields must be included.", - "fields": null, - "inputFields": [ - { - "name": "moderation", - "description": "moderation is the moderation mode for all Asset's on the site.", - "type": { - "kind": "ENUM", - "name": "MODERATION_MODE", - "ofType": null - }, - "defaultValue": null - }, - { - "name": "requireEmailConfirmation", - "description": "Enables a requirement for email confirmation before a user can login.", - "type": { - "kind": "SCALAR", - "name": "Boolean", - "ofType": null - }, - "defaultValue": null - }, - { - "name": "infoBoxEnable", - "description": "infoBoxEnable will enable the Info Box content visible above the question\nbox.", - "type": { - "kind": "SCALAR", - "name": "Boolean", - "ofType": null - }, - "defaultValue": null - }, - { - "name": "infoBoxContent", - "description": "infoBoxContent is the content of the Info Box.", - "type": { - "kind": "SCALAR", - "name": "String", - "ofType": null - }, - "defaultValue": null - }, - { - "name": "questionBoxEnable", - "description": "questionBoxEnable will enable the Question Box's content to be visible above\nThe comment box.", - "type": { - "kind": "SCALAR", - "name": "Boolean", - "ofType": null - }, - "defaultValue": null - }, - { - "name": "questionBoxContent", - "description": "questionBoxContent is the content of the Question Box.", - "type": { - "kind": "SCALAR", - "name": "String", - "ofType": null - }, - "defaultValue": null - }, - { - "name": "premodLinksEnable", - "description": "premodLinksEnable will put all comments that contain links into premod.", - "type": { - "kind": "SCALAR", - "name": "Boolean", - "ofType": null - }, - "defaultValue": null - }, - { - "name": "questionBoxIcon", - "description": "questionBoxIcon is the icon for the Question Box.", - "type": { - "kind": "SCALAR", - "name": "String", - "ofType": null - }, - "defaultValue": null - }, - { - "name": "autoCloseStream", - "description": "autoCloseStream when true will auto close the stream when the `closeTimeout`\namount of seconds have been reached.", - "type": { - "kind": "SCALAR", - "name": "Boolean", - "ofType": null - }, - "defaultValue": null - }, - { - "name": "customCssUrl", - "description": "customCssUrl is the URL of the custom CSS used to display on the frontend.", - "type": { - "kind": "SCALAR", - "name": "String", - "ofType": null - }, - "defaultValue": null - }, - { - "name": "closedTimeout", - "description": "closedTimeout is the amount of seconds from the created_at timestamp that a\ngiven asset will be considered closed.", - "type": { - "kind": "SCALAR", - "name": "Int", - "ofType": null - }, - "defaultValue": null - }, - { - "name": "closedMessage", - "description": "closedMessage is the message shown to the user when the given Asset is\nclosed.", - "type": { - "kind": "SCALAR", - "name": "String", - "ofType": null - }, - "defaultValue": null - }, - { - "name": "charCountEnable", - "description": "charCountEnable is true when the character count restriction is enabled.", - "type": { - "kind": "SCALAR", - "name": "Boolean", - "ofType": null - }, - "defaultValue": null - }, - { - "name": "charCount", - "description": "charCount is the maximum number of characters a comment may be.", - "type": { - "kind": "SCALAR", - "name": "Int", - "ofType": null - }, - "defaultValue": null - }, - { - "name": "organizationName", - "description": "organizationName is the name of the organization.", - "type": { - "kind": "SCALAR", - "name": "String", - "ofType": null - }, - "defaultValue": null - }, - { - "name": "editCommentWindowLength", - "description": "editCommentWindowLength is the length of time (in milliseconds) after a\ncomment is posted that it can still be edited by the author.", - "type": { - "kind": "SCALAR", - "name": "Int", - "ofType": null - }, - "defaultValue": null - }, - { - "name": "wordlist", - "description": "wordlist allows changing the available wordlists.", - "type": { - "kind": "INPUT_OBJECT", - "name": "UpdateWordlistInput", - "ofType": null - }, - "defaultValue": null - }, - { - "name": "domains", - "description": "domains allows changing the available lists of domains.", - "type": { - "kind": "INPUT_OBJECT", - "name": "UpdateDomainsInput", - "ofType": null - }, - "defaultValue": null - } - ], - "interfaces": null, - "enumValues": null, - "possibleTypes": null - }, - { - "kind": "INPUT_OBJECT", - "name": "UpdateWordlistInput", - "description": "UpdateWordlistInput is the list of words that composes the Wordlist.", - "fields": null, - "inputFields": [ - { - "name": "banned", - "description": "banned words will by default reject the comment if it is found.", - "type": { - "kind": "LIST", - "name": null, - "ofType": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "SCALAR", - "name": "String", - "ofType": null - } - } - }, - "defaultValue": null - }, - { - "name": "suspect", - "description": "suspect words will simply flag the comment.", - "type": { - "kind": "LIST", - "name": null, - "ofType": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "SCALAR", - "name": "String", - "ofType": null - } - } - }, - "defaultValue": null - } - ], - "interfaces": null, - "enumValues": null, - "possibleTypes": null - }, - { - "kind": "INPUT_OBJECT", - "name": "UpdateDomainsInput", - "description": "UpdateDomainsInput describes all the available lists of domains.", - "fields": null, - "inputFields": [ - { - "name": "whitelist", - "description": "whitelist is the list of domains that the embed is allowed to render on.", - "type": { - "kind": "LIST", - "name": null, - "ofType": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "SCALAR", - "name": "String", - "ofType": null - } - } - }, - "defaultValue": null - } - ], - "interfaces": null, - "enumValues": null, - "possibleTypes": null - }, - { - "kind": "OBJECT", - "name": "UpdateSettingsResponse", - "description": "UpdateSettingsResponse contains any errors that were rendered as a result\nof the mutation.", - "fields": [ - { - "name": "errors", - "description": "An array of errors relating to the mutation that occurred.", - "args": [], - "type": { - "kind": "LIST", - "name": null, - "ofType": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "INTERFACE", - "name": "UserError", - "ofType": null - } - } - }, - "isDeprecated": false, - "deprecationReason": null - } - ], - "inputFields": null, - "interfaces": [ - { - "kind": "INTERFACE", - "name": "Response", - "ofType": null - } - ], - "enumValues": null, - "possibleTypes": null - }, - { - "kind": "OBJECT", - "name": "IgnoreUserResponse", - "description": "Response to ignoreUser mutation", - "fields": [ - { - "name": "errors", - "description": "An array of errors relating to the mutation that occurred.", - "args": [], - "type": { - "kind": "LIST", - "name": null, - "ofType": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "INTERFACE", - "name": "UserError", - "ofType": null - } - } - }, - "isDeprecated": false, - "deprecationReason": null - } - ], - "inputFields": null, - "interfaces": [ - { - "kind": "INTERFACE", - "name": "Response", - "ofType": null - } - ], - "enumValues": null, - "possibleTypes": null - }, - { - "kind": "INPUT_OBJECT", - "name": "CreateTokenInput", - "description": "CreateTokenInput contains the input to create the token.", - "fields": null, - "inputFields": [ - { - "name": "name", - "description": "Name is the description for the token.", - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "SCALAR", - "name": "String", - "ofType": null - } - }, - "defaultValue": null - } - ], - "interfaces": null, - "enumValues": null, - "possibleTypes": null - }, - { - "kind": "OBJECT", - "name": "CreateTokenResponse", - "description": "CreateTokenResponse contains the errors related to creating a token.", - "fields": [ - { - "name": "token", - "description": "Token is the Token that was created, or null if it failed.", - "args": [], - "type": { - "kind": "OBJECT", - "name": "Token", - "ofType": null - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "errors", - "description": "An array of errors relating to the mutation that occurred.", - "args": [], - "type": { - "kind": "LIST", - "name": null, - "ofType": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "INTERFACE", - "name": "UserError", - "ofType": null - } - } - }, - "isDeprecated": false, - "deprecationReason": null - } - ], - "inputFields": null, - "interfaces": [ - { - "kind": "INTERFACE", - "name": "Response", - "ofType": null - } - ], - "enumValues": null, - "possibleTypes": null - }, - { - "kind": "INPUT_OBJECT", - "name": "RevokeTokenInput", - "description": "RevokeTokenInput contains the input to revoke the token.", - "fields": null, - "inputFields": [ - { - "name": "id", - "description": "ID is the JTI for the token.", - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "SCALAR", - "name": "ID", - "ofType": null - } - }, - "defaultValue": null - } - ], - "interfaces": null, - "enumValues": null, - "possibleTypes": null - }, - { - "kind": "OBJECT", - "name": "RevokeTokenResponse", - "description": "RevokeTokenResponse contains the errors related to revoking a token.", - "fields": [ - { - "name": "errors", - "description": "An array of errors relating to the mutation that occurred.", - "args": [], - "type": { - "kind": "LIST", - "name": null, - "ofType": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "INTERFACE", - "name": "UserError", - "ofType": null - } - } - }, - "isDeprecated": false, - "deprecationReason": null - } - ], - "inputFields": null, - "interfaces": [ - { - "kind": "INTERFACE", - "name": "Response", - "ofType": null - } - ], - "enumValues": null, - "possibleTypes": null - }, - { - "kind": "OBJECT", - "name": "StopIgnoringUserResponse", - "description": "Response to stopIgnoringUser mutation", - "fields": [ - { - "name": "errors", - "description": "An array of errors relating to the mutation that occurred.", - "args": [], - "type": { - "kind": "LIST", - "name": null, - "ofType": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "INTERFACE", - "name": "UserError", - "ofType": null - } - } - }, - "isDeprecated": false, - "deprecationReason": null - } - ], - "inputFields": null, - "interfaces": [ - { - "kind": "INTERFACE", - "name": "Response", - "ofType": null - } - ], - "enumValues": null, - "possibleTypes": null - }, - { - "kind": "OBJECT", - "name": "ForceScrapeAssetResponse", - "description": "", - "fields": [ - { - "name": "errors", - "description": "An array of errors relating to the mutation that occurred.", - "args": [], - "type": { - "kind": "LIST", - "name": null, - "ofType": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "INTERFACE", - "name": "UserError", - "ofType": null - } - } - }, - "isDeprecated": false, - "deprecationReason": null - } - ], - "inputFields": null, - "interfaces": [ - { - "kind": "INTERFACE", - "name": "Response", - "ofType": null - } - ], - "enumValues": null, - "possibleTypes": null - }, - { - "kind": "OBJECT", - "name": "DelUserResponse", - "description": "", - "fields": [ - { - "name": "errors", - "description": "An array of errors relating to the mutation that occurred.", - "args": [], - "type": { - "kind": "LIST", - "name": null, - "ofType": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "INTERFACE", - "name": "UserError", - "ofType": null - } - } - }, - "isDeprecated": false, - "deprecationReason": null - } - ], - "inputFields": null, - "interfaces": [ - { - "kind": "INTERFACE", - "name": "Response", - "ofType": null - } - ], - "enumValues": null, - "possibleTypes": null - }, - { - "kind": "INPUT_OBJECT", - "name": "CreateRespectActionInput", - "description": "", - "fields": null, - "inputFields": [ - { - "name": "item_id", - "description": "The item's id for which we are to create a respect.", - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "SCALAR", - "name": "ID", - "ofType": null - } - }, - "defaultValue": null - } - ], - "interfaces": null, - "enumValues": null, - "possibleTypes": null - }, - { - "kind": "OBJECT", - "name": "CreateRespectActionResponse", - "description": "", - "fields": [ - { - "name": "respect", - "description": "The respect that was created.", - "args": [], - "type": { - "kind": "OBJECT", - "name": "RespectAction", - "ofType": null - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "errors", - "description": "An array of errors relating to the mutation that occurred.", - "args": [], - "type": { - "kind": "LIST", - "name": null, - "ofType": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "INTERFACE", - "name": "UserError", - "ofType": null - } - } - }, - "isDeprecated": false, - "deprecationReason": null - } - ], - "inputFields": null, - "interfaces": [ - { - "kind": "INTERFACE", - "name": "Response", - "ofType": null - } - ], - "enumValues": null, - "possibleTypes": null - }, - { - "kind": "OBJECT", - "name": "RespectAction", - "description": "RespectAction is used by users who \"respect\" a specific entity.", - "fields": [ - { - "name": "id", - "description": "The ID of the action.", - "args": [], - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "SCALAR", - "name": "ID", - "ofType": null - } - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "user", - "description": "The author of the action.", - "args": [], - "type": { - "kind": "OBJECT", - "name": "User", - "ofType": null - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "updated_at", - "description": "The time when the Action was updated.", - "args": [], - "type": { - "kind": "SCALAR", - "name": "Date", - "ofType": null - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "created_at", - "description": "The time when the Action was created.", - "args": [], - "type": { - "kind": "SCALAR", - "name": "Date", - "ofType": null - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "item_id", - "description": "The item's id for which the Action was created.", - "args": [], - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "SCALAR", - "name": "ID", - "ofType": null - } - }, - "isDeprecated": false, - "deprecationReason": null - } - ], - "inputFields": null, - "interfaces": [ - { - "kind": "INTERFACE", - "name": "Action", - "ofType": null - } - ], - "enumValues": null, - "possibleTypes": null - }, - { - "kind": "INPUT_OBJECT", - "name": "DeleteRespectActionInput", - "description": "", - "fields": null, - "inputFields": [ - { - "name": "id", - "description": "The item's id for which we are deleting a respect.", - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "SCALAR", - "name": "ID", - "ofType": null - } - }, - "defaultValue": null - } - ], - "interfaces": null, - "enumValues": null, - "possibleTypes": null - }, - { - "kind": "OBJECT", - "name": "DeleteRespectActionResponse", - "description": "", - "fields": [ - { - "name": "errors", - "description": "An array of errors relating to the mutation that occurred.", - "args": [], - "type": { - "kind": "LIST", - "name": null, - "ofType": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "INTERFACE", - "name": "UserError", - "ofType": null - } - } - }, - "isDeprecated": false, - "deprecationReason": null - } - ], - "inputFields": null, - "interfaces": [ - { - "kind": "INTERFACE", - "name": "Response", - "ofType": null - } - ], - "enumValues": null, - "possibleTypes": null - }, - { - "kind": "INPUT_OBJECT", - "name": "NotificationSettingsInput", - "description": "", - "fields": null, - "inputFields": [ - { - "name": "digestFrequency", - "description": "digestFrequency is the frequency to send notifications.", - "type": { - "kind": "ENUM", - "name": "DIGEST_FREQUENCY", - "ofType": null - }, - "defaultValue": null - }, - { - "name": "onReply", - "description": "", - "type": { - "kind": "SCALAR", - "name": "Boolean", - "ofType": null - }, - "defaultValue": null - }, - { - "name": "onFeatured", - "description": "", - "type": { - "kind": "SCALAR", - "name": "Boolean", - "ofType": null - }, - "defaultValue": null - }, - { - "name": "onStaffReply", - "description": "", - "type": { - "kind": "SCALAR", - "name": "Boolean", - "ofType": null - }, - "defaultValue": null - } - ], - "interfaces": null, - "enumValues": null, - "possibleTypes": null - }, - { - "kind": "OBJECT", - "name": "UpdateNotificationSettingsResponse", - "description": "", - "fields": [ - { - "name": "errors", - "description": "An array of errors relating to the mutation that occurred.", - "args": [], - "type": { - "kind": "LIST", - "name": null, - "ofType": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "INTERFACE", - "name": "UserError", - "ofType": null - } - } - }, - "isDeprecated": false, - "deprecationReason": null - } - ], - "inputFields": null, - "interfaces": [ - { - "kind": "INTERFACE", - "name": "Response", - "ofType": null - } - ], - "enumValues": null, - "possibleTypes": null - }, - { - "kind": "OBJECT", - "name": "Subscription", - "description": "", - "fields": [ - { - "name": "commentAdded", - "description": "Get an update whenever a comment was added.\n`asset_id` is required except for users with the `ADMIN` or `MODERATOR` role.\nNon privileged user can only subscribe to 'NONE' and/or 'ACCEPTED' statuses.", - "args": [ - { - "name": "asset_id", - "description": "", - "type": { - "kind": "SCALAR", - "name": "ID", - "ofType": null - }, - "defaultValue": null - }, - { - "name": "statuses", - "description": "", - "type": { - "kind": "LIST", - "name": null, - "ofType": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "ENUM", - "name": "COMMENT_STATUS", - "ofType": null - } - } - }, - "defaultValue": "[NONE, ACCEPTED]" - } - ], - "type": { - "kind": "OBJECT", - "name": "Comment", - "ofType": null - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "commentEdited", - "description": "Get an update whenever a comment was edited.\n`asset_id` is required except for users with the `ADMIN` or `MODERATOR` role.", - "args": [ - { - "name": "asset_id", - "description": "", - "type": { - "kind": "SCALAR", - "name": "ID", - "ofType": null - }, - "defaultValue": null - } - ], - "type": { - "kind": "OBJECT", - "name": "Comment", - "ofType": null - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "commentFlagged", - "description": "Get an update whenever a comment was flagged.\nRequires the `ADMIN` or `MODERATOR` role.", - "args": [ - { - "name": "asset_id", - "description": "", - "type": { - "kind": "SCALAR", - "name": "ID", - "ofType": null - }, - "defaultValue": null - } - ], - "type": { - "kind": "OBJECT", - "name": "Comment", - "ofType": null - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "commentAccepted", - "description": "Get an update whenever a comment has been accepted.\nRequires the `ADMIN` or `MODERATOR` role.", - "args": [ - { - "name": "asset_id", - "description": "", - "type": { - "kind": "SCALAR", - "name": "ID", - "ofType": null - }, - "defaultValue": null - } - ], - "type": { - "kind": "OBJECT", - "name": "Comment", - "ofType": null - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "commentRejected", - "description": "Get an update whenever a comment has been rejected.\nRequires the `ADMIN` or `MODERATOR` role.", - "args": [ - { - "name": "asset_id", - "description": "", - "type": { - "kind": "SCALAR", - "name": "ID", - "ofType": null - }, - "defaultValue": null - } - ], - "type": { - "kind": "OBJECT", - "name": "Comment", - "ofType": null - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "commentReset", - "description": "Get an update whenever the status of a comment has been reset.\nRequires the `ADMIN` or `MODERATOR` role.", - "args": [ - { - "name": "asset_id", - "description": "", - "type": { - "kind": "SCALAR", - "name": "ID", - "ofType": null - }, - "defaultValue": null - } - ], - "type": { - "kind": "OBJECT", - "name": "Comment", - "ofType": null - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "userSuspended", - "description": "Get an update whenever a user has been suspended.\n`user_id` must match id of current user except for\nusers with the `ADMIN` or `MODERATOR` role.", - "args": [ - { - "name": "user_id", - "description": "", - "type": { - "kind": "SCALAR", - "name": "ID", - "ofType": null - }, - "defaultValue": null - } - ], - "type": { - "kind": "OBJECT", - "name": "User", - "ofType": null - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "userBanned", - "description": "Get an update whenever a user has been banned.\n`user_id` must match id of current user except for\nusers with the `ADMIN` or `MODERATOR` role.", - "args": [ - { - "name": "user_id", - "description": "", - "type": { - "kind": "SCALAR", - "name": "ID", - "ofType": null - }, - "defaultValue": null - } - ], - "type": { - "kind": "OBJECT", - "name": "User", - "ofType": null - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "usernameFlagged", - "description": "Get an update whenever a username was flagged.\n`user_id` must match id of current user except for\nusers with the `ADMIN` or `MODERATOR` role.", - "args": [ - { - "name": "user_id", - "description": "", - "type": { - "kind": "SCALAR", - "name": "ID", - "ofType": null - }, - "defaultValue": null - } - ], - "type": { - "kind": "OBJECT", - "name": "User", - "ofType": null - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "usernameRejected", - "description": "Get an update whenever a username has been rejected.\n`user_id` must match id of current user except for\nusers with the `ADMIN` or `MODERATOR` role.", - "args": [ - { - "name": "user_id", - "description": "", - "type": { - "kind": "SCALAR", - "name": "ID", - "ofType": null - }, - "defaultValue": null - } - ], - "type": { - "kind": "OBJECT", - "name": "User", - "ofType": null - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "usernameApproved", - "description": "Get an update whenever a username has been approved. `user_id` must match id\nof current user except for users with the `ADMIN` or `MODERATOR` role.", - "args": [ - { - "name": "user_id", - "description": "", - "type": { - "kind": "SCALAR", - "name": "ID", - "ofType": null - }, - "defaultValue": null - } - ], - "type": { - "kind": "OBJECT", - "name": "User", - "ofType": null - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "usernameChanged", - "description": "Get an update whenever a username has been changed. `user_id` must match id\nof current user except for users with the `ADMIN` or `MODERATOR` role.", - "args": [ - { - "name": "user_id", - "description": "", - "type": { - "kind": "SCALAR", - "name": "ID", - "ofType": null - }, - "defaultValue": null - } - ], - "type": { - "kind": "OBJECT", - "name": "UsernameChangedPayload", - "ofType": null - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "userCreated", - "description": "Get an update whenever a user is created. Only accessible to users with the\n`ADMIN` or `MODERATOR` roles.", - "args": [], - "type": { - "kind": "OBJECT", - "name": "User", - "ofType": null - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "respectActionCreated", - "description": "Subscribe to respects.", - "args": [ - { - "name": "asset_id", - "description": "", - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "SCALAR", - "name": "ID", - "ofType": null - } - }, - "defaultValue": null - } - ], - "type": { - "kind": "OBJECT", - "name": "RespectAction", - "ofType": null - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "respectActionDeleted", - "description": "Subscribe to respect removals.", - "args": [ - { - "name": "asset_id", - "description": "", - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "SCALAR", - "name": "ID", - "ofType": null - } - }, - "defaultValue": null - } - ], - "type": { - "kind": "OBJECT", - "name": "RespectAction", - "ofType": null - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "commentFeatured", - "description": "Subscribe to featured comments.", - "args": [ - { - "name": "asset_id", - "description": "", - "type": { - "kind": "SCALAR", - "name": "ID", - "ofType": null - }, - "defaultValue": null - } - ], - "type": { - "kind": "OBJECT", - "name": "CommentFeaturedData", - "ofType": null - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "commentUnfeatured", - "description": "Subscribe to featured comments.", - "args": [ - { - "name": "asset_id", - "description": "", - "type": { - "kind": "SCALAR", - "name": "ID", - "ofType": null - }, - "defaultValue": null - } - ], - "type": { - "kind": "OBJECT", - "name": "CommentUnfeaturedData", - "ofType": null - }, - "isDeprecated": false, - "deprecationReason": null - } - ], - "inputFields": null, - "interfaces": [], - "enumValues": null, - "possibleTypes": null - }, - { - "kind": "OBJECT", - "name": "UsernameChangedPayload", - "description": "", - "fields": [ - { - "name": "previousUsername", - "description": "", - "args": [], - "type": { - "kind": "SCALAR", - "name": "String", - "ofType": null - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "user", - "description": "", - "args": [], - "type": { - "kind": "OBJECT", - "name": "User", - "ofType": null - }, - "isDeprecated": false, - "deprecationReason": null - } - ], - "inputFields": null, - "interfaces": [], - "enumValues": null, - "possibleTypes": null - }, - { - "kind": "OBJECT", - "name": "CommentFeaturedData", - "description": "", - "fields": [ - { - "name": "comment", - "description": "", - "args": [], - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "OBJECT", - "name": "Comment", - "ofType": null - } - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "user", - "description": "", - "args": [], - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "OBJECT", - "name": "User", - "ofType": null - } - }, - "isDeprecated": false, - "deprecationReason": null - } - ], - "inputFields": null, - "interfaces": [], - "enumValues": null, - "possibleTypes": null - }, - { - "kind": "OBJECT", - "name": "CommentUnfeaturedData", - "description": "", - "fields": [ - { - "name": "comment", - "description": "", - "args": [], - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "OBJECT", - "name": "Comment", - "ofType": null - } - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "user", - "description": "", - "args": [], - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "OBJECT", - "name": "User", - "ofType": null - } - }, - "isDeprecated": false, - "deprecationReason": null - } - ], - "inputFields": null, - "interfaces": [], - "enumValues": null, - "possibleTypes": null - }, - { - "kind": "OBJECT", - "name": "__Schema", - "description": "A GraphQL Schema defines the capabilities of a GraphQL server. It exposes all available types and directives on the server, as well as the entry points for query, mutation, and subscription operations.", - "fields": [ - { - "name": "types", - "description": "A list of all types supported by this server.", - "args": [], - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "LIST", - "name": null, - "ofType": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "OBJECT", - "name": "__Type", - "ofType": null - } - } - } - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "queryType", - "description": "The type that query operations will be rooted at.", - "args": [], - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "OBJECT", - "name": "__Type", - "ofType": null - } - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "mutationType", - "description": "If this server supports mutation, the type that mutation operations will be rooted at.", - "args": [], - "type": { - "kind": "OBJECT", - "name": "__Type", - "ofType": null - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "subscriptionType", - "description": "If this server support subscription, the type that subscription operations will be rooted at.", - "args": [], - "type": { - "kind": "OBJECT", - "name": "__Type", - "ofType": null - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "directives", - "description": "A list of all directives supported by this server.", - "args": [], - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "LIST", - "name": null, - "ofType": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "OBJECT", - "name": "__Directive", - "ofType": null - } - } - } - }, - "isDeprecated": false, - "deprecationReason": null - } - ], - "inputFields": null, - "interfaces": [], - "enumValues": null, - "possibleTypes": null - }, - { - "kind": "OBJECT", - "name": "__Type", - "description": "The fundamental unit of any GraphQL Schema is the type. There are many kinds of types in GraphQL as represented by the `__TypeKind` enum.\n\nDepending on the kind of a type, certain fields describe information about that type. Scalar types provide no information beyond a name and description, while Enum types provide their values. Object and Interface types provide the fields they describe. Abstract types, Union and Interface, provide the Object types possible at runtime. List and NonNull types compose other types.", - "fields": [ - { - "name": "kind", - "description": null, - "args": [], - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "ENUM", - "name": "__TypeKind", - "ofType": null - } - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "name", - "description": null, - "args": [], - "type": { - "kind": "SCALAR", - "name": "String", - "ofType": null - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "description", - "description": null, - "args": [], - "type": { - "kind": "SCALAR", - "name": "String", - "ofType": null - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "fields", - "description": null, - "args": [ - { - "name": "includeDeprecated", - "description": null, - "type": { - "kind": "SCALAR", - "name": "Boolean", - "ofType": null - }, - "defaultValue": "false" - } - ], - "type": { - "kind": "LIST", - "name": null, - "ofType": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "OBJECT", - "name": "__Field", - "ofType": null - } - } - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "interfaces", - "description": null, - "args": [], - "type": { - "kind": "LIST", - "name": null, - "ofType": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "OBJECT", - "name": "__Type", - "ofType": null - } - } - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "possibleTypes", - "description": null, - "args": [], - "type": { - "kind": "LIST", - "name": null, - "ofType": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "OBJECT", - "name": "__Type", - "ofType": null - } - } - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "enumValues", - "description": null, - "args": [ - { - "name": "includeDeprecated", - "description": null, - "type": { - "kind": "SCALAR", - "name": "Boolean", - "ofType": null - }, - "defaultValue": "false" - } - ], - "type": { - "kind": "LIST", - "name": null, - "ofType": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "OBJECT", - "name": "__EnumValue", - "ofType": null - } - } - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "inputFields", - "description": null, - "args": [], - "type": { - "kind": "LIST", - "name": null, - "ofType": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "OBJECT", - "name": "__InputValue", - "ofType": null - } - } - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "ofType", - "description": null, - "args": [], - "type": { - "kind": "OBJECT", - "name": "__Type", - "ofType": null - }, - "isDeprecated": false, - "deprecationReason": null - } - ], - "inputFields": null, - "interfaces": [], - "enumValues": null, - "possibleTypes": null - }, - { - "kind": "ENUM", - "name": "__TypeKind", - "description": "An enum describing what kind of type a given `__Type` is.", - "fields": null, - "inputFields": null, - "interfaces": null, - "enumValues": [ - { - "name": "SCALAR", - "description": "Indicates this type is a scalar.", - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "OBJECT", - "description": "Indicates this type is an object. `fields` and `interfaces` are valid fields.", - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "INTERFACE", - "description": "Indicates this type is an interface. `fields` and `possibleTypes` are valid fields.", - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "UNION", - "description": "Indicates this type is a union. `possibleTypes` is a valid field.", - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "ENUM", - "description": "Indicates this type is an enum. `enumValues` is a valid field.", - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "INPUT_OBJECT", - "description": "Indicates this type is an input object. `inputFields` is a valid field.", - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "LIST", - "description": "Indicates this type is a list. `ofType` is a valid field.", - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "NON_NULL", - "description": "Indicates this type is a non-null. `ofType` is a valid field.", - "isDeprecated": false, - "deprecationReason": null - } - ], - "possibleTypes": null - }, - { - "kind": "OBJECT", - "name": "__Field", - "description": "Object and Interface types are described by a list of Fields, each of which has a name, potentially a list of arguments, and a return type.", - "fields": [ - { - "name": "name", - "description": null, - "args": [], - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "SCALAR", - "name": "String", - "ofType": null - } - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "description", - "description": null, - "args": [], - "type": { - "kind": "SCALAR", - "name": "String", - "ofType": null - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "args", - "description": null, - "args": [], - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "LIST", - "name": null, - "ofType": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "OBJECT", - "name": "__InputValue", - "ofType": null - } - } - } - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "type", - "description": null, - "args": [], - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "OBJECT", - "name": "__Type", - "ofType": null - } - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "isDeprecated", - "description": null, - "args": [], - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "SCALAR", - "name": "Boolean", - "ofType": null - } - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "deprecationReason", - "description": null, - "args": [], - "type": { - "kind": "SCALAR", - "name": "String", - "ofType": null - }, - "isDeprecated": false, - "deprecationReason": null - } - ], - "inputFields": null, - "interfaces": [], - "enumValues": null, - "possibleTypes": null - }, - { - "kind": "OBJECT", - "name": "__InputValue", - "description": "Arguments provided to Fields or Directives and the input fields of an InputObject are represented as Input Values which describe their type and optionally a default value.", - "fields": [ - { - "name": "name", - "description": null, - "args": [], - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "SCALAR", - "name": "String", - "ofType": null - } - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "description", - "description": null, - "args": [], - "type": { - "kind": "SCALAR", - "name": "String", - "ofType": null - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "type", - "description": null, - "args": [], - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "OBJECT", - "name": "__Type", - "ofType": null - } - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "defaultValue", - "description": "A GraphQL-formatted string representing the default value for this input value.", - "args": [], - "type": { - "kind": "SCALAR", - "name": "String", - "ofType": null - }, - "isDeprecated": false, - "deprecationReason": null - } - ], - "inputFields": null, - "interfaces": [], - "enumValues": null, - "possibleTypes": null - }, - { - "kind": "OBJECT", - "name": "__EnumValue", - "description": "One possible value for a given Enum. Enum values are unique values, not a placeholder for a string or numeric value. However an Enum value is returned in a JSON response as a string.", - "fields": [ - { - "name": "name", - "description": null, - "args": [], - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "SCALAR", - "name": "String", - "ofType": null - } - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "description", - "description": null, - "args": [], - "type": { - "kind": "SCALAR", - "name": "String", - "ofType": null - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "isDeprecated", - "description": null, - "args": [], - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "SCALAR", - "name": "Boolean", - "ofType": null - } - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "deprecationReason", - "description": null, - "args": [], - "type": { - "kind": "SCALAR", - "name": "String", - "ofType": null - }, - "isDeprecated": false, - "deprecationReason": null - } - ], - "inputFields": null, - "interfaces": [], - "enumValues": null, - "possibleTypes": null - }, - { - "kind": "OBJECT", - "name": "__Directive", - "description": "A Directive provides a way to describe alternate runtime execution and type validation behavior in a GraphQL document.\n\nIn some cases, you need to provide options to alter GraphQL's execution behavior in ways field arguments will not suffice, such as conditionally including or skipping a field. Directives provide this by describing additional information to the executor.", - "fields": [ - { - "name": "name", - "description": null, - "args": [], - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "SCALAR", - "name": "String", - "ofType": null - } - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "description", - "description": null, - "args": [], - "type": { - "kind": "SCALAR", - "name": "String", - "ofType": null - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "locations", - "description": null, - "args": [], - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "LIST", - "name": null, - "ofType": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "ENUM", - "name": "__DirectiveLocation", - "ofType": null - } - } - } - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "args", - "description": null, - "args": [], - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "LIST", - "name": null, - "ofType": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "OBJECT", - "name": "__InputValue", - "ofType": null - } - } - } - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "onOperation", - "description": null, - "args": [], - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "SCALAR", - "name": "Boolean", - "ofType": null - } - }, - "isDeprecated": true, - "deprecationReason": "Use `locations`." - }, - { - "name": "onFragment", - "description": null, - "args": [], - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "SCALAR", - "name": "Boolean", - "ofType": null - } - }, - "isDeprecated": true, - "deprecationReason": "Use `locations`." - }, - { - "name": "onField", - "description": null, - "args": [], - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "SCALAR", - "name": "Boolean", - "ofType": null - } - }, - "isDeprecated": true, - "deprecationReason": "Use `locations`." - } - ], - "inputFields": null, - "interfaces": [], - "enumValues": null, - "possibleTypes": null - }, - { - "kind": "ENUM", - "name": "__DirectiveLocation", - "description": "A Directive can be adjacent to many parts of the GraphQL language, a __DirectiveLocation describes one such possible adjacencies.", - "fields": null, - "inputFields": null, - "interfaces": null, - "enumValues": [ - { - "name": "QUERY", - "description": "Location adjacent to a query operation.", - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "MUTATION", - "description": "Location adjacent to a mutation operation.", - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "SUBSCRIPTION", - "description": "Location adjacent to a subscription operation.", - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "FIELD", - "description": "Location adjacent to a field.", - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "FRAGMENT_DEFINITION", - "description": "Location adjacent to a fragment definition.", - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "FRAGMENT_SPREAD", - "description": "Location adjacent to a fragment spread.", - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "INLINE_FRAGMENT", - "description": "Location adjacent to an inline fragment.", - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "SCHEMA", - "description": "Location adjacent to a schema definition.", - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "SCALAR", - "description": "Location adjacent to a scalar definition.", - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "OBJECT", - "description": "Location adjacent to an object type definition.", - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "FIELD_DEFINITION", - "description": "Location adjacent to a field definition.", - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "ARGUMENT_DEFINITION", - "description": "Location adjacent to an argument definition.", - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "INTERFACE", - "description": "Location adjacent to an interface definition.", - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "UNION", - "description": "Location adjacent to a union definition.", - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "ENUM", - "description": "Location adjacent to an enum definition.", - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "ENUM_VALUE", - "description": "Location adjacent to an enum value definition.", - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "INPUT_OBJECT", - "description": "Location adjacent to an input object type definition.", - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "INPUT_FIELD_DEFINITION", - "description": "Location adjacent to an input object field definition.", - "isDeprecated": false, - "deprecationReason": null - } - ], - "possibleTypes": null - }, - { - "kind": "OBJECT", - "name": "DefaultUserProfile", - "description": "DefaultUserProfile is a fallback if the type of UserProfile can't be\ndetermined (like if it was from a plugin that was removed).", - "fields": [ - { - "name": "id", - "description": "The id is an identifier for the user profile (email, facebook id, etc)", - "args": [], - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "SCALAR", - "name": "String", - "ofType": null - } - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "provider", - "description": "name of the provider attached to the authentication mode", - "args": [], - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "SCALAR", - "name": "String", - "ofType": null - } - }, - "isDeprecated": false, - "deprecationReason": null - } - ], - "inputFields": null, - "interfaces": [ - { - "kind": "INTERFACE", - "name": "UserProfile", - "ofType": null - } - ], - "enumValues": null, - "possibleTypes": null - }, - { - "kind": "OBJECT", - "name": "LocalUserProfile", - "description": "LocalUserProfile is for a User who has an authentication profile linked to\ntheir email address.", - "fields": [ - { - "name": "id", - "description": "id is the User's email address.", - "args": [], - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "SCALAR", - "name": "String", - "ofType": null - } - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "provider", - "description": "name of the provider attached to the authentication mode, in this case,\n'local'.", - "args": [], - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "SCALAR", - "name": "String", - "ofType": null - } - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "confirmedAt", - "description": "confirmedAt is the Date that the user had their email address confirmed,\nwhich is null if it has not been verified.", - "args": [], - "type": { - "kind": "SCALAR", - "name": "Date", - "ofType": null - }, - "isDeprecated": false, - "deprecationReason": null - } - ], - "inputFields": null, - "interfaces": [ - { - "kind": "INTERFACE", - "name": "UserProfile", - "ofType": null - } - ], - "enumValues": null, - "possibleTypes": null - }, - { - "kind": "OBJECT", - "name": "DefaultAction", - "description": "DefaultAction is the Action provided for undefined types.", - "fields": [ - { - "name": "id", - "description": "The ID of the action.", - "args": [], - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "SCALAR", - "name": "ID", - "ofType": null - } - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "user", - "description": "The author of the action.", - "args": [], - "type": { - "kind": "OBJECT", - "name": "User", - "ofType": null - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "updated_at", - "description": "The time when the Action was updated.", - "args": [], - "type": { - "kind": "SCALAR", - "name": "Date", - "ofType": null - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "created_at", - "description": "The time when the Action was created.", - "args": [], - "type": { - "kind": "SCALAR", - "name": "Date", - "ofType": null - }, - "isDeprecated": false, - "deprecationReason": null - } - ], - "inputFields": null, - "interfaces": [ - { - "kind": "INTERFACE", - "name": "Action", - "ofType": null - } - ], - "enumValues": null, - "possibleTypes": null - }, - { - "kind": "OBJECT", - "name": "DefaultActionSummary", - "description": "DefaultActionSummary is the ActionSummary provided for undefined types.", - "fields": [ - { - "name": "count", - "description": "The count of actions with this group.", - "args": [], - "type": { - "kind": "SCALAR", - "name": "Int", - "ofType": null - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "current_user", - "description": "The current user's action.", - "args": [], - "type": { - "kind": "INTERFACE", - "name": "Action", - "ofType": null - }, - "isDeprecated": false, - "deprecationReason": null - } - ], - "inputFields": null, - "interfaces": [ - { - "kind": "INTERFACE", - "name": "ActionSummary", - "ofType": null - } - ], - "enumValues": null, - "possibleTypes": null - }, - { - "kind": "OBJECT", - "name": "DefaultAssetActionSummary", - "description": "DefaultAssetActionSummary is the AssetActionSummary provided for undefined types.", - "fields": [ - { - "name": "actionCount", - "description": "Number of actions associated with actionable types on this this Asset.", - "args": [], - "type": { - "kind": "SCALAR", - "name": "Int", - "ofType": null - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "actionableItemCount", - "description": "Number of unique actionable types that are referenced by the actions.", - "args": [], - "type": { - "kind": "SCALAR", - "name": "Int", - "ofType": null - }, - "isDeprecated": false, - "deprecationReason": null - } - ], - "inputFields": null, - "interfaces": [ - { - "kind": "INTERFACE", - "name": "AssetActionSummary", - "ofType": null - } - ], - "enumValues": null, - "possibleTypes": null - }, - { - "kind": "OBJECT", - "name": "FlagAssetActionSummary", - "description": "A summary of counts related to all the Flags on an Asset.", - "fields": [ - { - "name": "actionCount", - "description": "Number of flags associated with actionable types on this this Asset.", - "args": [], - "type": { - "kind": "SCALAR", - "name": "Int", - "ofType": null - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "actionableItemCount", - "description": "Number of unique actionable types that are referenced by the flags.", - "args": [], - "type": { - "kind": "SCALAR", - "name": "Int", - "ofType": null - }, - "isDeprecated": false, - "deprecationReason": null - } - ], - "inputFields": null, - "interfaces": [ - { - "kind": "INTERFACE", - "name": "AssetActionSummary", - "ofType": null - } - ], - "enumValues": null, - "possibleTypes": null - }, - { - "kind": "OBJECT", - "name": "FlagActionSummary", - "description": "Summary for Flag Action with a a unique reason.", - "fields": [ - { - "name": "count", - "description": "The total count of flags with this reason.", - "args": [], - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "SCALAR", - "name": "Int", - "ofType": null - } - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "reason", - "description": "The reason for which the Flag Action was created.", - "args": [], - "type": { - "kind": "SCALAR", - "name": "String", - "ofType": null - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "current_user", - "description": "The flag by the current user against the parent entity with this reason.", - "args": [], - "type": { - "kind": "OBJECT", - "name": "FlagAction", - "ofType": null - }, - "isDeprecated": false, - "deprecationReason": null - } - ], - "inputFields": null, - "interfaces": [ - { - "kind": "INTERFACE", - "name": "ActionSummary", - "ofType": null - } - ], - "enumValues": null, - "possibleTypes": null - }, - { - "kind": "OBJECT", - "name": "DontAgreeActionSummary", - "description": "Summary for Don't Agree Action with a a unique reason.", - "fields": [ - { - "name": "count", - "description": "The total count of flags with this reason.", - "args": [], - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "SCALAR", - "name": "Int", - "ofType": null - } - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "current_user", - "description": "The don't agree action by the current user against the parent entity with this reason.", - "args": [], - "type": { - "kind": "OBJECT", - "name": "DontAgreeAction", - "ofType": null - }, - "isDeprecated": false, - "deprecationReason": null - } - ], - "inputFields": null, - "interfaces": [ - { - "kind": "INTERFACE", - "name": "ActionSummary", - "ofType": null - } - ], - "enumValues": null, - "possibleTypes": null - }, - { - "kind": "OBJECT", - "name": "GenericUserError", - "description": "A generic error not related to validation reasons.", - "fields": [ - { - "name": "translation_key", - "description": "Translation key relating to a translatable string containing details to be\ndisplayed to the end user.", - "args": [], - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "SCALAR", - "name": "String", - "ofType": null - } - }, - "isDeprecated": false, - "deprecationReason": null - } - ], - "inputFields": null, - "interfaces": [ - { - "kind": "INTERFACE", - "name": "UserError", - "ofType": null - } - ], - "enumValues": null, - "possibleTypes": null - }, - { - "kind": "OBJECT", - "name": "ValidationUserError", - "description": "A validation error that affects the input.", - "fields": [ - { - "name": "translation_key", - "description": "Translation key relating to a translatable string containing details to be\ndisplayed to the end user.", - "args": [], - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "SCALAR", - "name": "String", - "ofType": null - } - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "field_name", - "description": "The field in question that caused the error.", - "args": [], - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "SCALAR", - "name": "String", - "ofType": null - } - }, - "isDeprecated": false, - "deprecationReason": null - } - ], - "inputFields": null, - "interfaces": [ - { - "kind": "INTERFACE", - "name": "UserError", - "ofType": null - } - ], - "enumValues": null, - "possibleTypes": null - }, - { - "kind": "ENUM", - "name": "ASSET_METRICS_SORT", - "description": "", - "fields": null, - "inputFields": null, - "interfaces": null, - "enumValues": [ - { - "name": "RESPECT", - "description": "Represents a RespectAction.", - "isDeprecated": false, - "deprecationReason": null - } - ], - "possibleTypes": null - }, - { - "kind": "OBJECT", - "name": "RespectActionSummary", - "description": "", - "fields": [ - { - "name": "count", - "description": "The count of actions with this group.", - "args": [], - "type": { - "kind": "SCALAR", - "name": "Int", - "ofType": null - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "current_user", - "description": "The current user's action.", - "args": [], - "type": { - "kind": "OBJECT", - "name": "RespectAction", - "ofType": null - }, - "isDeprecated": false, - "deprecationReason": null - } - ], - "inputFields": null, - "interfaces": [ - { - "kind": "INTERFACE", - "name": "ActionSummary", - "ofType": null - } - ], - "enumValues": null, - "possibleTypes": null - }, - { - "kind": "OBJECT", - "name": "RespectAssetActionSummary", - "description": "A summary of counts related to all the Respects on an Asset.", - "fields": [ - { - "name": "actionCount", - "description": "Number of respects associated with actionable types on this this Asset.", - "args": [], - "type": { - "kind": "SCALAR", - "name": "Int", - "ofType": null - }, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "actionableItemCount", - "description": "Number of unique actionable types that are referenced by the respects.", - "args": [], - "type": { - "kind": "SCALAR", - "name": "Int", - "ofType": null - }, - "isDeprecated": false, - "deprecationReason": null - } - ], - "inputFields": null, - "interfaces": [ - { - "kind": "INTERFACE", - "name": "AssetActionSummary", - "ofType": null - } - ], - "enumValues": null, - "possibleTypes": null - } - ], - "directives": [ - { - "name": "skip", - "description": "Directs the executor to skip this field or fragment when the `if` argument is true.", - "locations": [ - "FIELD", - "FRAGMENT_SPREAD", - "INLINE_FRAGMENT" - ], - "args": [ - { - "name": "if", - "description": "Skipped when true.", - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "SCALAR", - "name": "Boolean", - "ofType": null - } - }, - "defaultValue": null - } - ] - }, - { - "name": "include", - "description": "Directs the executor to include this field or fragment only when the `if` argument is true.", - "locations": [ - "FIELD", - "FRAGMENT_SPREAD", - "INLINE_FRAGMENT" - ], - "args": [ - { - "name": "if", - "description": "Included when true.", - "type": { - "kind": "NON_NULL", - "name": null, - "ofType": { - "kind": "SCALAR", - "name": "Boolean", - "ofType": null - } - }, - "defaultValue": null - } - ] - }, - { - "name": "deprecated", - "description": "Marks an element of a GraphQL schema as no longer supported.", - "locations": [ - "FIELD_DEFINITION", - "ENUM_VALUE" - ], - "args": [ - { - "name": "reason", - "description": "Explains why this element was deprecated, usually also including a suggestion for how to access supported similar data. Formatted in [Markdown](https://daringfireball.net/projects/markdown/).", - "type": { - "kind": "SCALAR", - "name": "String", - "ofType": null - }, - "defaultValue": "\"No longer supported\"" - } - ] - } - ] - } -} \ No newline at end of file diff --git a/plugins/talk-plugin-auth-checkbox/client/components/TermsAndConditionsField.js b/plugins/talk-plugin-auth-checkbox/client/components/TermsAndConditionsField.js index 25f97dc09..aa9acb780 100644 --- a/plugins/talk-plugin-auth-checkbox/client/components/TermsAndConditionsField.js +++ b/plugins/talk-plugin-auth-checkbox/client/components/TermsAndConditionsField.js @@ -4,8 +4,6 @@ import { t } from 'plugin-api/beta/client/services'; import styles from './TermsAndConditionsField.css'; import cn from 'classnames'; -const pluginName = 'talk-plugin-auth-checkbox'; - const TermsLink = () => ( { + this.setState({ checked }); if (checked) { - this.setState(() => ({ checked })); - this.props.indicateBlockerOff(pluginName); + this.props.indicateBlockerResolved(); } else { - this.setState(() => ({ checked })); - this.props.indicateBlockerOn(pluginName); + this.props.indicateBlocker(); } }; diff --git a/plugins/talk-plugin-auth/client/login/components/SignUp.js b/plugins/talk-plugin-auth/client/login/components/SignUp.js index 3dc648de6..dfc2ced1e 100644 --- a/plugins/talk-plugin-auth/client/login/components/SignUp.js +++ b/plugins/talk-plugin-auth/client/login/components/SignUp.js @@ -29,6 +29,15 @@ class SignUp extends React.Component { this.props.onSubmit(); }; + childFactory = el => { + const key = el.key; + const props = { + indicateBlocker: () => this.props.indicateBlocker(key), + indicateBlockerResolved: () => this.props.indicateBlockerResolved(key), + }; + return React.cloneElement(el, props); + }; + render() { const { username, @@ -43,17 +52,9 @@ class SignUp extends React.Component { errorMessage, requireEmailConfirmation, success, - indicateBlockerOn, - indicateBlockerOff, - hasBlockers, + blocked, } = this.props; - const slotPassthrough = { - indicateBlockerOn, - indicateBlockerOff, - hasBlockers, - }; - return (
@@ -115,7 +116,7 @@ class SignUp extends React.Component { />
@@ -176,9 +177,9 @@ SignUp.propTypes = { errorMessage: PropTypes.string, requireEmailConfirmation: PropTypes.bool.isRequired, success: PropTypes.bool.isRequired, - hasBlockers: PropTypes.array.isRequired, - indicateBlockerOn: PropTypes.func.isRequired, - indicateBlockerOff: PropTypes.func.isRequired, + blocked: PropTypes.bool.isRequired, + indicateBlocker: PropTypes.func.isRequired, + indicateBlockerResolved: PropTypes.func.isRequired, }; export default SignUp; diff --git a/plugins/talk-plugin-auth/client/login/containers/SignUp.js b/plugins/talk-plugin-auth/client/login/containers/SignUp.js index 2b2a7d979..6b6116968 100644 --- a/plugins/talk-plugin-auth/client/login/containers/SignUp.js +++ b/plugins/talk-plugin-auth/client/login/containers/SignUp.js @@ -16,17 +16,17 @@ class SignUpContainer extends Component { emailError: null, passwordError: null, passwordRepeatError: null, - hasBlockers: [], + blockers: [], }; - indicateBlockerOn = plugin => + indicateBlocker = key => this.setState(state => ({ - hasBlockers: state.hasBlockers.concat(plugin), + blockers: state.blockers.concat(key), })); - indicateBlockerOff = plugin => + indicateBlockerResolved = key => this.setState(state => ({ - hasBlockers: state.hasBlockers.filter(i => i !== plugin), + blockers: state.blockers.filter(i => i !== key), })); validate = data => { @@ -87,9 +87,9 @@ class SignUpContainer extends Component { render() { return ( { - const pluginName = el.type.talkPluginName; + const key = el.key; const props = { - indicateOn: () => this.props.indicateOn(pluginName), - indicateOff: () => this.props.indicateOff(pluginName), + indicateOn: () => this.props.indicateOn(key), + indicateOff: () => this.props.indicateOff(key), }; return React.cloneElement(el, props); }; diff --git a/plugins/talk-plugin-notifications/client/containers/Settings.js b/plugins/talk-plugin-notifications/client/containers/Settings.js index 5862650ec..a3d9c12e0 100644 --- a/plugins/talk-plugin-notifications/client/containers/Settings.js +++ b/plugins/talk-plugin-notifications/client/containers/Settings.js @@ -20,14 +20,14 @@ class SettingsContainer extends React.Component { turnOffInput: {}, }; - indicateOn = plugin => + indicateOn = key => this.setState(state => ({ - hasNotifications: state.hasNotifications.concat(plugin), + hasNotifications: state.hasNotifications.concat(key), })); - indicateOff = plugin => + indicateOff = key => this.setState(state => ({ - hasNotifications: state.hasNotifications.filter(i => i !== plugin), + hasNotifications: state.hasNotifications.filter(i => i !== key), })); setTurnOffInputFragment = fragment => From f32c9fa6b1fcbbd8f08ec8ec24f2a89ae8f95945 Mon Sep 17 00:00:00 2001 From: Chi Vinh Le Date: Thu, 29 Mar 2018 22:51:40 +0200 Subject: [PATCH 17/84] Remaining rename --- plugins/talk-plugin-auth/client/login/containers/SignUp.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/talk-plugin-auth/client/login/containers/SignUp.js b/plugins/talk-plugin-auth/client/login/containers/SignUp.js index 6b6116968..f7fc6f2d2 100644 --- a/plugins/talk-plugin-auth/client/login/containers/SignUp.js +++ b/plugins/talk-plugin-auth/client/login/containers/SignUp.js @@ -59,7 +59,7 @@ class SignUpContainer extends Component { passwordRepeat: this.state.passwordRepeat, }; - if (this.validate(data) && !this.state.hasBlockers.length) { + if (this.validate(data) && !this.state.blockers.length) { this.props.signUp(data); } }; From ec117a67aa2527ab6756f3f5519dbb17ea4818b5 Mon Sep 17 00:00:00 2001 From: okbel Date: Fri, 30 Mar 2018 10:35:49 -0300 Subject: [PATCH 18/84] renaming --- .../components/TermsAndConditionsField.js | 20 +++++++++---------- .../client/translations.yml | 8 ++++---- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/plugins/talk-plugin-auth-checkbox/client/components/TermsAndConditionsField.js b/plugins/talk-plugin-auth-checkbox/client/components/TermsAndConditionsField.js index dec0d86ce..ddec0609d 100644 --- a/plugins/talk-plugin-auth-checkbox/client/components/TermsAndConditionsField.js +++ b/plugins/talk-plugin-auth-checkbox/client/components/TermsAndConditionsField.js @@ -9,20 +9,20 @@ const pluginName = 'talk-plugin-auth-checkbox'; const TermsLink = () => ( - {t('talk-plugin-health-report.terms')} + {t('talk-plugin-auth-checkbox.terms')} ); const PrivacyLink = () => ( - {t('talk-plugin-health-report.privacy-policy')} + {t('talk-plugin-auth-checkbox.privacy-policy')} ); @@ -49,12 +49,12 @@ class TermsAndConditionsField extends React.Component {
@@ -62,14 +62,14 @@ class TermsAndConditionsField extends React.Component { id={this.id} className={cn( styles.textLabel, - 'talk-plugin-health-report-text-label' + 'talk-plugin-auth-checkbox-text-label' )} > - {t('talk-plugin-health-report.copy')} + {t('talk-plugin-auth-checkbox.copy')} - {t('talk-plugin-health-report.and')} + {t('talk-plugin-auth-checkbox.and')} - {t('talk-plugin-health-report.from')} + {t('talk-plugin-auth-checkbox.from')}
); diff --git a/plugins/talk-plugin-auth-checkbox/client/translations.yml b/plugins/talk-plugin-auth-checkbox/client/translations.yml index 66bf7379c..ae50146d9 100644 --- a/plugins/talk-plugin-auth-checkbox/client/translations.yml +++ b/plugins/talk-plugin-auth-checkbox/client/translations.yml @@ -1,5 +1,5 @@ en: - talk-plugin-health-report: + talk-plugin-auth-checkbox: copy: I agree to the Mozilla and: and terms: Terms of Service @@ -8,7 +8,7 @@ en: terms-link: https://www.mozilla.org/en-US/about/legal/terms/mozilla/ privacy-policy-link: https://www.mozilla.org/en-US/privacy/websites/ es: - talk-plugin-health-report: + talk-plugin-auth-checkbox: copy: Estoy de acuerdo con and: y terms: los Terminos y Condiciones @@ -17,7 +17,7 @@ es: terms-link: https://www.mozilla.org/es-ES/about/legal/terms/mozilla/ privacy-policy-link: https://www.mozilla.org/es-ES/privacy/websites/ fr: - talk-plugin-health-report: + talk-plugin-auth-checkbox: copy: J'accepte and: et terms: les conditions d'utilisation @@ -26,7 +26,7 @@ fr: terms-link: https://www.mozilla.org/fr/about/legal/terms/mozilla/ privacy-policy-link: https://www.mozilla.org/fr/privacy/websites/ de: - talk-plugin-health-report: + talk-plugin-auth-checkbox: copy: Ich stimme and: und terms: den Allgemeinen Geschäftsbedingungen From 9cc089b3b2777247eb7ad3ca2efff5dfd47459c7 Mon Sep 17 00:00:00 2001 From: okbel Date: Fri, 30 Mar 2018 10:37:42 -0300 Subject: [PATCH 19/84] renaming slot :D --- plugins/talk-plugin-auth-checkbox/client/index.js | 2 +- plugins/talk-plugin-auth/client/login/components/SignUp.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/talk-plugin-auth-checkbox/client/index.js b/plugins/talk-plugin-auth-checkbox/client/index.js index d2aaaa1cb..9a30686c6 100644 --- a/plugins/talk-plugin-auth-checkbox/client/index.js +++ b/plugins/talk-plugin-auth-checkbox/client/index.js @@ -3,7 +3,7 @@ import translations from './translations.yml'; export default { slots: { - 'talkPluginAuth-formField': [TermsAndConditionsField], + 'talkPluginAuth.formField': [TermsAndConditionsField], }, translations, }; diff --git a/plugins/talk-plugin-auth/client/login/components/SignUp.js b/plugins/talk-plugin-auth/client/login/components/SignUp.js index dfc2ced1e..cc49e7391 100644 --- a/plugins/talk-plugin-auth/client/login/components/SignUp.js +++ b/plugins/talk-plugin-auth/client/login/components/SignUp.js @@ -115,7 +115,7 @@ class SignUp extends React.Component { minLength="8" />
From 06b64ad2509128cf7d1d99fe560227408b200d4e Mon Sep 17 00:00:00 2001 From: okbel Date: Fri, 30 Mar 2018 10:55:15 -0300 Subject: [PATCH 20/84] routes --- client/coral-admin/src/AppRouter.js | 15 ++++++++++-- .../routes/Configure/components/Configure.js | 24 ++----------------- 2 files changed, 15 insertions(+), 24 deletions(-) diff --git a/client/coral-admin/src/AppRouter.js b/client/coral-admin/src/AppRouter.js index 9d68d592b..92cc80d1d 100644 --- a/client/coral-admin/src/AppRouter.js +++ b/client/coral-admin/src/AppRouter.js @@ -2,10 +2,15 @@ import React from 'react'; import PropTypes from 'prop-types'; import { Router, Route, IndexRedirect, IndexRoute } from 'react-router'; -import Configure from 'routes/Configure'; import Install from 'routes/Install'; import Stories from 'routes/Stories'; import Community from 'routes/Community'; + +import Configure from 'routes/Configure'; +import StreamSettings from './routes/Configure/containers/StreamSettings'; +import ModerationSettings from './routes/Configure/containers/ModerationSettings'; +import TechSettings from './routes/Configure/containers/TechSettings'; + import { ModerationLayout, Moderation } from 'routes/Moderation'; import Layout from 'containers/Layout'; @@ -15,7 +20,13 @@ const routes = ( - + + + + + + + {/* Community Routes */} diff --git a/client/coral-admin/src/routes/Configure/components/Configure.js b/client/coral-admin/src/routes/Configure/components/Configure.js index efd428378..a988bcec5 100644 --- a/client/coral-admin/src/routes/Configure/components/Configure.js +++ b/client/coral-admin/src/routes/Configure/components/Configure.js @@ -2,26 +2,11 @@ import React, { Component } from 'react'; import { Button, List, Item } from 'coral-ui'; import styles from './Configure.css'; -import StreamSettings from '../containers/StreamSettings'; -import ModerationSettings from '../containers/ModerationSettings'; -import TechSettings from '../containers/TechSettings'; import t from 'coral-framework/services/i18n'; import { can } from 'coral-framework/services/perms'; import PropTypes from 'prop-types'; export default class Configure extends Component { - getSectionComponent(section) { - switch (section) { - case 'stream': - return StreamSettings; - case 'moderation': - return ModerationSettings; - case 'tech': - return TechSettings; - } - throw new Error(`Unknown section ${section}`); - } - render() { const { currentUser, @@ -30,7 +15,6 @@ export default class Configure extends Component { setActiveSection, activeSection, } = this.props; - const SectionComponent = this.getSectionComponent(activeSection); if (!can(currentUser, 'UPDATE_CONFIG')) { return ( @@ -73,12 +57,7 @@ export default class Configure extends Component { )}
-
- -
+
{this.props.children}
); } @@ -92,4 +71,5 @@ Configure.propTypes = { canSave: PropTypes.bool.isRequired, setActiveSection: PropTypes.func.isRequired, activeSection: PropTypes.string.isRequired, + children: PropTypes.node.isRequired, }; From 6982509233e52c14057fb12ecfc45c05d35a9cb9 Mon Sep 17 00:00:00 2001 From: okbel Date: Fri, 30 Mar 2018 12:00:50 -0300 Subject: [PATCH 21/84] router and state lifted --- client/coral-admin/src/AppRouter.js | 1 + client/coral-admin/src/actions/configure.js | 8 +++ client/coral-admin/src/constants/configure.js | 3 ++ client/coral-admin/src/reducers/configure.js | 15 ++++++ .../routes/Configure/components/Configure.js | 49 +++++++++++++------ .../routes/Configure/containers/Configure.js | 42 +++++++++++++--- 6 files changed, 94 insertions(+), 24 deletions(-) diff --git a/client/coral-admin/src/AppRouter.js b/client/coral-admin/src/AppRouter.js index 92cc80d1d..28255aeb5 100644 --- a/client/coral-admin/src/AppRouter.js +++ b/client/coral-admin/src/AppRouter.js @@ -25,6 +25,7 @@ const routes = ( + diff --git a/client/coral-admin/src/actions/configure.js b/client/coral-admin/src/actions/configure.js index acc30be1b..0f82832e0 100644 --- a/client/coral-admin/src/actions/configure.js +++ b/client/coral-admin/src/actions/configure.js @@ -11,3 +11,11 @@ export const clearPending = () => { export const setActiveSection = section => { return { type: actions.SET_ACTIVE_SECTION, section }; }; + +export const showSaveDialog = () => { + return { type: actions.SHOW_SAVE_DIALOG }; +}; + +export const hideSaveDialog = () => { + return { type: actions.HIDE_SAVE_DIALOG }; +}; diff --git a/client/coral-admin/src/constants/configure.js b/client/coral-admin/src/constants/configure.js index 05673b5aa..ed5f74578 100644 --- a/client/coral-admin/src/constants/configure.js +++ b/client/coral-admin/src/constants/configure.js @@ -3,3 +3,6 @@ const prefix = 'TALK_ADMIN_CONFIGURE'; export const UPDATE_PENDING = `${prefix}_UPDATE_PENDING`; export const CLEAR_PENDING = `${prefix}_CLEAR_PENDING`; export const SET_ACTIVE_SECTION = `${prefix}_SET_ACTIVE_SECTION`; + +export const SHOW_SAVE_DIALOG = `${prefix}_SHOW_SAVE_DIALOG`; +export const HIDE_SAVE_DIALOG = `${prefix}_HIDE_SAVE_DIALOG`; diff --git a/client/coral-admin/src/reducers/configure.js b/client/coral-admin/src/reducers/configure.js index 9809b0fa9..335cc4ce0 100644 --- a/client/coral-admin/src/reducers/configure.js +++ b/client/coral-admin/src/reducers/configure.js @@ -7,10 +7,23 @@ const initialState = { pending: {}, errors: {}, activeSection: 'stream', + saveDialog: false, }; export default function configure(state = initialState, action) { switch (action.type) { + case actions.SHOW_SAVE_DIALOG: { + return { + ...state, + saveDialog: true, + }; + } + case actions.HIDE_SAVE_DIALOG: { + return { + ...state, + saveDialog: false, + }; + } case actions.UPDATE_PENDING: { let next = state; if (action.updater) { @@ -45,6 +58,8 @@ export default function configure(state = initialState, action) { ...state, activeSection: action.section, }; + default: + return state; } return state; } diff --git a/client/coral-admin/src/routes/Configure/components/Configure.js b/client/coral-admin/src/routes/Configure/components/Configure.js index a988bcec5..4d9f0ff34 100644 --- a/client/coral-admin/src/routes/Configure/components/Configure.js +++ b/client/coral-admin/src/routes/Configure/components/Configure.js @@ -1,20 +1,14 @@ -import React, { Component } from 'react'; - -import { Button, List, Item } from 'coral-ui'; -import styles from './Configure.css'; -import t from 'coral-framework/services/i18n'; -import { can } from 'coral-framework/services/perms'; +import React from 'react'; import PropTypes from 'prop-types'; +import cn from 'classnames'; +import t from 'coral-framework/services/i18n'; +import { Button, List, Item, Dialog } from 'coral-ui'; +import { can } from 'coral-framework/services/perms'; +import styles from './Configure.css'; -export default class Configure extends Component { +class Configure extends React.Component { render() { - const { - currentUser, - canSave, - savePending, - setActiveSection, - activeSection, - } = this.props; + const { canSave, currentUser, root, savePending, settings } = this.props; if (!can(currentUser, 'UPDATE_CONFIG')) { return ( @@ -25,10 +19,27 @@ export default class Configure extends Component { ); } + const passProps = { + root, + settings, + }; + return (
+ + Are you sure? +
- + {t('configure.stream_settings')} @@ -57,7 +68,9 @@ export default class Configure extends Component { )}
-
{this.props.children}
+
+ {React.cloneElement(this.props.children, passProps)} +
); } @@ -72,4 +85,8 @@ Configure.propTypes = { setActiveSection: PropTypes.func.isRequired, activeSection: PropTypes.string.isRequired, children: PropTypes.node.isRequired, + saveDialog: PropTypes.bool, + hideSaveDialog: PropTypes.func.isRequired, }; + +export default Configure; diff --git a/client/coral-admin/src/routes/Configure/containers/Configure.js b/client/coral-admin/src/routes/Configure/containers/Configure.js index ce6ea0627..21f4b98e2 100644 --- a/client/coral-admin/src/routes/Configure/containers/Configure.js +++ b/client/coral-admin/src/routes/Configure/containers/Configure.js @@ -1,4 +1,4 @@ -import React, { Component } from 'react'; +import React from 'react'; import { connect } from 'react-redux'; import { bindActionCreators } from 'redux'; import { compose, gql } from 'react-apollo'; @@ -10,15 +10,28 @@ import { getDefinitionName } from 'coral-framework/utils'; import StreamSettings from './StreamSettings'; import TechSettings from './TechSettings'; import ModerationSettings from './ModerationSettings'; -import { clearPending, setActiveSection } from '../../../actions/configure'; +import { + clearPending, + setActiveSection, + showSaveDialog, + hideSaveDialog, +} from '../../../actions/configure'; import Configure from '../components/Configure'; +import { withRouter } from 'react-router'; -class ConfigureContainer extends Component { +class ConfigureContainer extends React.Component { savePending = async () => { await this.props.updateSettings(this.props.pending); this.props.clearPending(); }; + setActiveSection = section => { + // Check if pending + console.log('pending', this.props.pending); + this.props.setActiveSection(section); + this.props.router.push(`/admin/configure/${section}`); + }; + render() { if (this.props.data.error) { return
{this.props.data.error.message}
; @@ -30,14 +43,18 @@ class ConfigureContainer extends Component { return ( + > + {this.props.children} + ); } } @@ -74,6 +91,7 @@ const mapStateToProps = state => ({ pending: state.configure.pending, canSave: state.configure.canSave, activeSection: state.configure.activeSection, + saveDialog: state.configure.saveDialog, }); const mapDispatchToProps = dispatch => @@ -81,11 +99,14 @@ const mapDispatchToProps = dispatch => { clearPending, setActiveSection, + showSaveDialog, + hideSaveDialog, }, dispatch ); export default compose( + withRouter, connect(mapStateToProps, mapDispatchToProps), withUpdateSettings, withConfigureQuery, @@ -93,14 +114,19 @@ export default compose( )(ConfigureContainer); ConfigureContainer.propTypes = { + activeSection: PropTypes.string, updateSettings: PropTypes.func.isRequired, clearPending: PropTypes.func.isRequired, setActiveSection: PropTypes.func.isRequired, + showSaveDialog: PropTypes.func.isRequired, + hideSaveDialog: PropTypes.func.isRequired, + saveDialog: PropTypes.bool.isRequired, currentUser: PropTypes.object.isRequired, data: PropTypes.object.isRequired, root: PropTypes.object.isRequired, canSave: PropTypes.bool.isRequired, pending: PropTypes.object.isRequired, mergedSettings: PropTypes.object.isRequired, - activeSection: PropTypes.string.isRequired, + children: PropTypes.node.isRequired, + router: PropTypes.object, }; From 394ebb8f2db32ad4d67864828c1cf12fed062177 Mon Sep 17 00:00:00 2001 From: okbel Date: Fri, 30 Mar 2018 12:18:57 -0300 Subject: [PATCH 22/84] Save changes / discard changes dialohg --- .../routes/Configure/components/Configure.js | 6 +- .../routes/Configure/containers/Configure.js | 61 +++++++++++++------ 2 files changed, 49 insertions(+), 18 deletions(-) diff --git a/client/coral-admin/src/routes/Configure/components/Configure.js b/client/coral-admin/src/routes/Configure/components/Configure.js index 4d9f0ff34..fb55e25be 100644 --- a/client/coral-admin/src/routes/Configure/components/Configure.js +++ b/client/coral-admin/src/routes/Configure/components/Configure.js @@ -33,7 +33,9 @@ class Configure extends React.Component { onCancel={this.props.hideSaveDialog} title={t('bandialog.ban_user')} > - Are you sure? + There are unsaved changes, Are you sure you want to continue? + +
{ + await this.savePending(); + this.props.hideSaveDialog(); + }; + + discardChanges = () => { + this.props.clearPending(); + this.props.hideSaveDialog(); + }; + setActiveSection = section => { - // Check if pending - console.log('pending', this.props.pending); - this.props.setActiveSection(section); - this.props.router.push(`/admin/configure/${section}`); + if (this.shouldShowSaveDialog()) { + this.props.showSaveDialog(); + } else { + this.props.setActiveSection(section); + this.props.router.push(`/admin/configure/${section}`); + } + }; + + shouldShowSaveDialog = () => { + return !!Object.keys(this.props.pending).length; + }; + + onClickOutside = () => { + if (this.shouldShowSaveDialog()) { + this.props.showSaveDialog(); + } }; render() { @@ -42,19 +65,23 @@ class ConfigureContainer extends React.Component { } return ( - - {this.props.children} - + + + {this.props.children} + + ); } } From 681f9ecf5a31656ebd2c98f820ee3a0b3e7aa369 Mon Sep 17 00:00:00 2001 From: okbel Date: Fri, 30 Mar 2018 14:39:16 -0300 Subject: [PATCH 23/84] Removing plugin --- .gitignore | 1 - 1 file changed, 1 deletion(-) diff --git a/.gitignore b/.gitignore index a660453ea..1235e0db0 100644 --- a/.gitignore +++ b/.gitignore @@ -67,7 +67,6 @@ plugins/* !plugins/talk-plugin-upvote !plugins/talk-plugin-viewing-options !plugins/talk-plugin-rich-text -!plugins/talk-plugin-auth-checkbox **/node_modules/* yarn-error.log From fe75d9543c3860116cae9d9c3b1ff33c98b0447a Mon Sep 17 00:00:00 2001 From: Chi Vinh Le Date: Fri, 30 Mar 2018 20:12:10 +0200 Subject: [PATCH 24/84] Restore Links --- .../client/components/AdminCommentContent.js | 9 ++++++++- .../client/components/CommentContent.js | 5 ++++- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/plugins/talk-plugin-rich-text/client/components/AdminCommentContent.js b/plugins/talk-plugin-rich-text/client/components/AdminCommentContent.js index 1b7e53ebf..f49d89055 100644 --- a/plugins/talk-plugin-rich-text/client/components/AdminCommentContent.js +++ b/plugins/talk-plugin-rich-text/client/components/AdminCommentContent.js @@ -1,12 +1,13 @@ import React from 'react'; import PropTypes from 'prop-types'; +import Linkify from 'react-linkify'; import styles from './AdminCommentContent.css'; import { AdminCommentContent as Content } from 'plugin-api/beta/client/components'; class AdminCommentContent extends React.Component { render() { const { comment, suspectWords, bannedWords } = this.props; - return ( + const content = ( ); + + if (!!comment.richTextBody) { + return content; + } + + return {content}; } } diff --git a/plugins/talk-plugin-rich-text/client/components/CommentContent.js b/plugins/talk-plugin-rich-text/client/components/CommentContent.js index 2cf481830..64aa96c82 100644 --- a/plugins/talk-plugin-rich-text/client/components/CommentContent.js +++ b/plugins/talk-plugin-rich-text/client/components/CommentContent.js @@ -3,6 +3,7 @@ import PropTypes from 'prop-types'; import { PLUGIN_NAME } from '../constants'; import cn from 'classnames'; import styles from './CommentContent.css'; +import Linkify from 'react-linkify'; class CommentContent extends React.Component { render() { @@ -14,7 +15,9 @@ class CommentContent extends React.Component { dangerouslySetInnerHTML={{ __html: comment.richTextBody }} /> ) : ( -
{comment.body}
+ +
{comment.body}
+
); } } From 24c1abaa06063c0fe7074a623b04def6caafa12f Mon Sep 17 00:00:00 2001 From: okbel Date: Fri, 30 Mar 2018 16:05:25 -0300 Subject: [PATCH 25/84] Removing plugin --- plugins/talk-plugin-auth-checkbox/README.md | 1 - .../client/.eslintrc.json | 23 ------ .../components/TermsAndConditionsField.css | 18 ----- .../components/TermsAndConditionsField.js | 76 ------------------- .../talk-plugin-auth-checkbox/client/index.js | 9 --- .../client/translations.yml | 36 --------- plugins/talk-plugin-auth-checkbox/index.js | 1 - 7 files changed, 164 deletions(-) delete mode 100644 plugins/talk-plugin-auth-checkbox/README.md delete mode 100644 plugins/talk-plugin-auth-checkbox/client/.eslintrc.json delete mode 100644 plugins/talk-plugin-auth-checkbox/client/components/TermsAndConditionsField.css delete mode 100644 plugins/talk-plugin-auth-checkbox/client/components/TermsAndConditionsField.js delete mode 100644 plugins/talk-plugin-auth-checkbox/client/index.js delete mode 100644 plugins/talk-plugin-auth-checkbox/client/translations.yml delete mode 100644 plugins/talk-plugin-auth-checkbox/index.js diff --git a/plugins/talk-plugin-auth-checkbox/README.md b/plugins/talk-plugin-auth-checkbox/README.md deleted file mode 100644 index 9b70bde6c..000000000 --- a/plugins/talk-plugin-auth-checkbox/README.md +++ /dev/null @@ -1 +0,0 @@ -# talk-plugin-auth-checkbox diff --git a/plugins/talk-plugin-auth-checkbox/client/.eslintrc.json b/plugins/talk-plugin-auth-checkbox/client/.eslintrc.json deleted file mode 100644 index 9fe56bd14..000000000 --- a/plugins/talk-plugin-auth-checkbox/client/.eslintrc.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "env": { - "browser": true, - "es6": true, - "mocha": true - }, - "parserOptions": { - "sourceType": "module", - "ecmaFeatures": { - "experimentalObjectRestSpread": true, - "jsx": true - } - }, - "parser": "babel-eslint", - "plugins": [ - "react" - ], - "rules": { - "react/jsx-uses-react": "error", - "react/jsx-uses-vars": "error", - "no-console": ["warn", { "allow": ["warn", "error"] }] - } -} diff --git a/plugins/talk-plugin-auth-checkbox/client/components/TermsAndConditionsField.css b/plugins/talk-plugin-auth-checkbox/client/components/TermsAndConditionsField.css deleted file mode 100644 index c11f408a3..000000000 --- a/plugins/talk-plugin-auth-checkbox/client/components/TermsAndConditionsField.css +++ /dev/null @@ -1,18 +0,0 @@ -.link { - color: #2c69b6; - cursor: pointer; - margin: 0 3px; - - &:last-child { - margin-right: 0; - } -} - -.fieldContainer { - padding: 10px 0; - display: flex; -} - -.textLabel { - padding-left: 10px; -} \ No newline at end of file diff --git a/plugins/talk-plugin-auth-checkbox/client/components/TermsAndConditionsField.js b/plugins/talk-plugin-auth-checkbox/client/components/TermsAndConditionsField.js deleted file mode 100644 index f16a675b7..000000000 --- a/plugins/talk-plugin-auth-checkbox/client/components/TermsAndConditionsField.js +++ /dev/null @@ -1,76 +0,0 @@ -import React from 'react'; -import { Checkbox } from 'plugin-api/beta/client/components/ui'; -import { t } from 'plugin-api/beta/client/services'; -import styles from './TermsAndConditionsField.css'; -import cn from 'classnames'; - -const TermsLink = () => ( - - {t('talk-plugin-auth-checkbox.terms')} - -); - -const PrivacyLink = () => ( - - {t('talk-plugin-auth-checkbox.privacy-policy')} - -); - -class TermsAndConditionsField extends React.Component { - state = { checked: false }; - id = 'terms-and-conditions'; - - componentWillMount() { - this.props.indicateBlocker(); - } - - onChange = ({ target: { checked } }) => { - this.setState({ checked }); - if (checked) { - this.props.indicateBlockerResolved(); - } else { - this.props.indicateBlocker(); - } - }; - - render() { - return ( -
- -
- {t('talk-plugin-auth-checkbox.copy')} - - {t('talk-plugin-auth-checkbox.and')} - - {t('talk-plugin-auth-checkbox.from')} -
-
- ); - } -} - -export default TermsAndConditionsField; diff --git a/plugins/talk-plugin-auth-checkbox/client/index.js b/plugins/talk-plugin-auth-checkbox/client/index.js deleted file mode 100644 index 9a30686c6..000000000 --- a/plugins/talk-plugin-auth-checkbox/client/index.js +++ /dev/null @@ -1,9 +0,0 @@ -import TermsAndConditionsField from './components/TermsAndConditionsField'; -import translations from './translations.yml'; - -export default { - slots: { - 'talkPluginAuth.formField': [TermsAndConditionsField], - }, - translations, -}; diff --git a/plugins/talk-plugin-auth-checkbox/client/translations.yml b/plugins/talk-plugin-auth-checkbox/client/translations.yml deleted file mode 100644 index ae50146d9..000000000 --- a/plugins/talk-plugin-auth-checkbox/client/translations.yml +++ /dev/null @@ -1,36 +0,0 @@ -en: - talk-plugin-auth-checkbox: - copy: I agree to the Mozilla - and: and - terms: Terms of Service - privacy-policy: Privacy Policy - from: . - terms-link: https://www.mozilla.org/en-US/about/legal/terms/mozilla/ - privacy-policy-link: https://www.mozilla.org/en-US/privacy/websites/ -es: - talk-plugin-auth-checkbox: - copy: Estoy de acuerdo con - and: y - terms: los Terminos y Condiciones - privacy-policy: la Política de Privacidad - from: de Mozilla. - terms-link: https://www.mozilla.org/es-ES/about/legal/terms/mozilla/ - privacy-policy-link: https://www.mozilla.org/es-ES/privacy/websites/ -fr: - talk-plugin-auth-checkbox: - copy: J'accepte - and: et - terms: les conditions d'utilisation - privacy-policy: la politique de confidentialité - from: de Mozilla. - terms-link: https://www.mozilla.org/fr/about/legal/terms/mozilla/ - privacy-policy-link: https://www.mozilla.org/fr/privacy/websites/ -de: - talk-plugin-auth-checkbox: - copy: Ich stimme - and: und - terms: den Allgemeinen Geschäftsbedingungen - privacy-policy: der Datenschutzrichtlinie - from: von Mozilla zu. - terms-link: https://www.mozilla.org/de/about/legal/terms/mozilla/ - privacy-policy-link: https://www.mozilla.org/de/privacy/websites/ \ No newline at end of file diff --git a/plugins/talk-plugin-auth-checkbox/index.js b/plugins/talk-plugin-auth-checkbox/index.js deleted file mode 100644 index f053ebf79..000000000 --- a/plugins/talk-plugin-auth-checkbox/index.js +++ /dev/null @@ -1 +0,0 @@ -module.exports = {}; From a509405f43645b163b568ccf533e288422bb4fbe Mon Sep 17 00:00:00 2001 From: okbel Date: Mon, 2 Apr 2018 13:33:50 -0300 Subject: [PATCH 26/84] alert route hook ready! --- .../routes/Configure/containers/Configure.js | 41 ++++++++++--------- 1 file changed, 22 insertions(+), 19 deletions(-) diff --git a/client/coral-admin/src/routes/Configure/containers/Configure.js b/client/coral-admin/src/routes/Configure/containers/Configure.js index 6df911104..4affc53f5 100644 --- a/client/coral-admin/src/routes/Configure/containers/Configure.js +++ b/client/coral-admin/src/routes/Configure/containers/Configure.js @@ -3,7 +3,6 @@ import { connect } from 'react-redux'; import { bindActionCreators } from 'redux'; import { compose, gql } from 'react-apollo'; import { withQuery, withMergedSettings } from 'coral-framework/hocs'; -import ClickOutside from 'coral-framework/components/ClickOutside'; import { Spinner } from 'coral-ui'; import PropTypes from 'prop-types'; import { withUpdateSettings } from 'coral-framework/graphql/mutations'; @@ -49,12 +48,17 @@ class ConfigureContainer extends React.Component { return !!Object.keys(this.props.pending).length; }; - onClickOutside = () => { + routeLeave = () => { if (this.shouldShowSaveDialog()) { this.props.showSaveDialog(); + return false; } }; + componentDidMount() { + this.props.router.setRouteLeaveHook(this.props.route, this.routeLeave); + } + render() { if (this.props.data.error) { return
{this.props.data.error.message}
; @@ -65,23 +69,21 @@ class ConfigureContainer extends React.Component { } return ( - - - {this.props.children} - - + + {this.props.children} + ); } } @@ -156,4 +158,5 @@ ConfigureContainer.propTypes = { mergedSettings: PropTypes.object.isRequired, children: PropTypes.node.isRequired, router: PropTypes.object, + route: PropTypes.object, }; From 4ed04d1c39510ad3b81515e98f58200f514489b7 Mon Sep 17 00:00:00 2001 From: okbel Date: Mon, 2 Apr 2018 14:12:02 -0300 Subject: [PATCH 27/84] Adding translations --- .../routes/Configure/components/Configure.js | 23 +++++++++---------- locales/en.yml | 4 ++++ 2 files changed, 15 insertions(+), 12 deletions(-) diff --git a/client/coral-admin/src/routes/Configure/components/Configure.js b/client/coral-admin/src/routes/Configure/components/Configure.js index fb55e25be..dcc9308b8 100644 --- a/client/coral-admin/src/routes/Configure/components/Configure.js +++ b/client/coral-admin/src/routes/Configure/components/Configure.js @@ -11,12 +11,7 @@ class Configure extends React.Component { const { canSave, currentUser, root, savePending, settings } = this.props; if (!can(currentUser, 'UPDATE_CONFIG')) { - return ( -

- You must be an administrator to access config settings. Please find - the nearest Admin and ask them to level you up! -

- ); + return

{t('configure.access_message')}

; } const passProps = { @@ -27,15 +22,19 @@ class Configure extends React.Component { return (
- There are unsaved changes, Are you sure you want to continue? - - + {t('configure.save_dialog_copy')} + +
Date: Mon, 2 Apr 2018 14:19:25 -0300 Subject: [PATCH 28/84] Spanish translations --- locales/es.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/locales/es.yml b/locales/es.yml index ba6cb8ad3..6919ce413 100644 --- a/locales/es.yml +++ b/locales/es.yml @@ -153,6 +153,10 @@ es: sign_out: "Desconectar" stories: Artículos stream_settings: "Configuración de Comentarios" + save_dialog_copy: "Hay cambios no guardados. Está seguro que desea continuar?" + save_settings: "Guardar configuración" + discard_changes: "Descartar Cambios" + access_message: "Usted debe ser un administrador para acceder a esta página. Encuentre a otro admin y actualice los permisos de su cuenta!" suspect_word_title: "Lista de palabras sospechosas" suspect_word_text: "Comentarios que contengan estas palabras o frases, considerando mayusculas y minúsculas, serán automáticamente destacadas en los comentarios publicados. Escribir una palabra y apretar Enter o Tabulador para agregarla. O pegar una lista de palabras separadas por coma." tech_settings: "Configuración Técnica" From d16ce17e36623fad4803c98c793e3b04d90fe76e Mon Sep 17 00:00:00 2001 From: okbel Date: Mon, 2 Apr 2018 14:35:09 -0300 Subject: [PATCH 29/84] SaveChangesDialog --- .../routes/Configure/components/Configure.js | 25 ++++------ .../components/SaveChangesDialog.css | 19 ++++++++ .../Configure/components/SaveChangesDialog.js | 46 +++++++++++++++++++ 3 files changed, 73 insertions(+), 17 deletions(-) create mode 100644 client/coral-admin/src/routes/Configure/components/SaveChangesDialog.css create mode 100644 client/coral-admin/src/routes/Configure/components/SaveChangesDialog.js diff --git a/client/coral-admin/src/routes/Configure/components/Configure.js b/client/coral-admin/src/routes/Configure/components/Configure.js index dcc9308b8..137f685cf 100644 --- a/client/coral-admin/src/routes/Configure/components/Configure.js +++ b/client/coral-admin/src/routes/Configure/components/Configure.js @@ -1,10 +1,10 @@ import React from 'react'; import PropTypes from 'prop-types'; -import cn from 'classnames'; import t from 'coral-framework/services/i18n'; -import { Button, List, Item, Dialog } from 'coral-ui'; +import { Button, List, Item } from 'coral-ui'; import { can } from 'coral-framework/services/perms'; import styles from './Configure.css'; +import SaveChangesDialog from './SaveChangesDialog'; class Configure extends React.Component { render() { @@ -21,21 +21,12 @@ class Configure extends React.Component { return (
- - {t('configure.save_dialog_copy')} - - - +
( + + + × + + {t('configure.save_dialog_copy')} +
+ + +
+
+); + +SaveChangesDialog.propTypes = { + saveDialog: PropTypes.bool.isRequired, + hideSaveDialog: PropTypes.func.isRequired, + saveChanges: PropTypes.func.isRequired, + discardChanges: PropTypes.func.isRequired, +}; + +export default SaveChangesDialog; From 3da201c22df47d8e017d65128f19554d46331f2e Mon Sep 17 00:00:00 2001 From: Wyatt Johnson Date: Mon, 2 Apr 2018 12:25:18 -0600 Subject: [PATCH 30/84] allow plugin code from clients to be parsed by webpack --- webpack.config.js | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/webpack.config.js b/webpack.config.js index d6f216d02..c6b3a770f 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -22,6 +22,7 @@ require('dotenv').config(); const { plugins, pluginsPath, PluginManager } = require('./plugins'); const manager = new PluginManager(plugins); const targetPlugins = manager.section('targets').plugins; +const clientPlugins = manager.section('client').plugins; debug(`Using ${pluginsPath} as the plugin configuration path`); @@ -51,6 +52,19 @@ const TALK_WEBPACK_SOURCE_MAP = _.get( const devtool = TALK_WEBPACK_SOURCE_MAP === 'none' ? false : TALK_WEBPACK_SOURCE_MAP; +// Exclude everything from node_modules except for those that may be in a +// plugin folder inside the node_modules folder. +const exclude = { + and: [ + // Exclude everything inside node_modules.. + /node_modules/, + // But not those that contain plugins. + { + not: clientPlugins.map(({ name }) => new RegExp(`node_modules\/${name}`)), + }, + ], +}; + //============================================================================== // Base Webpack Config //============================================================================== @@ -73,7 +87,7 @@ const config = { }, { loader: 'babel-loader', - exclude: /node_modules/, + exclude, test: /\.js$/, query: { cacheDirectory: true, @@ -82,11 +96,11 @@ const config = { { loader: 'hjson-loader', test: /\.(json|yml)$/, - exclude: /node_modules/, + exclude, }, { loader: 'yaml-loader', - exclude: /node_modules/, + exclude, test: /\.yml$/, }, { @@ -118,7 +132,7 @@ const config = { }, { test: /\.(graphql|gql)$/, - exclude: /node_modules/, + exclude, loader: 'graphql-tag/loader', }, ], From e4358ac3a1d7d755c2db8857ba6de773be778ecb Mon Sep 17 00:00:00 2001 From: okbel Date: Mon, 2 Apr 2018 16:09:47 -0300 Subject: [PATCH 31/84] Style and translations --- .../components/SaveChangesDialog.css | 25 +++++++++++++++++-- .../Configure/components/SaveChangesDialog.js | 17 +++++++++---- locales/en.yml | 9 ++++--- locales/es.yml | 9 ++++--- 4 files changed, 47 insertions(+), 13 deletions(-) diff --git a/client/coral-admin/src/routes/Configure/components/SaveChangesDialog.css b/client/coral-admin/src/routes/Configure/components/SaveChangesDialog.css index e77dbab34..f66a92e44 100644 --- a/client/coral-admin/src/routes/Configure/components/SaveChangesDialog.css +++ b/client/coral-admin/src/routes/Configure/components/SaveChangesDialog.css @@ -1,9 +1,11 @@ .buttonActions { - padding-top: 10px; + padding-top: 15px; + text-align: right; } .dialog { - padding: 20px; + padding: 25px; + min-width: 400px; } .close { @@ -16,4 +18,23 @@ font-weight: bold; color: #363636; cursor: pointer; +} + +.title { + font-size: 18px; + font-weight: 800; + margin-bottom: 20px; +} + +.cancel { + color: #363636; + margin-right: 15px; + display: inline-block; + &:hover { + cursor: pointer; + } +} + +.button { + margin-left: 5px; } \ No newline at end of file diff --git a/client/coral-admin/src/routes/Configure/components/SaveChangesDialog.js b/client/coral-admin/src/routes/Configure/components/SaveChangesDialog.js index f65dfdd09..fca87d2cd 100644 --- a/client/coral-admin/src/routes/Configure/components/SaveChangesDialog.js +++ b/client/coral-admin/src/routes/Configure/components/SaveChangesDialog.js @@ -16,22 +16,29 @@ const SaveChangesDialog = ({ id="saveDialog" open={saveDialog} onCancel={hideSaveDialog} - title={t('configure.unsaved_changes')} > × - {t('configure.save_dialog_copy')} +
+ {t('configure.save_changes_dialog.unsaved_changes')} +
+ {t('configure.save_changes_dialog.copy')}
- + -
); diff --git a/locales/en.yml b/locales/en.yml index e3d87a522..6b2330f27 100644 --- a/locales/en.yml +++ b/locales/en.yml @@ -154,9 +154,6 @@ en: sign_out: "Sign Out" stories: Stories stream_settings: "Stream Settings" - save_dialog_copy: "There are unsaved changes, Are you sure you want to continue?" - save_settings: "Save Settings" - discard_changes: "Discard changes" access_message: "You must be an administrator to access config settings. Please find the nearest Admin and ask them to level you up!" suspect_word_title: "Suspect words list" suspect_word_text: "Comments which contain these words or phrases (not case-sensitive) will be highlighted in the comment stream. Type a word and press Enter or Tab to add. Optionally paste a comma-separated list." @@ -164,6 +161,12 @@ en: title: "Configure Comment Stream" weeks: Weeks wordlist: "Banned Words" + save_changes_dialog: + unsaved_changes: "Unsaved changes" + copy: "You have made one or more changes without saving. Would you like to save or discard your changes?" + save_settings: "Save Settings" + discard: "Discard" + cancel: "Cancel" continue: "Continue" createdisplay: check_the_form: "Invalid Form. Please check the fields" diff --git a/locales/es.yml b/locales/es.yml index 6919ce413..4e7a2c51b 100644 --- a/locales/es.yml +++ b/locales/es.yml @@ -153,9 +153,6 @@ es: sign_out: "Desconectar" stories: Artículos stream_settings: "Configuración de Comentarios" - save_dialog_copy: "Hay cambios no guardados. Está seguro que desea continuar?" - save_settings: "Guardar configuración" - discard_changes: "Descartar Cambios" access_message: "Usted debe ser un administrador para acceder a esta página. Encuentre a otro admin y actualice los permisos de su cuenta!" suspect_word_title: "Lista de palabras sospechosas" suspect_word_text: "Comentarios que contengan estas palabras o frases, considerando mayusculas y minúsculas, serán automáticamente destacadas en los comentarios publicados. Escribir una palabra y apretar Enter o Tabulador para agregarla. O pegar una lista de palabras separadas por coma." @@ -163,6 +160,12 @@ es: title: "Configurar los comentarios" weeks: Semanas wordlist: "Palabras Suspendidas" + save_changes_dialog: + unsaved_changes: "Cambios no guardados" + copy: Has hecho uno o más cambios sin guardar. Deseas guardar o descartar tus cambios?" + save_settings: "Guardar configuración" + discard: "Descartar" + cancel: "Cancelar" continue: "Continuar" createdisplay: check_the_form: "Formulario Inválido. Por favor verifica los campos" From fb3233cf01a269d16a2a7072e79bfe4d781fc0e3 Mon Sep 17 00:00:00 2001 From: Wyatt Johnson Date: Mon, 2 Apr 2018 17:01:49 -0600 Subject: [PATCH 32/84] IfSlotIsNotEmpty Bug --- client/coral-framework/components/IfSlotIsNotEmpty.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/client/coral-framework/components/IfSlotIsNotEmpty.js b/client/coral-framework/components/IfSlotIsNotEmpty.js index 6318e6447..1707a83b6 100644 --- a/client/coral-framework/components/IfSlotIsNotEmpty.js +++ b/client/coral-framework/components/IfSlotIsNotEmpty.js @@ -7,7 +7,7 @@ class IfSlotIsNotEmpty extends React.Component { isSlotEmpty(props = this.props) { const { slotElements } = props; return slotElements.length === 0 - ? false + ? true : slotElements.every(elements => elements.length === 0); } @@ -19,6 +19,7 @@ class IfSlotIsNotEmpty extends React.Component { IfSlotIsNotEmpty.propTypes = { slot: PropTypes.oneOfType([PropTypes.string, PropTypes.array]), + slotElements: PropTypes.array.isRequired, children: PropTypes.node.isRequired, passthrough: PropTypes.object.isRequired, }; From 1a39727e389c94589a2e6399f2d218d68f1bb514 Mon Sep 17 00:00:00 2001 From: okbel Date: Tue, 3 Apr 2018 10:29:07 -0300 Subject: [PATCH 33/84] account history to user history --- .../src/components/AccountHistory.js | 20 +++++++++---------- .../coral-admin/src/components/UserDetail.js | 2 +- locales/ar.yml | 4 ++-- locales/da.yml | 4 ++-- locales/de.yml | 4 ++-- locales/en.yml | 4 ++-- locales/es.yml | 4 ++-- locales/fr.yml | 4 ++-- locales/nl_NL.yml | 4 ++-- locales/pt_BR.yml | 4 ++-- locales/zh_CN.yml | 4 ++-- locales/zh_TW.yml | 4 ++-- 12 files changed, 30 insertions(+), 32 deletions(-) diff --git a/client/coral-admin/src/components/AccountHistory.js b/client/coral-admin/src/components/AccountHistory.js index 0d85b62af..6079187c6 100644 --- a/client/coral-admin/src/components/AccountHistory.js +++ b/client/coral-admin/src/components/AccountHistory.js @@ -43,15 +43,15 @@ const readableDuration = (startDate, endDate) => { const buildActionResponse = (typename, created_at, until, status) => { switch (typename) { case 'UsernameStatusHistory': - return t('account_history.username_status', status); + return t('user_history.username_status', status); case 'BannedStatusHistory': return status - ? t('account_history.user_banned') - : t('account_history.ban_removed'); + ? t('user_history.user_banned') + : t('user_history.ban_removed'); case 'SuspensionStatusHistory': return until - ? t('account_history.suspended', readableDuration(created_at, until)) - : t('account_history.suspension_removed'); + ? t('user_history.suspended', readableDuration(created_at, until)) + : t('user_history.suspension_removed'); default: return '-'; } @@ -62,7 +62,7 @@ const getModerationValue = assignedBy => assignedBy.username ) : ( - {t('account_history.system')} + {t('user_history.system')} ); @@ -79,14 +79,12 @@ class AccountHistory extends React.Component { 'talk-admin-account-history-header-row' )} > +
{t('user_history.date')}
- {t('account_history.date')} + {t('user_history.action')}
- {t('account_history.action')} -
-
- {t('account_history.taken_by')} + {t('user_history.taken_by')}
{userHistory.map( diff --git a/client/coral-admin/src/components/UserDetail.js b/client/coral-admin/src/components/UserDetail.js index 31ad999dc..7181a6f52 100644 --- a/client/coral-admin/src/components/UserDetail.js +++ b/client/coral-admin/src/components/UserDetail.js @@ -286,7 +286,7 @@ class UserDetail extends React.Component { 'talk-admin-user-detail-history-tab' )} > - {t('user_detail.account_history')} + {t('user_detail.user_history')} diff --git a/locales/ar.yml b/locales/ar.yml index 3c94c06f0..98f6cc54e 100644 --- a/locales/ar.yml +++ b/locales/ar.yml @@ -438,8 +438,8 @@ ar: reports: "Reports" all: "All" rejected: "Rejected" - account_history: "Account History" - account_history: + user_history: "User History" + user_history: user_banned: "User banned" ban_removed: "Ban removed" username_status: "Username {0}" diff --git a/locales/da.yml b/locales/da.yml index 9618b2f58..0afe4c5ce 100644 --- a/locales/da.yml +++ b/locales/da.yml @@ -431,8 +431,8 @@ da: reports: "Rapporter" all: "Alle" rejected: "Afvist" - account_history: "Konto historik" - account_history: + user_history: "Konto historik" + user_history: user_banned: "Bruger bannet" ban_removed: "Ban fjernet" username_status: "Brugernavn {0}" diff --git a/locales/de.yml b/locales/de.yml index 972c2735b..6f7a1a66e 100644 --- a/locales/de.yml +++ b/locales/de.yml @@ -430,8 +430,8 @@ de: reports: "Meldungen" all: "Alle" rejected: "Abgelehnte" - account_history: "Konto-Verlauf" - account_history: + user_history: "Konto-Verlauf" + user_history: user_banned: "User banned" ban_removed: "Ban removed" username_status: "Username {0}" diff --git a/locales/en.yml b/locales/en.yml index 08d64ddc8..6e4f82c0d 100644 --- a/locales/en.yml +++ b/locales/en.yml @@ -439,8 +439,8 @@ en: reports: "Reports" all: "All" rejected: "Rejected" - account_history: "Account History" - account_history: + user_history: "User History" + user_history: user_banned: "User banned" ban_removed: "Ban removed" username_status: "Username {0}" diff --git a/locales/es.yml b/locales/es.yml index ba6cb8ad3..e492aca6f 100644 --- a/locales/es.yml +++ b/locales/es.yml @@ -432,8 +432,8 @@ es: reports: "Reports" all: "All" rejected: "Rejected" - account_history: "Account History" - account_history: + user_history: "User History" + user_history: user_banned: "User banned" ban_removed: "Ban removed" username_status: "Username {0}" diff --git a/locales/fr.yml b/locales/fr.yml index dd14bea81..1f027d49a 100644 --- a/locales/fr.yml +++ b/locales/fr.yml @@ -432,8 +432,8 @@ fr: reports: "Reports" all: "All" rejected: "Rejected" - account_history: "Account History" - account_history: + user_history: "User History" + user_history: user_banned: "User banned" ban_removed: "Ban removed" username_status: "Username {0}" diff --git a/locales/nl_NL.yml b/locales/nl_NL.yml index aa80f58be..9d4057065 100644 --- a/locales/nl_NL.yml +++ b/locales/nl_NL.yml @@ -431,8 +431,8 @@ nl_NL: reports: "Rapportages" all: "Alle" rejected: "Afgewezen" - account_history: "Accountgeschiedenis" - account_history: + user_history: "Accountgeschiedenis" + user_history: user_banned: "User banned" ban_removed: "Ban removed" username_status: "Username {0}" diff --git a/locales/pt_BR.yml b/locales/pt_BR.yml index 86267d51d..d9ef119ed 100644 --- a/locales/pt_BR.yml +++ b/locales/pt_BR.yml @@ -430,8 +430,8 @@ pt_BR: reports: "Reports" all: "All" rejected: "Rejected" - account_history: "Account History" - account_history: + user_history: "User History" + user_history: user_banned: "User banned" ban_removed: "Ban removed" username_status: "Username {0}" diff --git a/locales/zh_CN.yml b/locales/zh_CN.yml index 59e028eb7..b3307a886 100644 --- a/locales/zh_CN.yml +++ b/locales/zh_CN.yml @@ -432,8 +432,8 @@ zh_CN: reports: "Reports" all: "All" rejected: "Rejected" - account_history: "Account History" - account_history: + user_history: "User History" + user_history: user_banned: "User banned" ban_removed: "Ban removed" username_status: "Username {0}" diff --git a/locales/zh_TW.yml b/locales/zh_TW.yml index 1b131f636..c186a667f 100644 --- a/locales/zh_TW.yml +++ b/locales/zh_TW.yml @@ -432,8 +432,8 @@ zh_TW: reports: "Reports" all: "All" rejected: "Rejected" - account_history: "Account History" - account_history: + user_history: "User History" + user_history: user_banned: "User banned" ban_removed: "Ban removed" username_status: "Username {0}" From ef10ab80888c33063f22aadb2f4320277ccc9393 Mon Sep 17 00:00:00 2001 From: okbel Date: Tue, 3 Apr 2018 11:20:22 -0300 Subject: [PATCH 34/84] Renaming components and styles --- .../{AccountHistory.css => UserHistory.css} | 0 .../{AccountHistory.js => UserHistory.js} | 20 +++++++------- package-lock.json | 27 +++++++++++++++++++ test/e2e/page_objects/admin.js | 4 +-- test/e2e/specs/06_suspendUser.js | 2 +- 5 files changed, 40 insertions(+), 13 deletions(-) rename client/coral-admin/src/components/{AccountHistory.css => UserHistory.css} (100%) rename client/coral-admin/src/components/{AccountHistory.js => UserHistory.js} (86%) create mode 100644 package-lock.json diff --git a/client/coral-admin/src/components/AccountHistory.css b/client/coral-admin/src/components/UserHistory.css similarity index 100% rename from client/coral-admin/src/components/AccountHistory.css rename to client/coral-admin/src/components/UserHistory.css diff --git a/client/coral-admin/src/components/AccountHistory.js b/client/coral-admin/src/components/UserHistory.js similarity index 86% rename from client/coral-admin/src/components/AccountHistory.js rename to client/coral-admin/src/components/UserHistory.js index 6079187c6..3a3472c86 100644 --- a/client/coral-admin/src/components/AccountHistory.js +++ b/client/coral-admin/src/components/UserHistory.js @@ -1,7 +1,7 @@ import React from 'react'; import PropTypes from 'prop-types'; import { murmur3 } from 'murmurhash-js'; -import styles from './AccountHistory.css'; +import styles from './UserHistory.css'; import cn from 'classnames'; import flatten from 'lodash/flatten'; import orderBy from 'lodash/orderBy'; @@ -66,17 +66,17 @@ const getModerationValue = assignedBy => ); -class AccountHistory extends React.Component { +class UserHistory extends React.Component { render() { const { user } = this.props; const userHistory = buildUserHistory(user.state); return (
-
+
{t('user_history.date')}
@@ -90,13 +90,13 @@ class AccountHistory extends React.Component { {userHistory.map( ({ __typename, created_at, assigned_by, until, status }) => (
{moment(new Date(created_at)).format('MMM DD, YYYY')} @@ -105,7 +105,7 @@ class AccountHistory extends React.Component { className={cn( styles.item, styles.action, - 'talk-admin-account-history-row-status' + 'talk-admin-user-history-row-status' )} > {buildActionResponse(__typename, created_at, until, status)} @@ -114,7 +114,7 @@ class AccountHistory extends React.Component { className={cn( styles.item, styles.username, - 'talk-admin-account-history-row-assigned-by' + 'talk-admin-user-history-row-assigned-by' )} > {getModerationValue(assigned_by)} @@ -128,8 +128,8 @@ class AccountHistory extends React.Component { } } -AccountHistory.propTypes = { +UserHistory.propTypes = { user: PropTypes.object.isRequired, }; -export default AccountHistory; +export default UserHistory; diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 000000000..d6105c594 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,27 @@ +{ + "name": "talk", + "version": "4.3.0", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "exenv": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/exenv/-/exenv-1.2.2.tgz", + "integrity": "sha1-KueOhdmJQVhnCwPUe+wfA72Ru50=" + }, + "react-side-effect": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/react-side-effect/-/react-side-effect-1.1.5.tgz", + "integrity": "sha512-Z2ZJE4p/jIfvUpiUMRydEVpQRf2f8GMHczT6qLcARmX7QRb28JDBTpnM2g/i5y/p7ZDEXYGHWg0RbhikE+hJRw==", + "requires": { + "exenv": "1.2.2", + "shallowequal": "1.0.2" + } + }, + "shallowequal": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/shallowequal/-/shallowequal-1.0.2.tgz", + "integrity": "sha512-zlVXeVUKvo+HEv1e2KQF/csyeMKx2oHvatQ9l6XjCUj3agvC8XGf6R9HvIPDSmp8FNPvx7b5kaEJTRi7CqxtEw==" + } + } +} diff --git a/test/e2e/page_objects/admin.js b/test/e2e/page_objects/admin.js index 218afe425..8e319e499 100644 --- a/test/e2e/page_objects/admin.js +++ b/test/e2e/page_objects/admin.js @@ -128,8 +128,8 @@ module.exports = { rejectedTab: '.talk-admin-user-detail-rejected-tab', historyTab: '.talk-admin-user-detail-history-tab', historyPane: '.talk-admin-user-detail-history-tab-pane', - accountHistory: '.talk-admin-account-history', - accountHistoryRowStatus: '.talk-admin-account-history-row-status', + UserHistory: '.talk-admin-user-history', + UserHistoryRowStatus: '.talk-admin-user-history-row-status', actionsMenu: '.talk-admin-user-detail-actions-button', actionItemSuspendUser: '.action-menu-item#suspendUser', actionMenuButton: diff --git a/test/e2e/specs/06_suspendUser.js b/test/e2e/specs/06_suspendUser.js index 0b89c5c01..81fde3781 100644 --- a/test/e2e/specs/06_suspendUser.js +++ b/test/e2e/specs/06_suspendUser.js @@ -125,7 +125,7 @@ module.exports = { .waitForElementVisible('@historyTab') .click('@historyTab') .waitForElementVisible('@historyPane') - .waitForElementVisible('@accountHistory') + .waitForElementVisible('@UserHistory') .click('@closeButton'); }, 'admin logs out': client => { From edbe49618a53c3a4091604e18f25266ec17f0842 Mon Sep 17 00:00:00 2001 From: okbel Date: Tue, 3 Apr 2018 20:17:31 -0300 Subject: [PATCH 35/84] Adding size, and proptypes --- .../src/tabs/profile/components/Comment.js | 1 + .../client/components/Comment.js | 9 +++++++++ 2 files changed, 10 insertions(+) diff --git a/client/coral-embed-stream/src/tabs/profile/components/Comment.js b/client/coral-embed-stream/src/tabs/profile/components/Comment.js index be13c2c50..171172dab 100644 --- a/client/coral-embed-stream/src/tabs/profile/components/Comment.js +++ b/client/coral-embed-stream/src/tabs/profile/components/Comment.js @@ -34,6 +34,7 @@ class Comment extends React.Component { defaultComponent={CommentContent} className={cn(styles.commentBody, 'my-comment-body')} passthrough={slotPassthrough} + size={1} />
@@ -87,4 +89,11 @@ class Comment extends React.Component { } } +Comment.propTypes = { + viewComment: PropTypes.func, + comment: PropTypes.object, + asset: PropTypes.object, + root: PropTypes.object, +}; + export default Comment; From 06f8926964abebf667bf332415973d250b07b1a7 Mon Sep 17 00:00:00 2001 From: okbel Date: Wed, 4 Apr 2018 07:25:47 -0300 Subject: [PATCH 36/84] Avoiding refetching --- .../coral-admin/src/components/UserDetail.js | 20 ++++++------------- .../coral-admin/src/containers/UserDetail.js | 3 ++- client/coral-framework/graphql/mutations.js | 2 +- 3 files changed, 9 insertions(+), 16 deletions(-) diff --git a/client/coral-admin/src/components/UserDetail.js b/client/coral-admin/src/components/UserDetail.js index 31ad999dc..90df62f4a 100644 --- a/client/coral-admin/src/components/UserDetail.js +++ b/client/coral-admin/src/components/UserDetail.js @@ -28,16 +28,6 @@ import UserInfoTooltip from './UserInfoTooltip'; import t from 'coral-framework/services/i18n'; class UserDetail extends React.Component { - rejectThenReload = async info => { - await this.props.rejectComment(info); - this.props.data.refetch(); - }; - - acceptThenReload = async info => { - await this.props.acceptComment(info); - this.props.data.refetch(); - }; - bulkAcceptThenReload = async () => { await this.props.bulkAccept(); this.props.data.refetch(); @@ -110,6 +100,8 @@ class UserDetail extends React.Component { unbanUser, unsuspendUser, modal, + acceptComment, + rejectComment, } = this.props; // if totalComments is 0, you're dividing by zero @@ -304,8 +296,8 @@ class UserDetail extends React.Component { loadMore={loadMore} toggleSelect={toggleSelect} viewUserDetail={viewUserDetail} - acceptComment={this.acceptThenReload} - rejectComment={this.rejectThenReload} + acceptComment={acceptComment} + rejectComment={rejectComment} selectedCommentIds={selectedCommentIds} toggleSelectAll={toggleSelectAll} bulkAcceptThenReload={this.bulkAcceptThenReload} @@ -322,8 +314,8 @@ class UserDetail extends React.Component { loadMore={loadMore} toggleSelect={toggleSelect} viewUserDetail={viewUserDetail} - acceptComment={this.acceptThenReload} - rejectComment={this.rejectThenReload} + acceptComment={acceptComment} + rejectComment={rejectComment} selectedCommentIds={selectedCommentIds} toggleSelectAll={toggleSelectAll} bulkAcceptThenReload={this.bulkAcceptThenReload} diff --git a/client/coral-admin/src/containers/UserDetail.js b/client/coral-admin/src/containers/UserDetail.js index fb1508a2f..73b46db3c 100644 --- a/client/coral-admin/src/containers/UserDetail.js +++ b/client/coral-admin/src/containers/UserDetail.js @@ -109,6 +109,7 @@ class UserDetailContainer extends React.Component { componentWillReceiveProps(next) { if (this.props.userId === null && next.userId) { next.data.refetch(); + console.log('fetching'); } } @@ -148,6 +149,7 @@ UserDetailContainer.propTypes = { selectedCommentIds: PropTypes.array, unbanUser: PropTypes.func.isRequired, unsuspendUser: PropTypes.func.isRequired, + userId: PropTypes.string, }; const LOAD_MORE_QUERY = gql` @@ -245,7 +247,6 @@ export const withUserDetailQuery = withQuery( options: ({ userId, statuses }) => { return { variables: { author_id: userId, statuses }, - fetchPolicy: 'network-only', }; }, skip: ownProps => !ownProps.userId, diff --git a/client/coral-framework/graphql/mutations.js b/client/coral-framework/graphql/mutations.js index 1b3b4c6fc..0c8d404f6 100644 --- a/client/coral-framework/graphql/mutations.js +++ b/client/coral-framework/graphql/mutations.js @@ -180,7 +180,7 @@ export const withSetCommentStatus = withMutation( const fragmentId = `Comment_${commentId}`; const data = proxy.readFragment({ fragment, id: fragmentId }); - + console.log('data', data); data.status = status; data.status_history = data.status_history ? data.status_history From a6a0b3e93174f8eb915f29ff257fcfa818c531f7 Mon Sep 17 00:00:00 2001 From: okbel Date: Wed, 4 Apr 2018 07:33:50 -0300 Subject: [PATCH 37/84] Removing reloads --- .../coral-admin/src/components/UserDetail.js | 20 ++++++------------- 1 file changed, 6 insertions(+), 14 deletions(-) diff --git a/client/coral-admin/src/components/UserDetail.js b/client/coral-admin/src/components/UserDetail.js index 90df62f4a..22a85ac88 100644 --- a/client/coral-admin/src/components/UserDetail.js +++ b/client/coral-admin/src/components/UserDetail.js @@ -28,16 +28,6 @@ import UserInfoTooltip from './UserInfoTooltip'; import t from 'coral-framework/services/i18n'; class UserDetail extends React.Component { - bulkAcceptThenReload = async () => { - await this.props.bulkAccept(); - this.props.data.refetch(); - }; - - bulkRejectThenReload = async () => { - await this.props.bulkReject(); - this.props.data.refetch(); - }; - changeTab = tab => { this.props.changeTab(tab); }; @@ -102,6 +92,8 @@ class UserDetail extends React.Component { modal, acceptComment, rejectComment, + bulkAccept, + bulkReject, } = this.props; // if totalComments is 0, you're dividing by zero @@ -300,8 +292,8 @@ class UserDetail extends React.Component { rejectComment={rejectComment} selectedCommentIds={selectedCommentIds} toggleSelectAll={toggleSelectAll} - bulkAcceptThenReload={this.bulkAcceptThenReload} - bulkRejectThenReload={this.bulkRejectThenReload} + bulkAcceptThenReload={bulkAccept} + bulkRejectThenReload={bulkReject} /> Date: Wed, 4 Apr 2018 07:37:27 -0300 Subject: [PATCH 38/84] Removing logs --- client/coral-admin/src/containers/UserDetail.js | 1 - client/coral-framework/graphql/mutations.js | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/client/coral-admin/src/containers/UserDetail.js b/client/coral-admin/src/containers/UserDetail.js index 73b46db3c..360819dd6 100644 --- a/client/coral-admin/src/containers/UserDetail.js +++ b/client/coral-admin/src/containers/UserDetail.js @@ -109,7 +109,6 @@ class UserDetailContainer extends React.Component { componentWillReceiveProps(next) { if (this.props.userId === null && next.userId) { next.data.refetch(); - console.log('fetching'); } } diff --git a/client/coral-framework/graphql/mutations.js b/client/coral-framework/graphql/mutations.js index 0c8d404f6..1b3b4c6fc 100644 --- a/client/coral-framework/graphql/mutations.js +++ b/client/coral-framework/graphql/mutations.js @@ -180,7 +180,7 @@ export const withSetCommentStatus = withMutation( const fragmentId = `Comment_${commentId}`; const data = proxy.readFragment({ fragment, id: fragmentId }); - console.log('data', data); + data.status = status; data.status_history = data.status_history ? data.status_history From 0c6ee2fe992bf86179da3ddfd7123fd8047ed7d4 Mon Sep 17 00:00:00 2001 From: Wyatt Johnson Date: Thu, 5 Apr 2018 10:33:38 -0600 Subject: [PATCH 39/84] updated yarn.lock --- yarn.lock | 279 ++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 273 insertions(+), 6 deletions(-) diff --git a/yarn.lock b/yarn.lock index 07a395260..f97cf762f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -68,7 +68,7 @@ lodash "^4.2.0" to-fast-properties "^2.0.0" -"@coralproject/eslint-config-talk@^0.1.0": +"@coralproject/eslint-config-talk@^0.1.0", "@coralproject/eslint-config-talk@^0.1.1": version "0.1.1" resolved "https://registry.yarnpkg.com/@coralproject/eslint-config-talk/-/eslint-config-talk-0.1.1.tgz#71991b4937a3ffe657128d7f1170da4b5fb75c9e" dependencies: @@ -126,6 +126,12 @@ to-title-case "~1.0.0" url-regex "~4.1.1" +"@types/form-data@*": + version "2.2.1" + resolved "https://registry.yarnpkg.com/@types/form-data/-/form-data-2.2.1.tgz#ee2b3b8eaa11c0938289953606b745b738c54b1e" + dependencies: + "@types/node" "*" + "@types/graphql@0.10.2": version "0.10.2" resolved "https://registry.yarnpkg.com/@types/graphql/-/graphql-0.10.2.tgz#d7c79acbaa17453b6681c80c34b38fcb10c4c08c" @@ -138,10 +144,25 @@ version "0.9.4" resolved "https://registry.yarnpkg.com/@types/graphql/-/graphql-0.9.4.tgz#cdeb6bcbef9b6c584374b81aa7f48ecf3da404fa" +"@types/lodash@^4.14.50": + version "4.14.106" + resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.106.tgz#6093e9a02aa567ddecfe9afadca89e53e5dce4dd" + "@types/node@*": version "8.0.53" resolved "https://registry.yarnpkg.com/@types/node/-/node-8.0.53.tgz#396b35af826fa66aad472c8cb7b8d5e277f4e6d8" +"@types/node@^7.0.0": + version "7.0.59" + resolved "https://registry.yarnpkg.com/@types/node/-/node-7.0.59.tgz#fd7dceba9521c2d62c3e0eda8c5d704bf88b261d" + +"@types/request@^0.0.39": + version "0.0.39" + resolved "https://registry.yarnpkg.com/@types/request/-/request-0.0.39.tgz#168b96cf4253c5d54d403f746f82ee7aed47ce2c" + dependencies: + "@types/form-data" "*" + "@types/node" "*" + "@types/ws@^3.0.0": version "3.2.0" resolved "https://registry.yarnpkg.com/@types/ws/-/ws-3.2.0.tgz#988ff690e6ed10068a86aa0e9f842d0a03c09e21" @@ -174,6 +195,13 @@ accepts@^1.3.4, accepts@~1.3.4: mime-types "~2.1.16" negotiator "0.6.1" +accepts@~1.3.5: + version "1.3.5" + resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.5.tgz#eb777df6011723a3b14e8a72c0805c8e86746bd2" + dependencies: + mime-types "~2.1.18" + negotiator "0.6.1" + acorn-dynamic-import@^2.0.0: version "2.0.2" resolved "https://registry.yarnpkg.com/acorn-dynamic-import/-/acorn-dynamic-import-2.0.2.tgz#c752bd210bef679501b6c6cb7fc84f8f47158cc4" @@ -228,6 +256,10 @@ acorn@^5.3.0: version "5.4.1" resolved "https://registry.yarnpkg.com/acorn/-/acorn-5.4.1.tgz#fdc58d9d17f4a4e98d102ded826a9b9759125102" +acorn@^5.5.0: + version "5.5.3" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-5.5.3.tgz#f473dd47e0277a08e28e9bec5aeeb04751f0b8c9" + addressparser@1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/addressparser/-/addressparser-1.0.1.tgz#47afbe1a2a9262191db6838e4fd1d39b40821746" @@ -1770,6 +1802,13 @@ caseless@~0.12.0: version "0.12.0" resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc" +casual@^1.5.19: + version "1.5.19" + resolved "https://registry.yarnpkg.com/casual/-/casual-1.5.19.tgz#66fac46f7ae463f468f5913eb139f9c41c58bbf2" + dependencies: + mersenne-twister "^1.0.1" + moment "^2.15.2" + center-align@^0.1.1: version "0.1.3" resolved "https://registry.yarnpkg.com/center-align/-/center-align-0.1.3.tgz#aa0d32629b6ee972200411cbd4461c907bc2b7ad" @@ -2427,6 +2466,10 @@ cosmiconfig@^4.0.0, cosmiconfig@~4.0.0: parse-json "^4.0.0" require-from-string "^2.0.1" +crc@3.4.4: + version "3.4.4" + resolved "https://registry.yarnpkg.com/crc/-/crc-3.4.4.tgz#9da1e980e3bd44fc5c93bf5ab3da3378d85e466b" + create-ecdh@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/create-ecdh/-/create-ecdh-4.0.0.tgz#888c723596cdf7612f6498233eebd7a35301737d" @@ -2916,7 +2959,7 @@ dns-prefetch-control@0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/dns-prefetch-control/-/dns-prefetch-control-0.1.0.tgz#60ddb457774e178f1f9415f0cabb0e85b0b300b2" -doctrine@^2.0.0: +doctrine@^2.0.0, doctrine@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-2.1.0.tgz#5cd01fc101621b42c4cd7f5d1a66243716d3f39d" dependencies: @@ -3057,6 +3100,10 @@ ejs@2.5.7, ejs@^2.5.7: version "2.5.7" resolved "https://registry.yarnpkg.com/ejs/-/ejs-2.5.7.tgz#cc872c168880ae3c7189762fd5ffc00896c9518a" +ejs@^2.5.8: + version "2.5.8" + resolved "https://registry.yarnpkg.com/ejs/-/ejs-2.5.8.tgz#2ab6954619f225e6193b7ac5f7c39c48fefe4380" + electron-to-chromium@^1.2.7: version "1.3.26" resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.26.tgz#996427294861a74d9c7c82b9260ea301e8c02d66" @@ -3352,6 +3399,49 @@ eslint-visitor-keys@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-1.0.0.tgz#3f3180fb2e291017716acb4c9d6d5b5c34a6a81d" +eslint@^4.19.1: + version "4.19.1" + resolved "https://registry.yarnpkg.com/eslint/-/eslint-4.19.1.tgz#32d1d653e1d90408854bfb296f076ec7e186a300" + dependencies: + ajv "^5.3.0" + babel-code-frame "^6.22.0" + chalk "^2.1.0" + concat-stream "^1.6.0" + cross-spawn "^5.1.0" + debug "^3.1.0" + doctrine "^2.1.0" + eslint-scope "^3.7.1" + eslint-visitor-keys "^1.0.0" + espree "^3.5.4" + esquery "^1.0.0" + esutils "^2.0.2" + file-entry-cache "^2.0.0" + functional-red-black-tree "^1.0.1" + glob "^7.1.2" + globals "^11.0.1" + ignore "^3.3.3" + imurmurhash "^0.1.4" + inquirer "^3.0.6" + is-resolvable "^1.0.0" + js-yaml "^3.9.1" + json-stable-stringify-without-jsonify "^1.0.1" + levn "^0.3.0" + lodash "^4.17.4" + minimatch "^3.0.2" + mkdirp "^0.5.1" + natural-compare "^1.4.0" + optionator "^0.8.2" + path-is-inside "^1.0.2" + pluralize "^7.0.0" + progress "^2.0.0" + regexpp "^1.0.1" + require-uncached "^1.0.3" + semver "^5.3.0" + strip-ansi "^4.0.0" + strip-json-comments "~2.0.1" + table "4.0.2" + text-table "~0.2.0" + eslint@^4.5.0: version "4.13.1" resolved "https://registry.yarnpkg.com/eslint/-/eslint-4.13.1.tgz#0055e0014464c7eb7878caf549ef2941992b444f" @@ -3401,6 +3491,13 @@ espree@^3.5.2: acorn "^5.2.1" acorn-jsx "^3.0.0" +espree@^3.5.4: + version "3.5.4" + resolved "https://registry.yarnpkg.com/espree/-/espree-3.5.4.tgz#b0f447187c8a8bed944b815a660bddf5deb5d1a7" + dependencies: + acorn "^5.5.0" + acorn-jsx "^3.0.0" + esprima@3.x.x, esprima@^3.1.3: version "3.1.3" resolved "https://registry.yarnpkg.com/esprima/-/esprima-3.1.3.tgz#fdca51cee6133895e3c88d535ce49dbff62a4633" @@ -3575,6 +3672,20 @@ exports-loader@^0.6.4: loader-utils "^1.0.2" source-map "0.5.x" +express-session@^1.15.6: + version "1.15.6" + resolved "https://registry.yarnpkg.com/express-session/-/express-session-1.15.6.tgz#47b4160c88f42ab70fe8a508e31cbff76757ab0a" + dependencies: + cookie "0.3.1" + cookie-signature "1.0.6" + crc "3.4.4" + debug "2.6.9" + depd "~1.1.1" + on-headers "~1.0.1" + parseurl "~1.3.2" + uid-safe "~2.1.5" + utils-merge "1.0.1" + express-static-gzip@^0.3.1: version "0.3.2" resolved "https://registry.yarnpkg.com/express-static-gzip/-/express-static-gzip-0.3.2.tgz#89ede84547a5717de3146315f62dc996c071a88d" @@ -3616,6 +3727,41 @@ express@4.16.0, express@^4.12.2: utils-merge "1.0.1" vary "~1.1.2" +express@^4.16.3: + version "4.16.3" + resolved "https://registry.yarnpkg.com/express/-/express-4.16.3.tgz#6af8a502350db3246ecc4becf6b5a34d22f7ed53" + dependencies: + accepts "~1.3.5" + array-flatten "1.1.1" + body-parser "1.18.2" + content-disposition "0.5.2" + content-type "~1.0.4" + cookie "0.3.1" + cookie-signature "1.0.6" + debug "2.6.9" + depd "~1.1.2" + encodeurl "~1.0.2" + escape-html "~1.0.3" + etag "~1.8.1" + finalhandler "1.1.1" + fresh "0.5.2" + merge-descriptors "1.0.1" + methods "~1.1.2" + on-finished "~2.3.0" + parseurl "~1.3.2" + path-to-regexp "0.1.7" + proxy-addr "~2.0.3" + qs "6.5.1" + range-parser "~1.2.0" + safe-buffer "5.1.1" + send "0.16.2" + serve-static "1.13.2" + setprototypeof "1.1.0" + statuses "~1.4.0" + type-is "~1.6.16" + utils-merge "1.0.1" + vary "~1.1.2" + extend-shallow@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/extend-shallow/-/extend-shallow-2.0.1.tgz#51af7d614ad9a9f610ea1bafbb989d6b1c56890f" @@ -3813,6 +3959,18 @@ finalhandler@1.1.0: statuses "~1.3.1" unpipe "~1.0.0" +finalhandler@1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-1.1.1.tgz#eebf4ed840079c83f4249038c9d703008301b105" + dependencies: + debug "2.6.9" + encodeurl "~1.0.2" + escape-html "~1.0.3" + on-finished "~2.3.0" + parseurl "~1.3.2" + statuses "~1.4.0" + unpipe "~1.0.0" + find-cache-dir@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/find-cache-dir/-/find-cache-dir-1.0.0.tgz#9288e3e9e3cc3748717d39eade17cf71fc30ee6f" @@ -4123,6 +4281,16 @@ getpass@^0.1.1: dependencies: assert-plus "^1.0.0" +gigya@2.0.33: + version "2.0.33" + resolved "https://registry.yarnpkg.com/gigya/-/gigya-2.0.33.tgz#c5845cd16fac8ebcfb5e727e1ebe9e51352482fb" + dependencies: + "@types/lodash" "^4.14.50" + "@types/node" "^7.0.0" + "@types/request" "^0.0.39" + lodash "^4.17.4" + request "^2.79.0" + git-up@^2.0.0: version "2.0.9" resolved "https://registry.yarnpkg.com/git-up/-/git-up-2.0.9.tgz#219bfd27c82daeead8495beb386dc18eae63636d" @@ -5119,6 +5287,10 @@ ipaddr.js@1.5.2: version "1.5.2" resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.5.2.tgz#d4b505bde9946987ccf0fc58d9010ff9607e3fa0" +ipaddr.js@1.6.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.6.0.tgz#e3fa357b773da619f26e95f049d055c72796f86b" + is-absolute-url@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/is-absolute-url/-/is-absolute-url-2.1.0.tgz#50530dfb84fcc9aa7dbe7852e83a37b93b9f2aa6" @@ -6975,6 +7147,10 @@ merge@^1.1.3: version "1.2.0" resolved "https://registry.yarnpkg.com/merge/-/merge-1.2.0.tgz#7531e39d4949c281a66b8c5a6e0265e8b05894da" +mersenne-twister@^1.0.1: + version "1.1.0" + resolved "https://registry.yarnpkg.com/mersenne-twister/-/mersenne-twister-1.1.0.tgz#f916618ee43d7179efcf641bec4531eb9670978a" + metascraper-author@^3.9.2: version "3.9.2" resolved "https://registry.yarnpkg.com/metascraper-author/-/metascraper-author-3.9.2.tgz#ff2020ac428f59a875d655df3b0d4bea171fde19" @@ -7115,7 +7291,7 @@ miller-rabin@^4.0.0: version "1.30.0" resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.30.0.tgz#74c643da2dd9d6a45399963465b26d5ca7d71f01" -"mime-db@>= 1.33.0 < 2": +"mime-db@>= 1.33.0 < 2", mime-db@~1.33.0: version "1.33.0" resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.33.0.tgz#a3492050a5cb9b63450541e39d9788d2272783db" @@ -7125,6 +7301,12 @@ mime-types@^2.1.10, mime-types@^2.1.12, mime-types@~2.1.15, mime-types@~2.1.16, dependencies: mime-db "~1.30.0" +mime-types@~2.1.18: + version "2.1.18" + resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.18.tgz#6f323f60a83d11146f831ff11fd66e2fe5503bb8" + dependencies: + mime-db "~1.33.0" + mime@1.4.1, mime@^1.3.4, mime@^1.4.1: version "1.4.1" resolved "https://registry.yarnpkg.com/mime/-/mime-1.4.1.tgz#121f9ebc49e3766f311a76e1fa1c8003c4b03aa6" @@ -7257,6 +7439,10 @@ moment@^2.10.3: version "2.19.1" resolved "https://registry.yarnpkg.com/moment/-/moment-2.19.1.tgz#56da1a2d1cbf01d38b7e1afc31c10bcfa1929167" +moment@^2.15.2: + version "2.22.0" + resolved "https://registry.yarnpkg.com/moment/-/moment-2.22.0.tgz#7921ade01017dd45186e7fee5f424f0b8663a730" + mongodb-core@2.1.17: version "2.1.17" resolved "https://registry.yarnpkg.com/mongodb-core/-/mongodb-core-2.1.17.tgz#a418b337a14a14990fb510b923dee6a813173df8" @@ -8214,6 +8400,15 @@ passport-oauth2@1.x.x, passport-oauth2@^1.1.2: uid2 "0.0.x" utils-merge "1.x.x" +passport-openidconnect@^0.0.2: + version "0.0.2" + resolved "https://registry.yarnpkg.com/passport-openidconnect/-/passport-openidconnect-0.0.2.tgz#e488f8bdb386c9a9fd39c91d5ab8c880156e8153" + dependencies: + oauth "0.9.x" + passport-strategy "1.x.x" + request "^2.75.0" + webfinger "0.4.x" + passport-strategy@1.x.x, passport-strategy@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/passport-strategy/-/passport-strategy-1.0.0.tgz#b5539aa8fc225a3d1ad179476ddf236b440f52e4" @@ -8938,6 +9133,13 @@ proxy-addr@~2.0.2: forwarded "~0.1.2" ipaddr.js "1.5.2" +proxy-addr@~2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-2.0.3.tgz#355f262505a621646b3130a728eb647e22055341" + dependencies: + forwarded "~0.1.2" + ipaddr.js "1.6.0" + proxy-agent@2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/proxy-agent/-/proxy-agent-2.0.0.tgz#57eb5347aa805d74ec681cb25649dba39c933499" @@ -9188,6 +9390,10 @@ randexp@^0.4.2: discontinuous-range "1.0.0" ret "~0.1.10" +random-bytes@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/random-bytes/-/random-bytes-1.0.0.tgz#4f68a1dc0ae58bd3fb95848c30324db75d64360b" + randomatic@^1.1.3: version "1.1.7" resolved "https://registry.yarnpkg.com/randomatic/-/randomatic-1.1.7.tgz#c7abe9cc8b87c0baa876b19fde83fd464797e38c" @@ -9658,6 +9864,10 @@ regexp-clone@0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/regexp-clone/-/regexp-clone-0.0.1.tgz#a7c2e09891fdbf38fbb10d376fb73003e68ac589" +regexpp@^1.0.1: + version "1.1.0" + resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-1.1.0.tgz#0e3516dd0b7904f413d2d4193dce4618c3a689ab" + regexpu-core@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/regexpu-core/-/regexpu-core-1.0.0.tgz#86a763f58ee4d7c2f6b102e4764050de7ed90c6b" @@ -9808,6 +10018,33 @@ request@2.81.0: tunnel-agent "^0.6.0" uuid "^3.0.0" +request@^2.75.0: + version "2.85.0" + resolved "https://registry.yarnpkg.com/request/-/request-2.85.0.tgz#5a03615a47c61420b3eb99b7dba204f83603e1fa" + dependencies: + aws-sign2 "~0.7.0" + aws4 "^1.6.0" + caseless "~0.12.0" + combined-stream "~1.0.5" + extend "~3.0.1" + forever-agent "~0.6.1" + form-data "~2.3.1" + har-validator "~5.0.3" + hawk "~6.0.2" + http-signature "~1.2.0" + is-typedarray "~1.0.0" + isstream "~0.1.2" + json-stringify-safe "~5.0.1" + mime-types "~2.1.17" + oauth-sign "~0.8.2" + performance-now "^2.1.0" + qs "~6.5.1" + safe-buffer "^5.1.1" + stringstream "~0.0.5" + tough-cookie "~2.3.3" + tunnel-agent "^0.6.0" + uuid "^3.1.0" + require-directory@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" @@ -10022,7 +10259,7 @@ sax@0.5.x: version "0.5.8" resolved "https://registry.yarnpkg.com/sax/-/sax-0.5.8.tgz#d472db228eb331c2506b0e8c15524adb939d12c1" -sax@^1.1.4, sax@^1.2.1, sax@^1.2.4, sax@~1.2.1: +sax@>=0.1.1, sax@^1.1.4, sax@^1.2.1, sax@^1.2.4, sax@~1.2.1: version "1.2.4" resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9" @@ -10161,7 +10398,7 @@ serve-static@1.13.0: parseurl "~1.3.2" send "0.16.0" -serve-static@^1.10.0: +serve-static@1.13.2, serve-static@^1.10.0: version "1.13.2" resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-1.13.2.tgz#095e8472fd5b46237db50ce486a43f4b86c6cec1" dependencies: @@ -10578,6 +10815,10 @@ stealthy-require@^1.1.0: version "1.1.1" resolved "https://registry.yarnpkg.com/stealthy-require/-/stealthy-require-1.1.1.tgz#35b09875b4ff49f26a777e509b3090a3226bf24b" +step@0.0.x: + version "0.0.6" + resolved "https://registry.yarnpkg.com/step/-/step-0.0.6.tgz#143e7849a5d7d3f4a088fe29af94915216eeede2" + stream-browserify@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/stream-browserify/-/stream-browserify-2.0.1.tgz#66266ee5f9bdb9940a4e4514cafb43bb71e5c9db" @@ -10875,7 +11116,7 @@ symbol-observable@^1.0.2, symbol-observable@^1.0.3, symbol-observable@^1.0.4: version "3.2.2" resolved "https://registry.yarnpkg.com/symbol-tree/-/symbol-tree-3.2.2.tgz#ae27db38f660a7ae2e1c3b7d1bc290819b8519e6" -table@^4.0.1: +table@4.0.2, table@^4.0.1: version "4.0.2" resolved "https://registry.yarnpkg.com/table/-/table-4.0.2.tgz#a33447375391e766ad34d3486e6e2aedc84d2e36" dependencies: @@ -11195,6 +11436,13 @@ type-is@~1.6.15: media-typer "0.3.0" mime-types "~2.1.15" +type-is@~1.6.16: + version "1.6.16" + resolved "https://registry.yarnpkg.com/type-is/-/type-is-1.6.16.tgz#f89ce341541c672b25ee7ae3c73dee3b2be50194" + dependencies: + media-typer "0.3.0" + mime-types "~2.1.18" + typedarray@^0.0.6: version "0.0.6" resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" @@ -11269,6 +11517,12 @@ uid-number@^0.0.6: version "0.0.6" resolved "https://registry.yarnpkg.com/uid-number/-/uid-number-0.0.6.tgz#0ea10e8035e8eb5b8e4449f06da1c730663baa81" +uid-safe@~2.1.5: + version "2.1.5" + resolved "https://registry.yarnpkg.com/uid-safe/-/uid-safe-2.1.5.tgz#2b3d5c7240e8fc2e58f8aa269e5ee49c0857bd3a" + dependencies: + random-bytes "~1.0.0" + uid2@0.0.x: version "0.0.3" resolved "https://registry.yarnpkg.com/uid2/-/uid2-0.0.3.tgz#483126e11774df2f71b8b639dcd799c376162b82" @@ -11559,6 +11813,13 @@ watchpack@^1.4.0: chokidar "^1.7.0" graceful-fs "^4.1.2" +webfinger@0.4.x: + version "0.4.2" + resolved "https://registry.yarnpkg.com/webfinger/-/webfinger-0.4.2.tgz#3477a6d97799461896039fcffc650b73468ee76d" + dependencies: + step "0.0.x" + xml2js "0.1.x" + webidl-conversions@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-2.0.1.tgz#3bf8258f7d318c7443c36f2e169402a1a6703506" @@ -11795,6 +12056,12 @@ xml-name-validator@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/xml-name-validator/-/xml-name-validator-3.0.0.tgz#6ae73e06de4d8c6e47f9fb181f78d648ad457c6a" +xml2js@0.1.x: + version "0.1.14" + resolved "https://registry.yarnpkg.com/xml2js/-/xml2js-0.1.14.tgz#5274e67f5a64c5f92974cd85139e0332adc6b90c" + dependencies: + sax ">=0.1.1" + xml@^1.0.0, xml@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/xml/-/xml-1.0.1.tgz#78ba72020029c5bc87b8a81a3cfcd74b4a2fc1e5" From e6b8cd32203036674aaf2eedd290bbd23b6cb7a0 Mon Sep 17 00:00:00 2001 From: Wyatt Johnson Date: Thu, 5 Apr 2018 14:19:11 -0600 Subject: [PATCH 40/84] fixes related to external auth --- client/coral-framework/actions/auth.js | 5 +++++ client/coral-framework/constants/auth.js | 2 ++ client/coral-framework/reducers/auth.js | 9 +++++++++ client/coral-framework/services/bootstrap.js | 8 +++++++- 4 files changed, 23 insertions(+), 1 deletion(-) diff --git a/client/coral-framework/actions/auth.js b/client/coral-framework/actions/auth.js index 17b1c38f9..5390153e6 100644 --- a/client/coral-framework/actions/auth.js +++ b/client/coral-framework/actions/auth.js @@ -57,6 +57,10 @@ export const setAuthToken = token => (dispatch, _, { localStorage }) => { localStorage.setItem('token', token); } + // Dispatch the set auth token action. For some browsers and situations, we + // may not be able to persist the auth token any other way. Keep it in redux! + dispatch({ type: actions.SET_AUTH_TOKEN, token }); + dispatch(checkLogin()); }; @@ -87,6 +91,7 @@ export const handleSuccessfulLogin = (user, token) => ( dispatch({ type: actions.HANDLE_SUCCESSFUL_LOGIN, user, + token, }); }; diff --git a/client/coral-framework/constants/auth.js b/client/coral-framework/constants/auth.js index 25e2f6a78..b340924c3 100644 --- a/client/coral-framework/constants/auth.js +++ b/client/coral-framework/constants/auth.js @@ -1,5 +1,7 @@ const prefix = `TALK_FRAMEWORK`; +export const SET_AUTH_TOKEN = `${prefix}_SET_AUTH_TOKEN`; + export const CHECK_LOGIN_REQUEST = `${prefix}_CHECK_LOGIN_REQUEST`; export const CHECK_LOGIN_SUCCESS = `${prefix}_CHECK_LOGIN_SUCCESS`; export const CHECK_LOGIN_FAILURE = `${prefix}_CHECK_LOGIN_FAILURE`; diff --git a/client/coral-framework/reducers/auth.js b/client/coral-framework/reducers/auth.js index 3af606d0b..89478e30d 100644 --- a/client/coral-framework/reducers/auth.js +++ b/client/coral-framework/reducers/auth.js @@ -5,6 +5,7 @@ const initialState = { checkedInitialLogin: false, initialLoginError: null, user: null, + token: null, }; const purge = user => { @@ -14,12 +15,18 @@ const purge = user => { export default function auth(state = initialState, action) { switch (action.type) { + case actions.SET_AUTH_TOKEN: + return { + ...state, + token: action.token || null, + }; case actions.CHECK_LOGIN_FAILURE: return { ...state, initialLoginError: action.error, checkedInitialLogin: true, user: null, + token: null, }; case actions.CHECK_LOGIN_SUCCESS: return { @@ -31,11 +38,13 @@ export default function auth(state = initialState, action) { return { ...state, user: action.user ? purge(action.user) : null, + token: action.token || null, }; case actions.LOGOUT: return { ...state, user: null, + token: null, }; case actions.UPDATE_STATUS: { return { diff --git a/client/coral-framework/services/bootstrap.js b/client/coral-framework/services/bootstrap.js index e0bfbb950..9bfec2acd 100644 --- a/client/coral-framework/services/bootstrap.js +++ b/client/coral-framework/services/bootstrap.js @@ -47,6 +47,12 @@ const getAuthToken = (store, storage) => { } else if (!bowser.safari && !bowser.ios && storage) { // Use local storage auth tokens where there's a stable api. return storage.getItem('token'); + } else if (state.auth && state.auth.token) { + // Use the redux token state if the remaining methods fall out. If the embed + // is called with `embed.login(token)`, and the browser is not capable of + // storing the token in localStorage, then we would have persisted it to the + // redux state. + return state.auth.token; } return null; @@ -123,7 +129,7 @@ export async function createContext({ // Try to get the token from localStorage. If it isn't here, it may // be passed as a cookie. - // NOTE: THIS IS ONLY EVER EVALUATED ONCE, IN ORDER TO SEND A DIFFERNT + // NOTE: THIS IS ONLY EVER EVALUATED ONCE, IN ORDER TO SEND A DIFFERENT // TOKEN YOU MUST DISCONNECT AND RECONNECT THE WEBSOCKET CLIENT. return getAuthToken(store, localStorage); }; From ad56d3b99a65b629bdf1136456d1409fac089434 Mon Sep 17 00:00:00 2001 From: okbel Date: Thu, 5 Apr 2018 17:24:11 -0300 Subject: [PATCH 41/84] Removin actions, using routes, and continuing the transition when the user makes an action --- client/coral-admin/src/actions/configure.js | 4 --- client/coral-admin/src/constants/configure.js | 1 - client/coral-admin/src/reducers/configure.js | 6 ---- .../routes/Configure/components/Configure.js | 4 +-- .../routes/Configure/containers/Configure.js | 36 +++++++++++++------ 5 files changed, 27 insertions(+), 24 deletions(-) diff --git a/client/coral-admin/src/actions/configure.js b/client/coral-admin/src/actions/configure.js index 0f82832e0..47c7fd25f 100644 --- a/client/coral-admin/src/actions/configure.js +++ b/client/coral-admin/src/actions/configure.js @@ -8,10 +8,6 @@ export const clearPending = () => { return { type: actions.CLEAR_PENDING }; }; -export const setActiveSection = section => { - return { type: actions.SET_ACTIVE_SECTION, section }; -}; - export const showSaveDialog = () => { return { type: actions.SHOW_SAVE_DIALOG }; }; diff --git a/client/coral-admin/src/constants/configure.js b/client/coral-admin/src/constants/configure.js index ed5f74578..9ab22580d 100644 --- a/client/coral-admin/src/constants/configure.js +++ b/client/coral-admin/src/constants/configure.js @@ -2,7 +2,6 @@ const prefix = 'TALK_ADMIN_CONFIGURE'; export const UPDATE_PENDING = `${prefix}_UPDATE_PENDING`; export const CLEAR_PENDING = `${prefix}_CLEAR_PENDING`; -export const SET_ACTIVE_SECTION = `${prefix}_SET_ACTIVE_SECTION`; export const SHOW_SAVE_DIALOG = `${prefix}_SHOW_SAVE_DIALOG`; export const HIDE_SAVE_DIALOG = `${prefix}_HIDE_SAVE_DIALOG`; diff --git a/client/coral-admin/src/reducers/configure.js b/client/coral-admin/src/reducers/configure.js index 335cc4ce0..c87463423 100644 --- a/client/coral-admin/src/reducers/configure.js +++ b/client/coral-admin/src/reducers/configure.js @@ -6,7 +6,6 @@ const initialState = { canSave: false, pending: {}, errors: {}, - activeSection: 'stream', saveDialog: false, }; @@ -53,11 +52,6 @@ export default function configure(state = initialState, action) { pending: {}, canSave: false, }; - case actions.SET_ACTIVE_SECTION: - return { - ...state, - activeSection: action.section, - }; default: return state; } diff --git a/client/coral-admin/src/routes/Configure/components/Configure.js b/client/coral-admin/src/routes/Configure/components/Configure.js index 137f685cf..4d88f8420 100644 --- a/client/coral-admin/src/routes/Configure/components/Configure.js +++ b/client/coral-admin/src/routes/Configure/components/Configure.js @@ -29,7 +29,7 @@ class Configure extends React.Component { />
@@ -76,7 +76,7 @@ Configure.propTypes = { root: PropTypes.object.isRequired, settings: PropTypes.object.isRequired, canSave: PropTypes.bool.isRequired, - setActiveSection: PropTypes.func.isRequired, + handleSectionChange: PropTypes.func.isRequired, activeSection: PropTypes.string.isRequired, children: PropTypes.node.isRequired, saveDialog: PropTypes.bool, diff --git a/client/coral-admin/src/routes/Configure/containers/Configure.js b/client/coral-admin/src/routes/Configure/containers/Configure.js index 4affc53f5..1d1547373 100644 --- a/client/coral-admin/src/routes/Configure/containers/Configure.js +++ b/client/coral-admin/src/routes/Configure/containers/Configure.js @@ -12,7 +12,6 @@ import TechSettings from './TechSettings'; import ModerationSettings from './ModerationSettings'; import { clearPending, - setActiveSection, showSaveDialog, hideSaveDialog, } from '../../../actions/configure'; @@ -20,6 +19,8 @@ import Configure from '../components/Configure'; import { withRouter } from 'react-router'; class ConfigureContainer extends React.Component { + state = { nextRoute: '' }; + savePending = async () => { await this.props.updateSettings(this.props.pending); this.props.clearPending(); @@ -28,19 +29,32 @@ class ConfigureContainer extends React.Component { saveChanges = async () => { await this.savePending(); this.props.hideSaveDialog(); + this.gotoNextRoute(); }; - discardChanges = () => { - this.props.clearPending(); + discardChanges = async () => { + await this.props.clearPending(); this.props.hideSaveDialog(); + this.gotoNextRoute(); }; - setActiveSection = section => { + gotoNextRoute = () => { + const { nextRoute } = this.state; + if (nextRoute) { + this.props.router.push(nextRoute); + this.setState({ nextRoute: '' }); + } + }; + + handleSectionChange = async section => { + const nextRoute = `/admin/configure/${section}`; + if (this.shouldShowSaveDialog()) { + await this.setState({ nextRoute }); this.props.showSaveDialog(); } else { - this.props.setActiveSection(section); - this.props.router.push(`/admin/configure/${section}`); + // Just go to the section + this.props.router.push(nextRoute); } }; @@ -48,8 +62,9 @@ class ConfigureContainer extends React.Component { return !!Object.keys(this.props.pending).length; }; - routeLeave = () => { + routeLeave = ({ pathname }) => { if (this.shouldShowSaveDialog()) { + this.setState({ nextRoute: pathname }); this.props.showSaveDialog(); return false; } @@ -73,13 +88,13 @@ class ConfigureContainer extends React.Component { saveChanges={this.saveChanges} discardChanges={this.discardChanges} saveDialog={this.props.saveDialog} - activeSection={this.props.activeSection} + activeSection={this.props.routes[3].path} hideSaveDialog={this.props.hideSaveDialog} canSave={this.props.canSave} currentUser={this.props.currentUser} root={this.props.root} settings={this.props.mergedSettings} - setActiveSection={this.setActiveSection} + handleSectionChange={this.handleSectionChange} savePending={this.savePending} > {this.props.children} @@ -127,7 +142,6 @@ const mapDispatchToProps = dispatch => bindActionCreators( { clearPending, - setActiveSection, showSaveDialog, hideSaveDialog, }, @@ -146,7 +160,6 @@ ConfigureContainer.propTypes = { activeSection: PropTypes.string, updateSettings: PropTypes.func.isRequired, clearPending: PropTypes.func.isRequired, - setActiveSection: PropTypes.func.isRequired, showSaveDialog: PropTypes.func.isRequired, hideSaveDialog: PropTypes.func.isRequired, saveDialog: PropTypes.bool.isRequired, @@ -159,4 +172,5 @@ ConfigureContainer.propTypes = { children: PropTypes.node.isRequired, router: PropTypes.object, route: PropTypes.object, + routes: PropTypes.object, }; From 793b4736816e21fe9751bf5b7a1ff3e87b5dfb2c Mon Sep 17 00:00:00 2001 From: okbel Date: Thu, 5 Apr 2018 17:25:00 -0300 Subject: [PATCH 42/84] PropTypes --- client/coral-admin/src/routes/Configure/containers/Configure.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/coral-admin/src/routes/Configure/containers/Configure.js b/client/coral-admin/src/routes/Configure/containers/Configure.js index 1d1547373..9f980d31b 100644 --- a/client/coral-admin/src/routes/Configure/containers/Configure.js +++ b/client/coral-admin/src/routes/Configure/containers/Configure.js @@ -172,5 +172,5 @@ ConfigureContainer.propTypes = { children: PropTypes.node.isRequired, router: PropTypes.object, route: PropTypes.object, - routes: PropTypes.object, + routes: PropTypes.array, }; From 9bbcb6a073d0c26f6b7ff0dba22d611acb9c3a03 Mon Sep 17 00:00:00 2001 From: okbel Date: Fri, 6 Apr 2018 18:39:23 -0300 Subject: [PATCH 43/84] wip - inconcistencies with cache, considering refetching --- .../coral-admin/src/components/UserDetail.js | 2 ++ client/coral-framework/graphql/mutations.js | 35 ++++++++++++++++++- 2 files changed, 36 insertions(+), 1 deletion(-) diff --git a/client/coral-admin/src/components/UserDetail.js b/client/coral-admin/src/components/UserDetail.js index 22a85ac88..02de6f47b 100644 --- a/client/coral-admin/src/components/UserDetail.js +++ b/client/coral-admin/src/components/UserDetail.js @@ -96,6 +96,8 @@ class UserDetail extends React.Component { bulkReject, } = this.props; + console.log(rejectedComments, totalComments); + // if totalComments is 0, you're dividing by zero let rejectedPercent = rejectedComments / totalComments * 100; diff --git a/client/coral-framework/graphql/mutations.js b/client/coral-framework/graphql/mutations.js index 1b3b4c6fc..bd31be630 100644 --- a/client/coral-framework/graphql/mutations.js +++ b/client/coral-framework/graphql/mutations.js @@ -1,5 +1,6 @@ import { gql } from 'react-apollo'; import withMutation from '../hocs/withMutation'; +import update from 'immutability-helper'; function convertItemType(item_type) { switch (item_type) { @@ -167,9 +168,39 @@ export const withSetCommentStatus = withMutation( errors: null, }, }, + updateQueries: { + CoralAdmin_UserDetail: prev => { + const increment = { + rejectedComments: { + $apply: count => + count < prev.totalComments ? count + 1 : count, + }, + }; + + const decrement = { + rejectedComments: { + $apply: count => (count > 0 ? count - 1 : 0), + }, + }; + + // If rejected then increment rejectedComments by one + if (status === 'REJECTED') { + const updated = update(prev, increment); + return updated; + } + + // If approved then decrement rejectedComments by one + if (status === 'ACCEPTED') { + const updated = update(prev, decrement); + return updated; + } + + return prev; + }, + }, update: proxy => { const fragment = gql` - fragment Talk_SetCommentStatus on Comment { + fragment Talk_SetCommentStatus_Comment on Comment { status status_history { type @@ -182,9 +213,11 @@ export const withSetCommentStatus = withMutation( const data = proxy.readFragment({ fragment, id: fragmentId }); data.status = status; + data.status_history = data.status_history ? data.status_history : []; + data.status_history.push({ __typename: 'CommentStatusHistory', type: status, From 93ccec6614b192a38ad2c479303089e356baee1a Mon Sep 17 00:00:00 2001 From: Wyatt Johnson Date: Fri, 6 Apr 2018 16:19:10 -0600 Subject: [PATCH 44/84] Fixes #1505 --- bin/cli-assets | 32 +++++++++++++++++++------------- 1 file changed, 19 insertions(+), 13 deletions(-) diff --git a/bin/cli-assets b/bin/cli-assets index 78229d91b..210b9f22e 100755 --- a/bin/cli-assets +++ b/bin/cli-assets @@ -13,6 +13,7 @@ const CommentModel = require('../models/comment'); const AssetsService = require('../services/assets'); const mongoose = require('../services/mongoose'); const scraper = require('../services/scraper'); +const Context = require('../graph/context'); const inquirer = require('inquirer'); const { URL } = require('url'); @@ -52,22 +53,27 @@ async function refreshAssets(ageString) { const ageMs = parseDuration(ageString); const age = new Date(now - ageMs); - let assets = await AssetModel.find({ - $or: [ - { - scraped: { - $lte: age, + let assets = await AssetModel.find( + { + $or: [ + { + scraped: { + $lte: age, + }, }, - }, - { - scraped: null, - }, - ], - }); + { + scraped: null, + }, + ], + }, + { id: 1 } + ); + + // Create a graph context. + const ctx = Context.forSystem(); // Queue all the assets for scraping. - await Promise.all(assets.map(scraper.create)); - + await Promise.all(assets.map(({ id }) => scraper.create(ctx, id))); console.log('Assets were queued to be scraped'); util.shutdown(); } catch (e) { From a6518b183f10f3de83a388734183e9ad401af55c Mon Sep 17 00:00:00 2001 From: Ville Kauppi Date: Sun, 8 Apr 2018 15:05:49 +0300 Subject: [PATCH 45/84] Add Finnish translation --- locales/fi_FI.yml | 465 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 465 insertions(+) create mode 100644 locales/fi_FI.yml diff --git a/locales/fi_FI.yml b/locales/fi_FI.yml new file mode 100644 index 000000000..88f58479f --- /dev/null +++ b/locales/fi_FI.yml @@ -0,0 +1,465 @@ +fi_FI: + your_account_has_been_suspended: Tilisi on väliaikasesti suljettu. + your_account_has_been_banned: Tilillesi on asetettu kirjoituskielto. + your_username_has_been_rejected: Tilisi on suljettu, koska käyttäjänimesi on epäsopiva. Vaihda käyttäjänimeä jatkaaksesi tilin käyttöä. + embed_comments_tab: Kommentit + bandialog: + are_you_sure: "Haluatko varmasti asettaa kirjoituskiellon käyttäjätilille {0}?" + ban_user: "Estä käyttäjä?" + banned_user: "Estetty käyttäjä" + cancel: "Peruuta" + note: "Huom! {0}" + note_reject_comment: "Käyttäjän asettaminen kirjoituskieltoon asettaa myös kommentin Hylätyt-jonoon" + note_ban_user: "Käyttäjän kirjoituskielto estää kommentoinnin, kommentteihin reagoinnin, sekä kommenttien ilmiantamisen." + yes_ban_user: "Kyllä, aseta käyttäjälle kirjoituskielto" + write_a_message: "Kirjoita viesti" + send: "Lähetä" + notify_ban_headline: "Lähetä käyttäjälle ilmoitus kirjoituskiellosta" + notify_ban_description: "Tämä lähettää käyttäjälle sähköposti-ilmoituksen kirjoituskiellosta." + email_message_ban: "{0},\n\nTilläsi on rikottu kommentoinnin sääntöjä, jonka takia tilille on asetettu kirjoituskielto. Tiliä ei voida enää käyttää kommentointiin osallistumiseen tai kommenttien ilmiantamiseen. Ota yhteyttä moderointiin, jos tämä on tapahtunut mielestäsi väärin perustein." + bio_offensive: "Kuvaus on loukkaava" + cancel: "Peruuta" + confirm_email: + click_to_confirm: "Vahvista sähköpostiosoitteesi" + confirm: "Vahvista" + password_reset: + mail_sent: "Salasananvaihtolinkki on lähetetty rekisteröityyn sähköpostiosoitteeseen" + set_new_password: "Luo uusi salasana" + new_password: "Uusi salasana" + new_password_help: "Salasanan tulee olla vähintään 8 merkin pituinen" + confirm_new_password: "Vahvista uusi salasana" + change_password: "Vaihda salasana" + characters_remaining: "merkki(ä) jäljellä" + comment: + anon: "Anonyymi" + undo_reject: "Peruuta" + ban_user: "Estä käyttäjä" + comment: "Kommentoi" + edited: Muokattu + flagged: "ilmiannettu" + view_context: "Näytä konteksti" + comment_box: + post: "Lähetä" + cancel: "Peruuta" + reply: "Vastaa" + comment: "Kommentoi" + name: "Nimi" + comment_post_notif: "Kommenttisi on lähetetty." + comment_post_notif_premod: "Kiitos kommentistasi. Moderointitiimimme käsittelee kommenttisi mahdollisimman pian." + comment_post_banned_word: "Kommenttisi sisältää vähintään yhden kielletyn sanan, joten kommenttiasi ei tulla julkaisemaan. Jos tämä ilmoitus on mielestäsi aiheeton, olethan yhteydessä moderointitiimiimme." + characters_remaining: "merkki(ä) jäljellä" + comment_offensive: "Kommentti on loukkaava" + comment_singular: Kommentti + comment_plural: Kommentit + comment_post_banned_word: "Kommenttisi sisältää vähintään yhden kielletyn sanan, joten kommenttiasi ei tulla julkaisemaan. Jos tämä ilmoitus on mielestäsi aiheeton, olethan yhteydessä moderointitiimiimme." + comment_post_notif: "Kommenttisi on lähetetty." + comment_post_notif_premod: "Kiitos kommentistasi. Moderointitiimimme käsittelee kommenttisi mahdollisimman pian." + common: + copy: 'Kopioi' + error: 'Tapahtui virhe.' + reply: 'vastaa' + replies: 'vastaukset' + reaction: 'reaktio' + reactions: 'reaktiot' + story: 'Artikkeli' + flagged_usernames: + notify_approved: '{0} hyväksyi käyttäjänimen {1}' + notify_rejected: '{0} hylkäsi käyttäjänimen {1}' + notify_flagged: '{0} ilmiantoi käyttäjänimen {1}' + notify_changed: 'käyttäjä {0} vaihtoi käyttäjänimesä muotoon {1}' + community: + account_creation_date: "Tilin luontiaika" + active: Aktiivinen + admin: Ylläpitäjä + ads_marketing: "Vaikuttaa mainokselta" + are_you_sure: "Haluatka varmasti asettaa käyttäjlle {0} kirjoituskiellon?" + ban_user: "Estä käyttäjä?" + banned: Estetty + banned_user: "Estetty käyttäjä" + cancel: Peruuta + dont_like_username: "Epäsopiva käyttäjänimi" + flaggedaccounts: "Ilmiannetut käyttäjänimet" + flags: Liputuksia + impersonating: "Toiseksi tekeytyminen" + loading: "Ladataan tuloksia" + moderator: Moderaattori + newsroom_role: "Uutishuoneen rooli" + no_flagged_accounts: "Ilmiannettujen nimimerkkien jono on tyhjä." + no_results: "Antamallasi hakusanalla ei löydy yhtään käyttäjää." + offensive: "Loukkaava" + other: Muu + people: Käyttäjät + role: "Valitse rooli..." + select_status: "Valitse tila..." + spam_ads: "Roskapostit/mainokset" + staff: "Työntekijä" + status: Tila + username_and_email: "Käyttäjänimi ja sähköposti" + yes_ban_user: "Kyllä, estä käyttäjä" + commenter: "Kommentoija" + configure: + apply: Käytä + banned_word_text: "Näitä sanoja tai fraaseja sisältävät kommentit poistetaan automaattisesti. Lisää uusi kirjoittamalla sana ja painamalla enter- tai tab-näppäintä. Vaihtoehtoisesti kopio lista, jossa sanat on eroteltu pilkuilla." + banned_words_title: "Estettyjen sanojen lista." + close: "Sulje" + close_after: "Sulje kommentit, kun on kulunut" + close_stream: "Sulje kommentointi" + close_stream_configuration: "Kommentointi suljettu. Kommentointi on mahdollista, jos avaat kommentoinnin uudelleen." + closed_comments_desc: "Kirjoita viesti, joka näytetään, kun kommenttivirta on suljettu ja uusia viestejä ei voi enää lähettää." + closed_comments_label: "Kirjoita viesti..." + closed_stream_settings: "Suljetun keskustelun ilmoitusviesti" + comment_count_error: "Syötä numero." + comment_count_header: "Rajoita kommentin pituutta" + comment_count_text_post: merkkiin + comment_count_text_pre: "Kommentin pituus rajoitetaan" + comment_settings: Asetukset + comment_stream: "Kommentit" + comment_stream_will_close: "Kommentointi sulkeutuu" + community: Yhteisö + configure: "Muokkaa asetuksia" + copy_and_paste: "Kopio ja liitä koodi sisällönhallintajärjestelmääsi upottaaksesi kommenttiosion artikkeliin." + custom_css_url: "CSS-tiedoston URL" + custom_css_url_desc: "CSS-tiedoston URL, jonka sisällöllä ylikirjoitetaan oletustyylit. Voi olla sisäinen tai ulkoinen." + days: Päivää + description: "Ylläpitäjänä voit muokata tämän artikkelin kommentoinnin asetuksia:" + domain_list_text: "Syötä verkkotunnukset, joilla on lupa käyttää Talkia. Esimerkiksi lokaalikehitys-, QA- ja tuotantoympäristöt: localhost:3000 staging.domain.com domain.com." + domain_list_title: "Luviteut verkkotunnukset" + edit_comment_timeframe_heading: "Muokkaa kommentin muokkausaikaikkunaa" + edit_comment_timeframe_text_pre: "Kommentoijilla on" + edit_comment_timeframe_text_post: "sekuntia aikaa muokata kommenttejaan." + embed_comment_stream: "Upota keskustelu" + enable_premod_links_text: Moderaattorien tulee hyväksyä sellaisten kommenttien julkaisu, joissa on linkki. + enable_pre_moderation: "Esimoderointi päälle" + enable_pre_moderation_text: "Moderaattorien tulee hyväksyä kaikki julkaistavat kommentit." + enable_premod_links: "Esimoderoi kommentit, joissa on linkki" + enable_premod: "Esimoderointi päälle" + enable_premod_description: "Moderaattorien tulee hyväksyä kaikki julkaistavat kommentit." + enable_premod_links_description: "Moderaattorien tulee hyväksyä sellaisten kommenttien julkaisu, joissa on linkki." + enable_questionbox: "Kysy lukijoilta" + enable_questionbox_description: "Tämä kysymys tulee näkymään kommenttiosion ylälaidassa. Kysy artikkelin aiheesta tai ohjaa keskustelua kysymyksen avulla." + hours: Tuntia + include_comment_stream: "Sisällytä kommentoinnin kuvaus lukijoille" + include_comment_stream_desc: "Kirjoita kommenttiosion yläreunassa näkyvä viesti. Aseta keskustelun aihe, sisällytä sääntöjä tms." + include_text: "Lisää teksti tähän." + include_question_here: "Kirjoita kysymyksesi tähän:" + moderate: Moderoi + moderation_settings: "Moderointiasetukset" + open: "Avaa" + open_stream: "Avaa kommentointi" + open_stream_configuration: "Tämän artikkelin kommentointi on tällä hetkellä auki. Jos se suljetaan, ei kommentointi ole enää mahdollista, mutta vanhat kommentit jäävät näkyviin. + require_email_verification: "Vaadi sähköpostin vahvistus" + require_email_verification_text: "Uusien käyttäjien täytyy vahvistaa sähköpostiosoitteensa ennen kommentoinnin aloittamista" + save_changes: "Tallenna muutokset" + shortcuts: Pikalinkit + sign_out: "Kirjaudu ulos" + stories: Artikkelitarinat + stream_settings: "Kommentoinnin asetukset" + suspect_word_title: "Epäilyttävien sanojen lista" + suspect_word_text: "Nämä sanat tai fraasit näkyvät korostettuina kommenteissa. Lisää uusi kirjoittamalla sana ja painamalla enter- tai tab-näppäintä. Vaihtoehtoisesti kopio lista, jossa sanat on eroteltu pilkuilla." + tech_settings: "Tekniset asetukset" + title: "Muokkaa kommentoinnin asetuksia" + weeks: Viikkoa + wordlist: "Kielletyt sanat" + continue: "Jatka" + createdisplay: + check_the_form: "Tarkista syöttämäsi tiedot" + continue: "Käytä Facebook-käyttäjänimeä" + error_create: "Käyttäjänimen vaihdossa tapahtui virhe" + fake_comment_body: "Tämä on esimerkkikommentti. Lukijat voivat jakaa mielipiteitään ja näkemyksiään kommenttiosiossa." + fake_comment_date: "1 minuutti sitten" + if_you_dont_change_your_name: "Facebook-käyttäjänimesi näkyy kommenttiesi yhteydessä, ellet tässä vaiheessa vaihda käyttäjänimeäsi." + required_field: "Vaadittu tieto" + save: Tallenna + special_characters: "Käyttäjänimissä sallittuja merkkejä ovat ainoastaan kirjaimet, numerot, sekä alaviiva" + username: Käyttäjänimi + write_your_username: "Muokkaa käyttäjänimeäsi" + your_username: "Käyttäjänimesi näkyy jokaisen kommenttisi yhteydessä" + done: Valmis + edit_comment: + body_input_label: "Muokkaa tätä kommenttia" + save_button: "Tallenna muutokset" + edit_window_expired: "Et voi enää muokata tätä kommenttia, koska muokkauksen aikaikkuna on umpeutunut. Jätä sen sijaan uusi kommentti?" + edit_window_expired_close: "Sulje" + edit_window_timer_prefix: "Muokkaa aikaikkunaa: " + second: "sekunti" + seconds_plural: "sekuntia" + minute: "minuutti" + minutes_plural: "minuuttia" + email: + suspended: + subject: "Tilisi on väliaikaisesti suljettu" + banned: + subject: "Tilisi on asetettu kirjoituskieltoon" + body: "Tilisi on asetettu kirjoituskieltoon. Et voi osallistua keskusteluun kirjoituskiellon aikana." + confirm: + has_been_requested: "Sähköpostivahvistus on pyydetty tilille:" + to_confirm: "Vahvista tili klikkaamalla seuraavaa linkkiä:" + confirm_email: "Vahvista sähköposti" + if_you_did_not: "Jätä tämä viesti huomioimatta, jos et ole tehnyt pyyntöä." + subject: "Sähköpostin vahvistus" + password_reset: + we_received_a_request: "Tilisi salasanan vaihtoa on pyydetty. Jätä tämä viesti huomioimatta, jos et ole tehnyt pyyntöä." + if_you_did: "Jos pyysit," + please_click: "klikkaa tästä vaihtaaksesi salasanasi." + embedlink: + copy: "Kopioi leikepöydälle" + error: + COMMENT_PARENT_NOT_VISIBLE: "Kommenttia, johon yrität vastata, ei enää ole." + EMAIL_VERIFICATION_TOKEN_INVALID: "Sähköpostin vahvistusvarmiste on epävalidi." + PASSWORD_RESET_TOKEN_INVALID: "Salasananvaihtolinkki on epävalidi." + COMMENT_TOO_SHORT: "Kommentin tulee olla vähintään kaksi merkkiä pitkä. Tarkista kirjoittamasi teksti." + NOT_AUTHORIZED: "Sinulla ei ole oikeutta suorittaa tätä toimintoa." + NO_SPECIAL_CHARACTERS: "Käyttäjänimissä sallittuja merkkejä ovat ainoastaan kirjaimet, numerot, sekä alaviiva" + PASSWORD_LENGTH: "Salasana on liian lyhyt" + PROFANITY_ERROR: "Käyttäjänimet eivät saa sisältää hävyttömyyksiä. Ota yhteyttä ylläpitoon, jos mielestäsi on tapahtunut virhe." + RATE_LIMIT_EXCEEDED: "Raja-arvo on ylittynyt" + USERNAME_IN_USE: "Käyttäjänimi jo käytössä" + USERNAME_REQUIRED: "Syötä käyttäjänimi" + EMAIL_NOT_VERIFIED: "Sähköpostiosoitetta ei ole vahvistettu" + EDIT_WINDOW_ENDED: ""Et voi enää muokata tätä kommenttia, koska muokkauksen aikaikkuna on umpeutunut." + EDIT_USERNAME_NOT_AUTHORIZED: "Sinulla ei ole oikeutta päivittää tai muokata käyttäjänimeä." + SAME_USERNAME_PROVIDED: "Anna eri käyttäjänimi." + EMAIL_IN_USE: "Sähköpostiosoite on jo käytössä" + EMAIL_REQUIRED: "Syötä sähköpostiosoite" + LOGIN_MAXIMUM_EXCEEDED: "Olet tehnyt liian monta epäonnistunutta yritystä. Odota, ole hyvä." + PASSWORD_REQUIRED: "Syötä salasana" + COMMENTING_CLOSED: "Kommentointi on suljettu" + NOT_FOUND: "Resurssia ei löydy" + ALREADY_EXISTS: "Resurssi on jo olemassa" + INVALID_ASSET_URL: "Tarkista tiedoston URL" + CANNOT_IGNORE_STAFF: "Työntekijöitä ei voi jättää huomioimatta" + email: "Tarkista sähköpostiosoite" + confirm_password: "Salasanat eivät täsmää. Tarkista, ole hyvä." + network_error: "Palvelimeen yhdistäminen epäonnistui. Tarkista internetyhteytesi." + email_not_verified: "Sähköpostiosoitetta {0} ei ole vahvistettu." + email_password: "Sähköpostiosoite ja/tai salasana on väärä." + organization_name: "Organisaation nimessä voi käyttää vain kirjaimia ja numeroita." + password: "Salasanan tulee olla vähintään 8 merkkiä pitkä" + username: "Käyttäjänimissä sallittuja merkkejä ovat ainoastaan kirjaimet, numerot, sekä alaviiva" + unexpected: "Tapahtui odottamaton virhe. Pahiottelemme!" + required_field: "Tämä on vaadittu kenttä" + temporarily_suspended: "Tilisi on suljettu väliaikaisesti. Se aktivoituu uudelleen {0}. Ota yhteyttä, jos on sinulla on aiheesta kysyttävää." + flag_comment: "Ilmianna kommentti" + flag_reason: "Ilmiannon syy (ei pakollinen)" + flag_username: "Ilmianna käyttäjänimi" + framework: + banned_account_header: "Tilisi on kirjoituskiellossa" + banned_account_body: "Et pysty kirjoittamaan tai ilmiantamaan kommentteja." + comment: kommentti + comment_is_ignored: "Tämä kommentti on piilossa, koska olet päättänyt jättää kommentin kirjoittajan huomiotta." + comment_is_rejected: "Olet hylännyt tämän kommentin." + comment_is_hidden: "Tämä kommentti ei ole saatavilla." + comments: kommentit + configure_stream: "Muokkaa asetuksia" + content_not_available: "Sisältö ei ole saatavilla" + edit_name: + button: Lähetä + error: "Käyttäjänimissä sallittuja merkkejä ovat ainoastaan kirjaimet, numerot, sekä alaviiva" + label: "Uusi käyttäjänimi" + msg: "Tilisi on suljettu väliaikaisesti, koska käyttäjänimi on todettu sopimattomaksi. Vaihda käyttäjänimi, jos haluat jatkaa tilin käyttöä. Ole meihin yhteydessä, jos sinulla on aiheesta kysyttävää." + changed_name: + msg: "Käyttäjänimen vaihto on moderointitiimillämme tarkistuksessa." + my_comments: "Kommenttini" + my_profile: "Profiilini" + new_count: "Näytä {0} lisää {1}" + profile: Profiili + show_all_comments: "Näytä kaikki kommentit" + success_bio_update: "Kuvauksesi on päivitetty" + success_name_update: "Käyttäjänimesi on päivitetty" + success_update_settings: "Tekemäsi muutokset on otettu käyttöön" + show_all_replies: Näytä kaikki vastaukset + show_more_replies: Näytä lisää vastauksia + view_more_comments: "näytä lisää kommentteja" + view_reply: "näytä vastaus" + from_settings_page: "Näet kommentointihistoriasi profiilisivulta." + like: Tykkää + loading_results: "Ladataan tuloksia" + marketing: "Vaikuttaa mainokselta" + moderate_this_stream: "Moderoi tätä kommentointia" + flags: + reasons: + user: + username_offensive: "Loukkaava" + username_nolike: "En tykkää" + username_impersonating: "Toisena esiintyminen" + username_spam: "Roskaviesti" + username_other: "Muu" + comment: + comment_offensive: "Loukkaava" + comment_spam: "Roskaviesti" + comment_noagree: "Olen eri mieltä" + comment_other: "Muu" + suspect_word: "Epäilyttävä sana" + banned_word: "Kielletty sana" + body_count: "Liian pitkä viesti" + trust: "Luotettava" + links: "Linkki" + modqueue: + account: "Liputuksia" + actions: Toiminnot + all: kaikki + all_streams: "Kaikki keskustelut" + notify_edited: '{0} muokkasi kommenttia "{1}"' + notify_accepted: '{0} hyväksyi kommentin "{1}"' + notify_rejected: '{0} hylkäsi kommentin "{1}"' + notify_flagged: '{0} ilmiantoi kommentin "{1}"' + notify_reset: '{0} tyhjensi kommentin "{1}" tilan' + approve: "Hyväksy" + approved: "Hyväksytty" + ban_user: "Kirjoituskielto käyttäjälle" + billion: mrd + close: Sulje + empty_queue: "Moderointijono on tyhjä." + flagged: liputettu + reported: raportoitu + less_detail: "Vähemmän yksityiskohtia" + likes: tykkäyksiä + million: milj. + mod_faster: "Moderoi nopeammin käyttäen pikanäppäimiä" + moderate: "Moderoi →" + more_detail: "Enemmän yksityiskohtia" + new: Uusi + newest_first: "Uusin ensin" + navigation: Navioginti + next_comment: "Seuraava kommentti" + toggle_search: "Avaa haku" + next_queue: "Vaihda jonoa" + oldest_first: "Vanhin ensin" + premod: esimoderoi + prev_comment: "Edellinen kommentti" + reject: "Hylkää" + rejected: "Hylätty" + reply: "Vastaa" + select_stream: "Valitse kommenttivirta" + shift_key: "⇧" + shortcuts: "Pikalinkit" + sort: "Järjestä" + show_shortcuts: "Näytä pikalinkit" + singleview: "Zen-moodi" + thismenu: "Avaa valikko" + jump_to_queue: "Siirry tiettyyn jonoon" + thousand: tuhatta + try_these: "Kokeile näitä" + view_more_shortcuts: "Näytä enemmän pikalinkkejä" + my_comment_history: "Kommentointihistoriani" + name: Nimi + no_agree_comment: "En ole samaa mieltä" + no_like_bio: "En pidä kuvauksesta" + no_like_username: "En pidä käyttäjänimestä" + already_flagged_username: "Olet jo ilmiantanut tämän käyttäjänimen." + other: Muu + permalink: Jaa + personal_info: "Kommentti sisältää henkilökohtaisesti tunnistettavia tietoja" + post: Lähetä + profile: Profiili + profile_settings: Profiiliasetukset + reply: Vastaa + report: Ilmianna + report_notif: "Kiitos ilmiannosta. Moderointitiimimme käsittelee tapauksen mahdollisimman pian." + report_notif_remove: "Ilmiantosi on poistettu." + reported: Ilmiannettu + settings: + from_settings_page: "Näet kommenttihistoriasi profiilisivultasi." + my_comment_history: "Kommenttihistoriani" + profile: Profiili + profile_settings: "Profiiliasetukset" + sign_in: "Kirjaudu sisään" + to_access: "päästäksesi profiilisivulle" + user_no_comment: "Et ole jättänyt yhtään kommenttia. Liity keskusteluun!" + stream: + all_comments: "Kaikki kommentit" + temporarily_suspended: "Tilisi on väliaikasesti suljettu, koska et ole noudattanut {0}-sivuston sääntöjä. Voit liittyä keskusteluun uudelleen {1}." + comment_not_found: "Kommenttia ei ole olemassa." + no_comments: "Ei vielä kommentteja." + no_comments_and_closed: "Tässä artikkelissa ei vielä ollut kommentteja." + step_1_header: "Ilmianna ongelma" + step_2_header: "Auta meitä ymmärtämään" + step_3_header: "Kiitos panostuksestasi" + streams: + all: Kaikki + article: Artikkeli + closed: Suljettu + empty_result: "Ei hakutuloksia. Kokeile laajentaa hakuasi." + filter_streams: "Suodata kommenttivirtoja" + newest: Uusin + oldest: Vanhin + open: Avoin + pubdate: "Julkaisupäivä" + search: Haku + sort_by: "Järjestä" + status: "Kommentoinnin tila" + stream_status: "Kommentoinnin tila" + suspenduser: + title_suspend: "Aseta väliaikainen käyttökielto" + description_suspend: "Olet asettamassa käyttäjälle {0} väliaikasta käyttökieltoa. Tämä kommentti menee hylätyt-jonoon, ja käyttäjä {0} ei voi reagoida kommentteihin, ilmiantaa, tai vastata niihin, kunnes käyttökielto on päättynyt." + select_duration: "Käyttökiellon kesto" + one_hour: "1 tunti" + hours: "{0} tuntia" + days: "{0} päivää" + cancel: "Peruuta" + suspend_user: "Aseta väliaikainen käyttökielto" + email_message_suspend: "Hyvä {0}, tilisi on asetettu {1}-sivuston sääntöjenmukaiseen käyttökieltoon. Et voi osallistua keskusteluun käyttökiellon aikana. Voit liittyä keskusteluun uudelleen {2}." + title_notify: "Lähetä käyttäjälle tieto asetetusta käyttökiellosta" + notify_suspend_until: "Käyttäjä {0} on asetettu väliaikaiseen käyttökieltoon. Kielto päättyy automaattisesti {1}." + description_notify: "Käyttökiellon asettaminen sulkee tilin väliaikaisesti." + write_message: "Kirjoita viesti" + send: Lähetä + reject_username: + username: käyttäjänimi + no_cancel: "En, peruuta" + description_reject: "Haluatko asettaa käyttökiellon, syynä {0}? Jos haluat, asetetaan käyttäjätili väliaikaseen käyttökieltoon, kunnes {0} on kirjoitettu uudelleen." + title_notify: "Lähetä käyttäjälle tieto asetetusta käyttökiellosta" + description_notify: "Käyttökiellon asettaminen sulkee tilin väliaikaisesti." + title_reject: "Huomasimme sinun hylänneen käyttäjänimen" + suspend_user: "Aseta väliaikainen käyttökielto" + yes_suspend: "Kyllä, sulje väliaikaisesti" + email_message_reject: "Toinen yhteisön jäsen on ilmiantanut käyttäjänimesi ja sen perusteella nimi on hylätty. Et voi enää osallistua keskusteluun. Ole ystävällisesti yhteydessä meihin, jos sinulla on asiasta kysyttävää." + write_message: "Kirjoita viesti" + send: Lähetä + thank_you: "Arvostamme palautettasi. Moderaattorimme käy läpi tekemäsi ilmiannon." + user: + bio_flags: "liputusta kuvaukselle" + user_bio: "Käyttäjän kuvaus" + username_flags: "liputusta käyttäjänimelle" + user_detail: + remove_suspension: "Poista käyttökielto" + suspend: "Aseta väliaikainen käyttökielto" + remove_ban: "Poista kirjoituskielto" + ban: "Aseta kirjoituskielto" + member_since: "Jäsenenä lähtien" + email: "Sähköposti" + total_comments: "Kommentteja yhteensä" + reject_rate: "Hylkäysaste" + reports: "Raportit" + all: "Kaikki" + rejected: "Hylätyt" + account_history: "Tilin historia" + user_impersonating: "Käyttäjä on tekeytynyt toiseksi" + user_no_comment: "Et ole jättänyt yhtään kommenttia. Liity mukaan keskusteluun!" + username_offensive: "Käyttäjänimi on loukkaava" + view_conversation: "Näytä keskustelu" + install: + initial: + description: "Ota Talk käyttöön, vain muutama askel jäljellä" + submit: "Aloita käyttö" + add_organization: + description: "Kerro organisaatiosi nimi. Tämä näkyy uusien jäsenten kutsuissa." + label: "Organisaation nimi" + save: "Tallenna" + create: + email: "Sähköpostiosoite" + username: "Käyttäjänimi" + password: "Salasana" + confirm_password: "Salasana uudelleen" + save: "Tallenna" + permitted_domains: + title: "Sallitut domainit" + description: "Syötä domainit, joilla on lupa käyttää Talkia. Esimerkiksi lokaalikehitys-, QA- ja tuotantoympäristöt: localhost:3000 staging.domain.com domain.com." + submit: "Lopeta asennus" + final: + description: "Kiitos kun asensit Talkin! Lähetämme sähköpostinvarmistusviestin antamaasi osoitteeseen. Voit nyt aloittaa kommentoinnin käytön." + launch: "Käynnistä Talk" + close: "Sulje asennusnäkymä" + admin_sidebar: + view_options: "Näytä asetukset" + sort_comments: "Järjestä kommentit" From 4694f986c059f076a00f6cea1fd14f6e092c5779 Mon Sep 17 00:00:00 2001 From: Wyatt Johnson Date: Mon, 9 Apr 2018 13:22:02 -0600 Subject: [PATCH 46/84] Logging Improvements - development logging prints pretty again - http requests are logged using bunyan now --- app.js | 4 +- middleware/logging.js | 39 ++++++ package.json | 3 +- routes/index.js | 15 +-- services/logging.js | 42 +++++- yarn.lock | 298 +++++++++++++++++++++++++++++++++++++++++- 6 files changed, 373 insertions(+), 28 deletions(-) create mode 100644 middleware/logging.js diff --git a/app.js b/app.js index c2bf9648d..47555f728 100644 --- a/app.js +++ b/app.js @@ -1,5 +1,5 @@ const express = require('express'); -const morgan = require('morgan'); +const logging = require('./middleware/logging'); const path = require('path'); const merge = require('lodash/merge'); const helmet = require('helmet'); @@ -30,7 +30,7 @@ plugins.get('server', 'app').forEach(({ plugin, app: callback }) => { // Add the logging middleware only if we aren't testing. if (process.env.NODE_ENV !== 'test') { - app.use(morgan('dev')); + app.use(logging.log); } if (ENABLE_TRACING && APOLLO_ENGINE_KEY) { diff --git a/middleware/logging.js b/middleware/logging.js new file mode 100644 index 000000000..ec1ca6bb0 --- /dev/null +++ b/middleware/logging.js @@ -0,0 +1,39 @@ +const { logger } = require('../services/logging'); +const now = require('performance-now'); + +const log = (req, res, next) => { + const startTime = now(); + const end = res.end; + res.end = function(chunk, encoding) { + // Compute the end time. + const responseTime = Math.round(now() - startTime); + + // Get some extra goodies from the request. + const userAgent = req.get('User-Agent'); + + // Reattach the old end, and finish. + res.end = end; + res.end(chunk, encoding); + + // Log this out. + logger.info( + { + url: req.originalUrl || req.url, + method: req.method, + statusCode: res.statusCode, + userAgent, + responseTime, + }, + 'http request' + ); + }; + + next(); +}; + +const error = (err, req, res, next) => { + logger.error({ err }, 'http error'); + next(err); +}; + +module.exports = { log, error }; diff --git a/package.json b/package.json index d331f62b4..29a198194 100644 --- a/package.json +++ b/package.json @@ -146,7 +146,6 @@ "minimist": "^1.2.0", "moment": "^2.18.1", "mongoose": "^4.12.3", - "morgan": "^1.9.0", "ms": "^2.0.0", "murmurhash-js": "^1.0.0", "name-all-modules-plugin": "^1.0.1", @@ -158,6 +157,7 @@ "passport": "^0.4.0", "passport-jwt": "^3.0.0", "passport-local": "^1.0.0", + "performance-now": "^2.1.0", "pluralize": "^7.0.0", "postcss-loader": "^1.3.3", "postcss-smart-import": "^0.5.1", @@ -215,6 +215,7 @@ "babel-plugin-dynamic-import-node": "^1.1.0", "babel-plugin-transform-es2015-modules-commonjs": "^6.26.0", "browserstack-local": "^1.3.0", + "bunyan-debug-stream": "^1.0.8", "chai": "^3.5.0", "chai-as-promised": "^6.0.0", "chai-datetime": "^1.5.0", diff --git a/routes/index.js b/routes/index.js index 489a5e8b5..2d0a73b43 100644 --- a/routes/index.js +++ b/routes/index.js @@ -1,7 +1,7 @@ const SetupService = require('../services/setup'); const authentication = require('../middleware/authentication'); +const logging = require('../middleware/logging'); const cookieParser = require('cookie-parser'); -const enabled = require('debug').enabled; const errors = require('../errors'); const express = require('express'); const i18n = require('../middleware/i18n'); @@ -152,15 +152,12 @@ router.use((req, res, next) => { next(errors.ErrNotFound); }); +// Add logging for errors. +router.use(logging.error); + // General API error handler. Respond with the message and error if we have it // while returning a status code that makes sense. router.use('/api', (err, req, res, next) => { - if (err !== errors.ErrNotFound) { - if (process.env.NODE_ENV !== 'test' || enabled('talk:errors')) { - console.error(err); - } - } - if (err instanceof errors.APIError) { res.status(err.status).json({ message: res.locals.t(`error.${err.translation_key}`), @@ -172,10 +169,6 @@ router.use('/api', (err, req, res, next) => { }); router.use('/', (err, req, res, next) => { - if (err !== errors.ErrNotFound) { - console.error(err); - } - if (err instanceof errors.APIError) { res.status(err.status); res.render('error', { diff --git a/services/logging.js b/services/logging.js index 47ef93af8..3e2a6d731 100644 --- a/services/logging.js +++ b/services/logging.js @@ -1,18 +1,46 @@ const { version } = require('../package.json'); -const Logger = require('bunyan'); +const path = require('path'); +const { createLogger: createBunyanLogger, stdSerializers } = require('bunyan'); const { LOGGING_LEVEL, REVISION_HASH } = require('../config'); -const logger = new Logger({ + +// Streams enables the ability for development logs to be readable to a human, +// but will send JSON logs in production that's parsable by a system like ELK. +const streams = (() => { + // In development, use the debug stream printer. + if (process.env.NODE_ENV === 'development') { + const debug = require('bunyan-debug-stream'); + return [ + { + level: 'debug', + type: 'raw', + stream: debug({ + basepath: path.resolve(__dirname, '..'), + forceColor: true, + }), + }, + ]; + } + + // In production, emit JSON. + return [{ stream: process.stdout, level: 'info' }]; +})(); + +// logger is the base logger used by all logging systems in Talk. +const logger = createBunyanLogger({ src: true, name: 'talk', version, revision: REVISION_HASH, level: LOGGING_LEVEL, - serializers: Logger.stdSerializers, + streams, + serializers: stdSerializers, }); -// Create the logging instance that all logger's are branched from. -function createLogger(name, traceID) { - return logger.child({ origin: name, traceID }); -} +/** + * + * @param {String} origin the origin name used by the logger + * @param {String} traceID the id of the request being made + */ +const createLogger = (origin, traceID) => logger.child({ origin, traceID }); module.exports = { logger, createLogger }; diff --git a/yarn.lock b/yarn.lock index 07a395260..b5d130ab0 100644 --- a/yarn.lock +++ b/yarn.lock @@ -68,7 +68,7 @@ lodash "^4.2.0" to-fast-properties "^2.0.0" -"@coralproject/eslint-config-talk@^0.1.0": +"@coralproject/eslint-config-talk@^0.1.0", "@coralproject/eslint-config-talk@^0.1.1": version "0.1.1" resolved "https://registry.yarnpkg.com/@coralproject/eslint-config-talk/-/eslint-config-talk-0.1.1.tgz#71991b4937a3ffe657128d7f1170da4b5fb75c9e" dependencies: @@ -126,6 +126,12 @@ to-title-case "~1.0.0" url-regex "~4.1.1" +"@types/form-data@*": + version "2.2.1" + resolved "https://registry.yarnpkg.com/@types/form-data/-/form-data-2.2.1.tgz#ee2b3b8eaa11c0938289953606b745b738c54b1e" + dependencies: + "@types/node" "*" + "@types/graphql@0.10.2": version "0.10.2" resolved "https://registry.yarnpkg.com/@types/graphql/-/graphql-0.10.2.tgz#d7c79acbaa17453b6681c80c34b38fcb10c4c08c" @@ -138,10 +144,25 @@ version "0.9.4" resolved "https://registry.yarnpkg.com/@types/graphql/-/graphql-0.9.4.tgz#cdeb6bcbef9b6c584374b81aa7f48ecf3da404fa" +"@types/lodash@^4.14.50": + version "4.14.106" + resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.106.tgz#6093e9a02aa567ddecfe9afadca89e53e5dce4dd" + "@types/node@*": version "8.0.53" resolved "https://registry.yarnpkg.com/@types/node/-/node-8.0.53.tgz#396b35af826fa66aad472c8cb7b8d5e277f4e6d8" +"@types/node@^7.0.0": + version "7.0.59" + resolved "https://registry.yarnpkg.com/@types/node/-/node-7.0.59.tgz#fd7dceba9521c2d62c3e0eda8c5d704bf88b261d" + +"@types/request@^0.0.39": + version "0.0.39" + resolved "https://registry.yarnpkg.com/@types/request/-/request-0.0.39.tgz#168b96cf4253c5d54d403f746f82ee7aed47ce2c" + dependencies: + "@types/form-data" "*" + "@types/node" "*" + "@types/ws@^3.0.0": version "3.2.0" resolved "https://registry.yarnpkg.com/@types/ws/-/ws-3.2.0.tgz#988ff690e6ed10068a86aa0e9f842d0a03c09e21" @@ -174,6 +195,13 @@ accepts@^1.3.4, accepts@~1.3.4: mime-types "~2.1.16" negotiator "0.6.1" +accepts@~1.3.5: + version "1.3.5" + resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.5.tgz#eb777df6011723a3b14e8a72c0805c8e86746bd2" + dependencies: + mime-types "~2.1.18" + negotiator "0.6.1" + acorn-dynamic-import@^2.0.0: version "2.0.2" resolved "https://registry.yarnpkg.com/acorn-dynamic-import/-/acorn-dynamic-import-2.0.2.tgz#c752bd210bef679501b6c6cb7fc84f8f47158cc4" @@ -228,6 +256,10 @@ acorn@^5.3.0: version "5.4.1" resolved "https://registry.yarnpkg.com/acorn/-/acorn-5.4.1.tgz#fdc58d9d17f4a4e98d102ded826a9b9759125102" +acorn@^5.5.0: + version "5.5.3" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-5.5.3.tgz#f473dd47e0277a08e28e9bec5aeeb04751f0b8c9" + addressparser@1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/addressparser/-/addressparser-1.0.1.tgz#47afbe1a2a9262191db6838e4fd1d39b40821746" @@ -1652,6 +1684,13 @@ builtin-status-codes@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz#85982878e21b98e1c66425e03d0174788f569ee8" +bunyan-debug-stream@^1.0.8: + version "1.0.8" + resolved "https://registry.yarnpkg.com/bunyan-debug-stream/-/bunyan-debug-stream-1.0.8.tgz#df612852d5d0b6d6df3f30214d8a7e4ee925106d" + dependencies: + colors "^1.0.3" + exception-formatter "^1.0.4" + bunyan@^1.8.12: version "1.8.12" resolved "https://registry.yarnpkg.com/bunyan/-/bunyan-1.8.12.tgz#f150f0f6748abdd72aeae84f04403be2ef113797" @@ -1770,6 +1809,13 @@ caseless@~0.12.0: version "0.12.0" resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc" +casual@^1.5.19: + version "1.5.19" + resolved "https://registry.yarnpkg.com/casual/-/casual-1.5.19.tgz#66fac46f7ae463f468f5913eb139f9c41c58bbf2" + dependencies: + mersenne-twister "^1.0.1" + moment "^2.15.2" + center-align@^0.1.1: version "0.1.3" resolved "https://registry.yarnpkg.com/center-align/-/center-align-0.1.3.tgz#aa0d32629b6ee972200411cbd4461c907bc2b7ad" @@ -2135,6 +2181,10 @@ colors@1.0.3, colors@1.0.x: version "1.0.3" resolved "https://registry.yarnpkg.com/colors/-/colors-1.0.3.tgz#0433f44d809680fdeb60ed260f1b0c262e82a40b" +colors@^1.0.3: + version "1.2.1" + resolved "https://registry.yarnpkg.com/colors/-/colors-1.2.1.tgz#f4a3d302976aaf042356ba1ade3b1a2c62d9d794" + colors@^1.1.2, colors@~1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/colors/-/colors-1.1.2.tgz#168a4701756b6a7f51a12ce0c97bfa28c084ed63" @@ -2427,6 +2477,10 @@ cosmiconfig@^4.0.0, cosmiconfig@~4.0.0: parse-json "^4.0.0" require-from-string "^2.0.1" +crc@3.4.4: + version "3.4.4" + resolved "https://registry.yarnpkg.com/crc/-/crc-3.4.4.tgz#9da1e980e3bd44fc5c93bf5ab3da3378d85e466b" + create-ecdh@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/create-ecdh/-/create-ecdh-4.0.0.tgz#888c723596cdf7612f6498233eebd7a35301737d" @@ -2916,7 +2970,7 @@ dns-prefetch-control@0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/dns-prefetch-control/-/dns-prefetch-control-0.1.0.tgz#60ddb457774e178f1f9415f0cabb0e85b0b300b2" -doctrine@^2.0.0: +doctrine@^2.0.0, doctrine@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-2.1.0.tgz#5cd01fc101621b42c4cd7f5d1a66243716d3f39d" dependencies: @@ -3057,6 +3111,10 @@ ejs@2.5.7, ejs@^2.5.7: version "2.5.7" resolved "https://registry.yarnpkg.com/ejs/-/ejs-2.5.7.tgz#cc872c168880ae3c7189762fd5ffc00896c9518a" +ejs@^2.5.8: + version "2.5.8" + resolved "https://registry.yarnpkg.com/ejs/-/ejs-2.5.8.tgz#2ab6954619f225e6193b7ac5f7c39c48fefe4380" + electron-to-chromium@^1.2.7: version "1.3.26" resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.26.tgz#996427294861a74d9c7c82b9260ea301e8c02d66" @@ -3352,6 +3410,49 @@ eslint-visitor-keys@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-1.0.0.tgz#3f3180fb2e291017716acb4c9d6d5b5c34a6a81d" +eslint@^4.19.1: + version "4.19.1" + resolved "https://registry.yarnpkg.com/eslint/-/eslint-4.19.1.tgz#32d1d653e1d90408854bfb296f076ec7e186a300" + dependencies: + ajv "^5.3.0" + babel-code-frame "^6.22.0" + chalk "^2.1.0" + concat-stream "^1.6.0" + cross-spawn "^5.1.0" + debug "^3.1.0" + doctrine "^2.1.0" + eslint-scope "^3.7.1" + eslint-visitor-keys "^1.0.0" + espree "^3.5.4" + esquery "^1.0.0" + esutils "^2.0.2" + file-entry-cache "^2.0.0" + functional-red-black-tree "^1.0.1" + glob "^7.1.2" + globals "^11.0.1" + ignore "^3.3.3" + imurmurhash "^0.1.4" + inquirer "^3.0.6" + is-resolvable "^1.0.0" + js-yaml "^3.9.1" + json-stable-stringify-without-jsonify "^1.0.1" + levn "^0.3.0" + lodash "^4.17.4" + minimatch "^3.0.2" + mkdirp "^0.5.1" + natural-compare "^1.4.0" + optionator "^0.8.2" + path-is-inside "^1.0.2" + pluralize "^7.0.0" + progress "^2.0.0" + regexpp "^1.0.1" + require-uncached "^1.0.3" + semver "^5.3.0" + strip-ansi "^4.0.0" + strip-json-comments "~2.0.1" + table "4.0.2" + text-table "~0.2.0" + eslint@^4.5.0: version "4.13.1" resolved "https://registry.yarnpkg.com/eslint/-/eslint-4.13.1.tgz#0055e0014464c7eb7878caf549ef2941992b444f" @@ -3401,6 +3502,13 @@ espree@^3.5.2: acorn "^5.2.1" acorn-jsx "^3.0.0" +espree@^3.5.4: + version "3.5.4" + resolved "https://registry.yarnpkg.com/espree/-/espree-3.5.4.tgz#b0f447187c8a8bed944b815a660bddf5deb5d1a7" + dependencies: + acorn "^5.5.0" + acorn-jsx "^3.0.0" + esprima@3.x.x, esprima@^3.1.3: version "3.1.3" resolved "https://registry.yarnpkg.com/esprima/-/esprima-3.1.3.tgz#fdca51cee6133895e3c88d535ce49dbff62a4633" @@ -3480,6 +3588,12 @@ evp_bytestokey@^1.0.0, evp_bytestokey@^1.0.3: md5.js "^1.3.4" safe-buffer "^5.1.1" +exception-formatter@^1.0.4: + version "1.0.5" + resolved "https://registry.yarnpkg.com/exception-formatter/-/exception-formatter-1.0.5.tgz#bda957319789cbabdf36848fb5288c59634b73a5" + dependencies: + colors "^1.0.3" + exec-sh@^0.2.0: version "0.2.1" resolved "https://registry.yarnpkg.com/exec-sh/-/exec-sh-0.2.1.tgz#163b98a6e89e6b65b47c2a28d215bc1f63989c38" @@ -3575,6 +3689,20 @@ exports-loader@^0.6.4: loader-utils "^1.0.2" source-map "0.5.x" +express-session@^1.15.6: + version "1.15.6" + resolved "https://registry.yarnpkg.com/express-session/-/express-session-1.15.6.tgz#47b4160c88f42ab70fe8a508e31cbff76757ab0a" + dependencies: + cookie "0.3.1" + cookie-signature "1.0.6" + crc "3.4.4" + debug "2.6.9" + depd "~1.1.1" + on-headers "~1.0.1" + parseurl "~1.3.2" + uid-safe "~2.1.5" + utils-merge "1.0.1" + express-static-gzip@^0.3.1: version "0.3.2" resolved "https://registry.yarnpkg.com/express-static-gzip/-/express-static-gzip-0.3.2.tgz#89ede84547a5717de3146315f62dc996c071a88d" @@ -3616,6 +3744,41 @@ express@4.16.0, express@^4.12.2: utils-merge "1.0.1" vary "~1.1.2" +express@^4.16.3: + version "4.16.3" + resolved "https://registry.yarnpkg.com/express/-/express-4.16.3.tgz#6af8a502350db3246ecc4becf6b5a34d22f7ed53" + dependencies: + accepts "~1.3.5" + array-flatten "1.1.1" + body-parser "1.18.2" + content-disposition "0.5.2" + content-type "~1.0.4" + cookie "0.3.1" + cookie-signature "1.0.6" + debug "2.6.9" + depd "~1.1.2" + encodeurl "~1.0.2" + escape-html "~1.0.3" + etag "~1.8.1" + finalhandler "1.1.1" + fresh "0.5.2" + merge-descriptors "1.0.1" + methods "~1.1.2" + on-finished "~2.3.0" + parseurl "~1.3.2" + path-to-regexp "0.1.7" + proxy-addr "~2.0.3" + qs "6.5.1" + range-parser "~1.2.0" + safe-buffer "5.1.1" + send "0.16.2" + serve-static "1.13.2" + setprototypeof "1.1.0" + statuses "~1.4.0" + type-is "~1.6.16" + utils-merge "1.0.1" + vary "~1.1.2" + extend-shallow@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/extend-shallow/-/extend-shallow-2.0.1.tgz#51af7d614ad9a9f610ea1bafbb989d6b1c56890f" @@ -3813,6 +3976,18 @@ finalhandler@1.1.0: statuses "~1.3.1" unpipe "~1.0.0" +finalhandler@1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-1.1.1.tgz#eebf4ed840079c83f4249038c9d703008301b105" + dependencies: + debug "2.6.9" + encodeurl "~1.0.2" + escape-html "~1.0.3" + on-finished "~2.3.0" + parseurl "~1.3.2" + statuses "~1.4.0" + unpipe "~1.0.0" + find-cache-dir@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/find-cache-dir/-/find-cache-dir-1.0.0.tgz#9288e3e9e3cc3748717d39eade17cf71fc30ee6f" @@ -4123,6 +4298,16 @@ getpass@^0.1.1: dependencies: assert-plus "^1.0.0" +gigya@2.0.33: + version "2.0.33" + resolved "https://registry.yarnpkg.com/gigya/-/gigya-2.0.33.tgz#c5845cd16fac8ebcfb5e727e1ebe9e51352482fb" + dependencies: + "@types/lodash" "^4.14.50" + "@types/node" "^7.0.0" + "@types/request" "^0.0.39" + lodash "^4.17.4" + request "^2.79.0" + git-up@^2.0.0: version "2.0.9" resolved "https://registry.yarnpkg.com/git-up/-/git-up-2.0.9.tgz#219bfd27c82daeead8495beb386dc18eae63636d" @@ -5119,6 +5304,10 @@ ipaddr.js@1.5.2: version "1.5.2" resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.5.2.tgz#d4b505bde9946987ccf0fc58d9010ff9607e3fa0" +ipaddr.js@1.6.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.6.0.tgz#e3fa357b773da619f26e95f049d055c72796f86b" + is-absolute-url@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/is-absolute-url/-/is-absolute-url-2.1.0.tgz#50530dfb84fcc9aa7dbe7852e83a37b93b9f2aa6" @@ -6975,6 +7164,10 @@ merge@^1.1.3: version "1.2.0" resolved "https://registry.yarnpkg.com/merge/-/merge-1.2.0.tgz#7531e39d4949c281a66b8c5a6e0265e8b05894da" +mersenne-twister@^1.0.1: + version "1.1.0" + resolved "https://registry.yarnpkg.com/mersenne-twister/-/mersenne-twister-1.1.0.tgz#f916618ee43d7179efcf641bec4531eb9670978a" + metascraper-author@^3.9.2: version "3.9.2" resolved "https://registry.yarnpkg.com/metascraper-author/-/metascraper-author-3.9.2.tgz#ff2020ac428f59a875d655df3b0d4bea171fde19" @@ -7115,7 +7308,7 @@ miller-rabin@^4.0.0: version "1.30.0" resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.30.0.tgz#74c643da2dd9d6a45399963465b26d5ca7d71f01" -"mime-db@>= 1.33.0 < 2": +"mime-db@>= 1.33.0 < 2", mime-db@~1.33.0: version "1.33.0" resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.33.0.tgz#a3492050a5cb9b63450541e39d9788d2272783db" @@ -7125,6 +7318,12 @@ mime-types@^2.1.10, mime-types@^2.1.12, mime-types@~2.1.15, mime-types@~2.1.16, dependencies: mime-db "~1.30.0" +mime-types@~2.1.18: + version "2.1.18" + resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.18.tgz#6f323f60a83d11146f831ff11fd66e2fe5503bb8" + dependencies: + mime-db "~1.33.0" + mime@1.4.1, mime@^1.3.4, mime@^1.4.1: version "1.4.1" resolved "https://registry.yarnpkg.com/mime/-/mime-1.4.1.tgz#121f9ebc49e3766f311a76e1fa1c8003c4b03aa6" @@ -7257,6 +7456,10 @@ moment@^2.10.3: version "2.19.1" resolved "https://registry.yarnpkg.com/moment/-/moment-2.19.1.tgz#56da1a2d1cbf01d38b7e1afc31c10bcfa1929167" +moment@^2.15.2: + version "2.22.0" + resolved "https://registry.yarnpkg.com/moment/-/moment-2.22.0.tgz#7921ade01017dd45186e7fee5f424f0b8663a730" + mongodb-core@2.1.17: version "2.1.17" resolved "https://registry.yarnpkg.com/mongodb-core/-/mongodb-core-2.1.17.tgz#a418b337a14a14990fb510b923dee6a813173df8" @@ -7294,7 +7497,7 @@ moo-server@*, moo-server@1.3.x: version "1.3.0" resolved "https://registry.yarnpkg.com/moo-server/-/moo-server-1.3.0.tgz#5dc79569565a10d6efed5439491e69d2392e58f1" -morgan@^1.6.1, morgan@^1.9.0: +morgan@^1.6.1: version "1.9.0" resolved "https://registry.yarnpkg.com/morgan/-/morgan-1.9.0.tgz#d01fa6c65859b76fcf31b3cb53a3821a311d8051" dependencies: @@ -8214,6 +8417,15 @@ passport-oauth2@1.x.x, passport-oauth2@^1.1.2: uid2 "0.0.x" utils-merge "1.x.x" +passport-openidconnect@^0.0.2: + version "0.0.2" + resolved "https://registry.yarnpkg.com/passport-openidconnect/-/passport-openidconnect-0.0.2.tgz#e488f8bdb386c9a9fd39c91d5ab8c880156e8153" + dependencies: + oauth "0.9.x" + passport-strategy "1.x.x" + request "^2.75.0" + webfinger "0.4.x" + passport-strategy@1.x.x, passport-strategy@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/passport-strategy/-/passport-strategy-1.0.0.tgz#b5539aa8fc225a3d1ad179476ddf236b440f52e4" @@ -8938,6 +9150,13 @@ proxy-addr@~2.0.2: forwarded "~0.1.2" ipaddr.js "1.5.2" +proxy-addr@~2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-2.0.3.tgz#355f262505a621646b3130a728eb647e22055341" + dependencies: + forwarded "~0.1.2" + ipaddr.js "1.6.0" + proxy-agent@2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/proxy-agent/-/proxy-agent-2.0.0.tgz#57eb5347aa805d74ec681cb25649dba39c933499" @@ -9188,6 +9407,10 @@ randexp@^0.4.2: discontinuous-range "1.0.0" ret "~0.1.10" +random-bytes@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/random-bytes/-/random-bytes-1.0.0.tgz#4f68a1dc0ae58bd3fb95848c30324db75d64360b" + randomatic@^1.1.3: version "1.1.7" resolved "https://registry.yarnpkg.com/randomatic/-/randomatic-1.1.7.tgz#c7abe9cc8b87c0baa876b19fde83fd464797e38c" @@ -9658,6 +9881,10 @@ regexp-clone@0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/regexp-clone/-/regexp-clone-0.0.1.tgz#a7c2e09891fdbf38fbb10d376fb73003e68ac589" +regexpp@^1.0.1: + version "1.1.0" + resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-1.1.0.tgz#0e3516dd0b7904f413d2d4193dce4618c3a689ab" + regexpu-core@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/regexpu-core/-/regexpu-core-1.0.0.tgz#86a763f58ee4d7c2f6b102e4764050de7ed90c6b" @@ -9808,6 +10035,33 @@ request@2.81.0: tunnel-agent "^0.6.0" uuid "^3.0.0" +request@^2.75.0: + version "2.85.0" + resolved "https://registry.yarnpkg.com/request/-/request-2.85.0.tgz#5a03615a47c61420b3eb99b7dba204f83603e1fa" + dependencies: + aws-sign2 "~0.7.0" + aws4 "^1.6.0" + caseless "~0.12.0" + combined-stream "~1.0.5" + extend "~3.0.1" + forever-agent "~0.6.1" + form-data "~2.3.1" + har-validator "~5.0.3" + hawk "~6.0.2" + http-signature "~1.2.0" + is-typedarray "~1.0.0" + isstream "~0.1.2" + json-stringify-safe "~5.0.1" + mime-types "~2.1.17" + oauth-sign "~0.8.2" + performance-now "^2.1.0" + qs "~6.5.1" + safe-buffer "^5.1.1" + stringstream "~0.0.5" + tough-cookie "~2.3.3" + tunnel-agent "^0.6.0" + uuid "^3.1.0" + require-directory@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" @@ -10022,7 +10276,7 @@ sax@0.5.x: version "0.5.8" resolved "https://registry.yarnpkg.com/sax/-/sax-0.5.8.tgz#d472db228eb331c2506b0e8c15524adb939d12c1" -sax@^1.1.4, sax@^1.2.1, sax@^1.2.4, sax@~1.2.1: +sax@>=0.1.1, sax@^1.1.4, sax@^1.2.1, sax@^1.2.4, sax@~1.2.1: version "1.2.4" resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9" @@ -10161,7 +10415,7 @@ serve-static@1.13.0: parseurl "~1.3.2" send "0.16.0" -serve-static@^1.10.0: +serve-static@1.13.2, serve-static@^1.10.0: version "1.13.2" resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-1.13.2.tgz#095e8472fd5b46237db50ce486a43f4b86c6cec1" dependencies: @@ -10578,6 +10832,10 @@ stealthy-require@^1.1.0: version "1.1.1" resolved "https://registry.yarnpkg.com/stealthy-require/-/stealthy-require-1.1.1.tgz#35b09875b4ff49f26a777e509b3090a3226bf24b" +step@0.0.x: + version "0.0.6" + resolved "https://registry.yarnpkg.com/step/-/step-0.0.6.tgz#143e7849a5d7d3f4a088fe29af94915216eeede2" + stream-browserify@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/stream-browserify/-/stream-browserify-2.0.1.tgz#66266ee5f9bdb9940a4e4514cafb43bb71e5c9db" @@ -10875,7 +11133,7 @@ symbol-observable@^1.0.2, symbol-observable@^1.0.3, symbol-observable@^1.0.4: version "3.2.2" resolved "https://registry.yarnpkg.com/symbol-tree/-/symbol-tree-3.2.2.tgz#ae27db38f660a7ae2e1c3b7d1bc290819b8519e6" -table@^4.0.1: +table@4.0.2, table@^4.0.1: version "4.0.2" resolved "https://registry.yarnpkg.com/table/-/table-4.0.2.tgz#a33447375391e766ad34d3486e6e2aedc84d2e36" dependencies: @@ -11195,6 +11453,13 @@ type-is@~1.6.15: media-typer "0.3.0" mime-types "~2.1.15" +type-is@~1.6.16: + version "1.6.16" + resolved "https://registry.yarnpkg.com/type-is/-/type-is-1.6.16.tgz#f89ce341541c672b25ee7ae3c73dee3b2be50194" + dependencies: + media-typer "0.3.0" + mime-types "~2.1.18" + typedarray@^0.0.6: version "0.0.6" resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" @@ -11269,6 +11534,12 @@ uid-number@^0.0.6: version "0.0.6" resolved "https://registry.yarnpkg.com/uid-number/-/uid-number-0.0.6.tgz#0ea10e8035e8eb5b8e4449f06da1c730663baa81" +uid-safe@~2.1.5: + version "2.1.5" + resolved "https://registry.yarnpkg.com/uid-safe/-/uid-safe-2.1.5.tgz#2b3d5c7240e8fc2e58f8aa269e5ee49c0857bd3a" + dependencies: + random-bytes "~1.0.0" + uid2@0.0.x: version "0.0.3" resolved "https://registry.yarnpkg.com/uid2/-/uid2-0.0.3.tgz#483126e11774df2f71b8b639dcd799c376162b82" @@ -11559,6 +11830,13 @@ watchpack@^1.4.0: chokidar "^1.7.0" graceful-fs "^4.1.2" +webfinger@0.4.x: + version "0.4.2" + resolved "https://registry.yarnpkg.com/webfinger/-/webfinger-0.4.2.tgz#3477a6d97799461896039fcffc650b73468ee76d" + dependencies: + step "0.0.x" + xml2js "0.1.x" + webidl-conversions@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-2.0.1.tgz#3bf8258f7d318c7443c36f2e169402a1a6703506" @@ -11795,6 +12073,12 @@ xml-name-validator@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/xml-name-validator/-/xml-name-validator-3.0.0.tgz#6ae73e06de4d8c6e47f9fb181f78d648ad457c6a" +xml2js@0.1.x: + version "0.1.14" + resolved "https://registry.yarnpkg.com/xml2js/-/xml2js-0.1.14.tgz#5274e67f5a64c5f92974cd85139e0332adc6b90c" + dependencies: + sax ">=0.1.1" + xml@^1.0.0, xml@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/xml/-/xml-1.0.1.tgz#78ba72020029c5bc87b8a81a3cfcd74b4a2fc1e5" From 3fcad354923a8ddf20530234231e52d1da9e2f1e Mon Sep 17 00:00:00 2001 From: okbel Date: Mon, 9 Apr 2018 16:27:02 -0300 Subject: [PATCH 47/84] autocomplete off --- client/coral-ui/components/TextField.js | 3 +++ plugins/talk-plugin-auth/client/login/components/SignUp.js | 4 ++++ 2 files changed, 7 insertions(+) diff --git a/client/coral-ui/components/TextField.js b/client/coral-ui/components/TextField.js index 6cbb7a06b..2ba23e6e8 100644 --- a/client/coral-ui/components/TextField.js +++ b/client/coral-ui/components/TextField.js @@ -27,11 +27,14 @@ const TextField = ({ ); TextField.propTypes = { + id: PropTypes.string, label: PropTypes.string, value: PropTypes.string, onChange: PropTypes.func, errorMsg: PropTypes.string, type: PropTypes.string, + className: PropTypes.string, + showErrors: PropTypes.bool, }; export default TextField; diff --git a/plugins/talk-plugin-auth/client/login/components/SignUp.js b/plugins/talk-plugin-auth/client/login/components/SignUp.js index cc49e7391..2a6de0c24 100644 --- a/plugins/talk-plugin-auth/client/login/components/SignUp.js +++ b/plugins/talk-plugin-auth/client/login/components/SignUp.js @@ -75,6 +75,7 @@ class SignUp extends React.Component { showErrors={!!emailError} errorMsg={emailError} onChange={this.handleEmailChange} + autocomplete="off" /> {passwordError && ( @@ -113,6 +116,7 @@ class SignUp extends React.Component { errorMsg={passwordRepeatError} onChange={this.handlePasswordRepeatChange} minLength="8" + autocomplete="off" /> Date: Mon, 9 Apr 2018 13:59:23 -0600 Subject: [PATCH 48/84] improved support --- app.js | 5 +++ middleware/logging.js | 1 + middleware/trace.js | 7 +++++ services/mongoose.js | 71 ++++++++++++++++++++----------------------- services/redis.js | 25 +++++++-------- 5 files changed, 59 insertions(+), 50 deletions(-) create mode 100644 middleware/trace.js diff --git a/app.js b/app.js index 47555f728..c5507da2e 100644 --- a/app.js +++ b/app.js @@ -1,4 +1,5 @@ const express = require('express'); +const trace = require('./middleware/trace'); const logging = require('./middleware/logging'); const path = require('path'); const merge = require('lodash/merge'); @@ -12,6 +13,10 @@ const { ENABLE_TRACING, APOLLO_ENGINE_KEY, PORT } = require('./config'); const app = express(); +// Add the trace middleware first, it will create a request ID for each request +// downstream. +app.use(trace); + //============================================================================== // PLUGIN PRE APPLICATION MIDDLEWARE //============================================================================== diff --git a/middleware/logging.js b/middleware/logging.js index ec1ca6bb0..efabbe34f 100644 --- a/middleware/logging.js +++ b/middleware/logging.js @@ -18,6 +18,7 @@ const log = (req, res, next) => { // Log this out. logger.info( { + traceID: req.id, url: req.originalUrl || req.url, method: req.method, statusCode: res.statusCode, diff --git a/middleware/trace.js b/middleware/trace.js new file mode 100644 index 000000000..24cbf38f4 --- /dev/null +++ b/middleware/trace.js @@ -0,0 +1,7 @@ +const uuid = require('uuid/v1'); + +// Trace middleware attaches a request id to each incoming request. +module.exports = (req, res, next) => { + req.id = uuid(); + next(); +}; diff --git a/services/mongoose.js b/services/mongoose.js index 20765293a..5e498cee8 100644 --- a/services/mongoose.js +++ b/services/mongoose.js @@ -1,48 +1,40 @@ -const { MONGO_URL, WEBPACK, CREATE_MONGO_INDEXES } = require('../config'); - +const { + MONGO_URL, + WEBPACK, + CREATE_MONGO_INDEXES, + LOGGING_LEVEL, +} = require('../config'); +const { logger } = require('./logging'); const mongoose = require('mongoose'); -const debug = require('debug')('talk:db'); -const enabled = require('debug').enabled; -const queryDebugger = require('debug')('talk:db:query'); - -// Loading the formatter from Mongoose: -// -// https://github.com/Automattic/mongoose/blob/1a93d1f4d12e441e17ddf451e96fbc5f6e8f54b8/lib/drivers/node-mongodb-native/collection.js#L182 -// -// so we can wrap parameters. -const formatter = require('mongoose').Collection.prototype.$format; // Provide a newly wrapped debugQuery function which wraps the `debug` package. -function debugQuery(name, i, ...args) { - let functionCall = ['db', name, i].join('.'); - let _args = []; - for (let j = args.length - 1; j >= 0; --j) { - if (formatter(args[j]) || _args.length) { - _args.unshift(formatter(args[j])); - } - } - - let params = `(${_args.join(', ')})`; - - queryDebugger(functionCall + params); +function debugQuery(name, operation, ...args) { + logger.debug( + { + query: `db.${name}.${operation}(${args + .map(arg => JSON.stringify(arg)) + .join(', ')})`, + }, + 'mongodb query' + ); } // Use native promises mongoose.Promise = global.Promise; -// Check if debugging is enabled on the talk:db prefix. -if (enabled('talk:db:query')) { +// Check if verbose logging is enabled. +if (['debug', 'trace'].includes(LOGGING_LEVEL)) { // Enable the mongoose debugger, here we wrap the similar print function // provided by setting the debug parameter. mongoose.set('debug', debugQuery); } if (WEBPACK) { - debug('Not connecting to mongodb during webpack build'); + logger.debug('Not connecting to mongodb during webpack build'); - // @wyattjoh: We didn't call connect, but because we include mongoose, it will hold the socket ready, - // preventing node from exiting. Calling disconnect here just ensures that the application - // can quit correctly. + // @wyattjoh: We didn't call connect, but because we include mongoose, it will + // hold the socket ready, preventing node from exiting. Calling disconnect + // here just ensures that the application can quit correctly. mongoose.disconnect(); } else { // Connect to the Mongo instance. @@ -54,7 +46,7 @@ if (WEBPACK) { }, }) .then(() => { - debug('connection established'); + logger.debug('mongodb connection established'); }) .catch(err => { console.error(err); @@ -66,10 +58,13 @@ module.exports = mongoose; // Here we include all the models that mongoose is used for, this ensures that // when we import mongoose that we also start up all the indexing operations -// here. -require('../models/action'); -require('../models/asset'); -require('../models/comment'); -require('../models/setting'); -require('../models/user'); -require('./migration'); +// here. No point also in importing this if we're not actually doing any +// indexing now. +if (CREATE_MONGO_INDEXES) { + require('../models/action'); + require('../models/asset'); + require('../models/comment'); + require('../models/setting'); + require('../models/user'); + require('./migration'); +} diff --git a/services/redis.js b/services/redis.js index 2576d2c13..0a25b34d6 100644 --- a/services/redis.js +++ b/services/redis.js @@ -1,7 +1,5 @@ const Redis = require('ioredis'); const merge = require('lodash/merge'); -const debug = require('debug')('talk:services:redis'); -const enabled = require('debug').enabled('talk:services:redis'); const { REDIS_URL, REDIS_RECONNECTION_BACKOFF_FACTOR, @@ -9,29 +7,32 @@ const { REDIS_CLIENT_CONFIG, REDIS_CLUSTER_MODE, REDIS_CLUSTER_CONFIGURATION, + LOGGING_LEVEL, } = require('../config'); +const { createLogger } = require('./logging'); +const logger = createLogger('redis'); const attachMonitors = client => { - debug('client created'); + logger.debug('client created'); // Debug events. - if (enabled) { - client.on('connect', () => debug('client connected')); - client.on('ready', () => debug('client ready')); - client.on('close', () => debug('client closed the connection')); + if (['debug', 'trace'].includes(LOGGING_LEVEL)) { + client.on('connect', () => logger.info('client connected')); + client.on('ready', () => logger.debug('client ready')); + client.on('close', () => logger.debug('client closed the connection')); client.on('reconnecting', () => - debug('client connection lost, attempting to reconnect') + logger.debug('client connection lost, attempting to reconnect') ); - client.on('end', () => debug('client ended')); + client.on('end', () => logger.debug('client ended')); } // Error events. client.on('error', err => { if (err) { - console.error('Error connecting to redis:', err); + logger.error({ err }, 'cannot connect to redis'); } }); - client.on('node error', err => debug('node error', err)); + client.on('node error', err => logger.error({ err }, 'node error')); }; function retryStrategy(times) { @@ -40,7 +41,7 @@ function retryStrategy(times) { REDIS_RECONNECTION_BACKOFF_MINIMUM_TIME ); - debug(`retry strategy: try to reconnect ${delay} ms from now`); + logger.debug(`retry strategy: try to reconnect ${delay} ms from now`); return delay; } From ffde113a74cde6ab9c86fc9e858441f125f70128 Mon Sep 17 00:00:00 2001 From: cecile Date: Mon, 9 Apr 2018 17:53:07 -0700 Subject: [PATCH 49/84] add missing strings in fr locale --- locales/fr.yml | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/locales/fr.yml b/locales/fr.yml index dd14bea81..6ffa9d35b 100644 --- a/locales/fr.yml +++ b/locales/fr.yml @@ -23,6 +23,7 @@ fr: click_to_confirm: "Click below to confirm your email address" confirm: "Confirm" password_reset: + mail_sent: 'If you have a registered account, a password reset link was sent to that email' set_new_password: "Change Your Password" new_password: "New Password" new_password_help: "Password must be at least 8 characters" @@ -153,12 +154,19 @@ fr: sign_out: "Se Déconnecter" stories: Histoires stream_settings: "Paramètres du fil" + access_message: "You must be an administrator to access config settings. Please find the nearest Admin and ask them to level you up!" suspect_word_title: "Liste des mots suspects" suspect_word_text: "Les commentaires contenant ces mots ou expressions (non sensibles à la casse) seront mis en évidence dans le flux de commentaires. Tapez un mot et appuyez sur Entrée ou Tabulation pour ajouter. En option, entrez une liste séparée par des virgules." tech_settings: "Paramètres techniques" title: "Configurer le fil de commentaires" weeks: Semaines wordlist: "Mots interdits" + save_changes_dialog: + unsaved_changes: "Unsaved changes" + copy: "You have made one or more changes without saving. Would you like to save or discard your changes?" + save_settings: "Save Settings" + discard: "Discard" + cancel: "Cancel" continue: Continuer createdisplay: check_the_form: "Invalid Form. Please check the fields" @@ -205,6 +213,7 @@ fr: error: COMMENT_PARENT_NOT_VISIBLE: "The comment that you're replying to has been removed or doesn't exist." EMAIL_VERIFICATION_TOKEN_INVALID: "Email verification token is invalid." + EMAIL_ALREADY_VERIFIED: "Email address already verified." PASSWORD_RESET_TOKEN_INVALID: "Your password reset link is invalid." COMMENT_TOO_SHORT: "Votre commentaire doit contenir quelque chose" NOT_AUTHORIZED: "Vous n'êtes pas autorisé à effectuer cette action." @@ -357,6 +366,9 @@ fr: report_notif: "Merci de signaler ce commentaire. Notre équipe de modération a é té informée." report_notif_remove: "Votre signalement a été supprimé." reported: Signalé + comment_history_blank: + title: You have not written any comments + info: A history of your comments will appear here settings: from_settings_page: "Dans la page Profil, vous pouvez voir l'historique de vos commentaires." my_comment_history: "Mon historique de commentaires" @@ -395,6 +407,8 @@ fr: one_hour: "1 heure" hours: "{0} heures" days: "{0} jours" + hour: "{0} hours" + day: "{0} days" cancel: "Annuler" suspend_user: "Suspendre l'utilisateur" email_message_suspend: "Cher {0},\n\nConformément à la charte des commentaires de {1}, votre compte a été temporairement suspendu. Pendant cette période, vous ne pourrez pas commenter, signaler ou participer à d'autres commentaires. \n\nMerci de revenir dans la conversation {2}." From e9e70f7e7559c4ed5f25b285c2208162e6225d6a Mon Sep 17 00:00:00 2001 From: cecile Date: Mon, 9 Apr 2018 21:20:23 -0700 Subject: [PATCH 50/84] added missing translations --- locales/fr.yml | 270 ++++++++++++++++++++++++------------------------- 1 file changed, 135 insertions(+), 135 deletions(-) diff --git a/locales/fr.yml b/locales/fr.yml index 6ffa9d35b..dc6212213 100644 --- a/locales/fr.yml +++ b/locales/fr.yml @@ -1,42 +1,42 @@ fr: - your_account_has_been_suspended: Your account has been temporarily suspended. - your_account_has_been_banned: Your account has been banned. - your_username_has_been_rejected: Your account has been suspended because your username has been deemed inappropriate. To restore your account please enter a new username. - embed_comments_tab: Comments + your_account_has_been_suspended: Votre compte a été temporairement suspendu. + your_account_has_been_banned: Votre compte a été banni. + your_username_has_been_rejected: Votre compte a été suspendu en raison de votre nom d’utilisateur jugé inapproprié. Veuillez saisir un nouveau nom d’utilisateur pour restaurer votre compte. + embed_comments_tab: Commentaires bandialog: are_you_sure: "Êtes-vous sûr de vouloir bannir {0}?" ban_user: "Bannir l'utilisateur ?" banned_user: "Utilisateur banni" cancel: Annuler - note: "Remarque: bannir cet utilisateur rejettera également ce commentaire." - note_reject_comment: "Banning this user will also place this comment in the Rejected queue." - note_ban_user: "Banning this user will not let them comment, react to, or report comments." + note: "Remarque : bannir cet utilisateur rejettera également ce commentaire." + note_reject_comment: "Bannir cet utilisateur placera ce commentaire dans la liste des commentaires rejetées." + note_ban_user: "Bannir cet utilisateur empêchera cet utilisateur d’écrire, de réagir à ou de signaler des commentaires." yes_ban_user: "Oui, bannir cet utilisateur" - write_a_message: "Write a message" - send: "Send" - notify_ban_headline: "Notify the user of ban" - notify_ban_description: "This will notify the user by email that they have been banned from the community" - email_message_ban: "Dear {0},\n\nSomeone with access to your account has violated our community guidelines. As a result, your account has been banned. You will no longer be able to comment, like or report comments. if you think this has been done in error, please contact our community team." + write_a_message: "Écrire un message" + send: "Envoyer" + notify_ban_headline: "Aviser l’utilisateur du bannissement" + notify_ban_description: "Ceci avisera l’utilisateur par courrier électronique de son bannissement de la communauté" + email_message_ban: "Cher {0},\n\nUne personne ayant accès à votre compte a transgressée nos directives communautaires. En conséquences, votre compte a été banni. Vous ne pourrez plus écrire, aimer ou signaler des commentaires. Si vous pensez qu’il s’agit d’une erreur, veuillez contacter notre équipe communautaire." bio_offensive: "Cette biographie est offensante" - cancel: Annuler + cancel: "Annuler" confirm_email: - click_to_confirm: "Click below to confirm your email address" - confirm: "Confirm" + click_to_confirm: "Cliquez ci-dessous pour confirmer votre adresse électronique" + confirm: "Confirmer" password_reset: - mail_sent: 'If you have a registered account, a password reset link was sent to that email' - set_new_password: "Change Your Password" - new_password: "New Password" - new_password_help: "Password must be at least 8 characters" - confirm_new_password: "Confirm New Password" - change_password: "Change Password" + mail_sent: 'Si vous avez un compte enregistré, un lien de réinitialisation de mot de passe a été envoyé à cette adresse électronique' + set_new_password: "Changer votre mot de passe" + new_password: "Nouveau mot de passe" + new_password_help: "Le mot de passe doit comporter au moins 8 caractères" + confirm_new_password: "Confirmer le nouveau mot de passe" + change_password: "Changer le mot de passe" characters_remaining: "caractères restants" comment: - anon: Anonyme + anon: "Anonyme" ban_user: "Bannir utilisateur" - undo_reject: "Undo" + undo_reject: "Annuler" comment: "Publier un commentaire" - flagged: signalé - edited: Edited + flagged: "signalé" + edited: Modifié view_context: "Afficher le contexte" comment_box: post: "Publier" @@ -55,18 +55,18 @@ fr: comment_post_notif: "Votre commentaire a été publié." comment_post_notif_premod: "Merci d'avoir envoyé un commentaire. Notre équipe de modération passera en revue votre commentaire sous peu." common: - copy: 'Copy' - error: 'An error has occurred.' - reply: 'reply' - replies: 'replies' - reaction: 'reaction' - reactions: 'reactions' - story: 'Story' + copy: 'Copier' + error: 'Une erreur est survenue.' + reply: 'répondre' + replies: 'réponses' + reaction: 'réaction' + reactions: 'réactions' + story: 'Histoire' flagged_usernames: - notify_approved: '{0} approved username {1}' - notify_rejected: '{0} rejected username {1}' - notify_flagged: '{0} reported username {1}' - notify_changed: 'user {0} changed their username to {1}' + notify_approved: "{0} a approuvé le nom d’utilisateur {1}" + notify_rejected: "{0} a rejeté le nom d’utilisateur {1}" + notify_flagged: "{0} a signalé le nom d’utilisateur {1}" + notify_changed: "l’utilisateur {0} a modifié son nom d’utilisateur à {1}" community: account_creation_date: "Date de création du compte" active: Actif @@ -76,18 +76,18 @@ fr: ban_user: "Bannir l'utilisateur ?" banned: Banni banned_user: "Utilisateur banni" - cancel: Signalé - dont_like_username: "Dislike username" + cancel: Annuler + dont_like_username: "Ne pas aimer le nom d’utilisateur" flaggedaccounts: "Noms d'utilisateurs signalés" flags: Signalements - impersonating: Impersonation" + impersonating: "Impersonation" loading: "Chargement des résultats" moderator: Modérateur newsroom_role: "Rôle de la salle de presse" no_flagged_accounts: "La liste des comptes signalés est vide." no_results: "Aucun utilisateur n'a été trouvé avec ce nom d'utilisateur ou cette adresse de messagerie. Ils se cachent !" offensive: "Offensive" - other: "Other" + other: "Autre" people: Gens role: "Sélectionnez le rôle ..." select_status: "Sélectionnez l'état ..." @@ -154,7 +154,7 @@ fr: sign_out: "Se Déconnecter" stories: Histoires stream_settings: "Paramètres du fil" - access_message: "You must be an administrator to access config settings. Please find the nearest Admin and ask them to level you up!" + access_message: "Vous devez être un administrateur pour accéder au paramètres de configuration. Veuillez trouver l'administrateur le plus proche et demandez-lui d'augmenter votre niveau d’accès !" suspect_word_title: "Liste des mots suspects" suspect_word_text: "Les commentaires contenant ces mots ou expressions (non sensibles à la casse) seront mis en évidence dans le flux de commentaires. Tapez un mot et appuyez sur Entrée ou Tabulation pour ajouter. En option, entrez une liste séparée par des virgules." tech_settings: "Paramètres techniques" @@ -162,25 +162,25 @@ fr: weeks: Semaines wordlist: "Mots interdits" save_changes_dialog: - unsaved_changes: "Unsaved changes" - copy: "You have made one or more changes without saving. Would you like to save or discard your changes?" - save_settings: "Save Settings" - discard: "Discard" - cancel: "Cancel" + unsaved_changes: "Modifications non enregistrées" + copy: "Vous avez fait une ou plusieures modifications sans enregistrer. Souhaitez vous sauvegarder ou abandonner vos modifications ?" + save_settings: "Enregistrer la configuration" + discard: "Abandonner" + cancel: "Annuler" continue: Continuer createdisplay: - check_the_form: "Invalid Form. Please check the fields" - continue: "Continue with the same Facebook username" - error_create: "Error when changing username" - fake_comment_body: "This is an example comment. Readers can share their thoughts and opinions with newsrooms in the comments section." - fake_comment_date: "1 minute ago" - if_you_dont_change_your_name: "If you don't change your username at this step your Facebook display name will appear alongside of all your comments." - required_field: "Required field" - save: Save - special_characters: "Usernames can contain letters numbers and _ only" - username: Username - write_your_username: "Edit your username" - your_username: "Your username appears on every comment you post." + check_the_form: "Formulaire invalide. Veuillez vérifier les champs" + continue: "Continuer avec le même nom d’utilisateur Facebook" + error_create: "Une erreur lors du changement de nom d’utilisateur" + fake_comment_body: "Ceci est un exemple de commentaire. Les lecteurs peuvent livrer leurs réflexions et avis avec les salles de presse dans la section des commentaires." + fake_comment_date: "il y a 1 minute" + if_you_dont_change_your_name: "Si vous ne modifier pas votre nom d’utilisateur à cette étape, votre nom d’affichage Facefook apparaîtra avec tous vos commentaires." + required_field: "Champ obligatoire" + save: Sauvegarder + special_characters: "Les noms d'utilisateur ne peuvent contenir que des lettres, des chiffres et \"_\"" + username: Nom d’utilisateur + write_your_username: "Modifier votre nom d’utilisateur" + your_username: "Votre nom d’utilisateur apparait sur chaque commentaire publié." done: Terminé edit_comment: body_input_label: "Modifier ce commentaire" @@ -194,48 +194,48 @@ fr: minutes_plural: "minutes" email: suspended: - subject: "Your account has been suspended" + subject: "Votre compte a été suspendu" banned: - subject: "Your account has been banned" - body: "In accordance with The Coral Project’s community guidelines, your account has been banned. You are now longer allowed to comment, flag or engage with our community." + subject: "Votre compte a été banni" + body: "Conformément aux directives communautaires du projet Coral, votre compte a été banni. Vous ne pouvez désormais plus commenter, signaler ou collaborer avec notre communauté." confirm: - has_been_requested: "A email confirmation has been requested for the following account:" - to_confirm: "To confirm the account, please visit the following link:" - confirm_email: "Confirm Email" - if_you_did_not: "If you did not request this, you can safely ignore this email." - subject: "Email Confirmation" + has_been_requested: "Une confirmation de l’adresse électronique a été demandée pour le compte suivant :" + to_confirm: "Pour confirmer le compte, veuillez suivre le lien suivant :" + confirm_email: "Confirmer l’adresse électronique" + if_you_did_not: "Si vous n'avez pas fait cette requête, vous pouvez ignorer ce courriel en toute sécurité." + subject: "Confirmation adresse électronique" password_reset: - we_received_a_request: "We received a request to reset your password. If you did not request this change, you can ignore this email." - if_you_did: "If you did," - please_click: "please click here to reset password" + we_received_a_request: "Nous avons reçu une demande de réinitialisation de votre mot de passe. Si vous n'avez pas demandé cette modification, vous pouvez ignorer ce courriel." + if_you_did: "Si vous avez fait cette requête," + please_click: "veuillez cliquez ici pour réinitialiser le mot de passe" embedlink: copy: "Copier dans le presse-papier" error: - COMMENT_PARENT_NOT_VISIBLE: "The comment that you're replying to has been removed or doesn't exist." - EMAIL_VERIFICATION_TOKEN_INVALID: "Email verification token is invalid." - EMAIL_ALREADY_VERIFIED: "Email address already verified." - PASSWORD_RESET_TOKEN_INVALID: "Your password reset link is invalid." + COMMENT_PARENT_NOT_VISIBLE: "Le commentaire auquel vous répondez a été supprimé ou n’existe plus." + EMAIL_VERIFICATION_TOKEN_INVALID: "Le code de vérification de l'adresse électronique Email n'est pas valide." + EMAIL_ALREADY_VERIFIED: "Adresse électronique déjà vérifiée." + PASSWORD_RESET_TOKEN_INVALID: "Votre lien de réinitialisation de mot de passe n'est pas valide." COMMENT_TOO_SHORT: "Votre commentaire doit contenir quelque chose" NOT_AUTHORIZED: "Vous n'êtes pas autorisé à effectuer cette action." NO_SPECIAL_CHARACTERS: "Les noms d'utilisateur ne peuvent contenir que des lettres, des chiffres et \"_\" seulement" PASSWORD_LENGTH: "Le mot de passe est trop court" PROFANITY_ERROR: "Les noms d'utilisateur ne doivent pas contenir de mots offensants. Veuillez contacter l'administrateur si vous pensez qu'il y a une erreur." - RATE_LIMIT_EXCEEDED: "Rate limit exceeded" + RATE_LIMIT_EXCEEDED: "Nombre d’utilisations dépassé" USERNAME_IN_USE: "Ce nom d'utilisateur est déjà pris" USERNAME_REQUIRED: "Doit entrer un nom d'utilisateur" - EMAIL_NOT_VERIFIED: "E-mail address not verified" + EMAIL_NOT_VERIFIED: "Adresse électronique non vérifiée" EDIT_WINDOW_ENDED: "Vous ne pouvez plus modifier ce commentaire. La fenêtre de temps pour le faire a expiré." EDIT_USERNAME_NOT_AUTHORIZED: "Vous n'avez pas la permission de mettre à jour votre nom d'utilisateur." - SAME_USERNAME_PROVIDED: "You must submit a different username." - EMAIL_IN_USE: "Adresse e-mail déjà utilisée" - EMAIL_REQUIRED: "Une adresse email est requise" + SAME_USERNAME_PROVIDED: "Vous devez soumettre un nom d’utilisateur différent." + EMAIL_IN_USE: "Adresse électronique déjà utilisée" + EMAIL_REQUIRED: "Une adresse électronique est requise" LOGIN_MAXIMUM_EXCEEDED: "Vous avez effectué trop de tentatives infructueuses pour entrer votre mot de passe. S'il vous plaît, attendez." PASSWORD_REQUIRED: "Doit entrer un mot de passe" COMMENTING_CLOSED: "Les commentaires sont déjà fermés" NOT_FOUND: "Ressource introuvable" - ALREADY_EXISTS: "Resource already exists" + ALREADY_EXISTS: "Ressource déjà existante" INVALID_ASSET_URL: "L'URL est invalide" - CANNOT_IGNORE_STAFF: "Cannot ignore Staff members." + CANNOT_IGNORE_STAFF: "Ne peut pas ignorer les membres de l'équipe." email: "Pas une adresse e-mail valide" confirm_password: "Les mots de passe ne correspondent pas. Vérifiez à nouveau" network_error: "Échec de connexion au serveur. Vérifiez votre connexion Internet et réessayez." @@ -245,20 +245,20 @@ fr: password: "Le mot de passe doit être d'au moins 8 caractères" username: "Les noms d'utilisateur ne peuvent contenir que des chiffres, des lettres et \"_\"" required_field: "Ce champ est obligatoire" - unexpected: "Unexpected error occurred. Sorry!" - temporarily_suspended: "Your account is currently suspended. It will be reactivated {0}. Please contact us if you have any questions." + unexpected: "Désolé, une erreur inattendue s'est produite." + temporarily_suspended: "Votre compte est actuellement suspendu. Il sera réactivé {0}. Veuillez nous contacter si vous avez des questions." flag_comment: "Signaler un commentaire" flag_reason: "Motif du signalement (facultatif)" flag_username: "Signaler un nom d'utilisateur" framework: - banned_account_header: "Your account is currently banned." - banned_account_body: "This means that you cannot Like, Report, or write comments." + banned_account_header: "Votre compte est actuellement banni." + banned_account_body: "Cela signifie que vous ne pouvez pas aimer, signaler ou écrire des commentaires." comment: commentaire - comment_is_rejected: "You have rejected this comment." - comment_is_hidden: "This comment is not available." + comment_is_rejected: "Vous avez rejeté ce commentaire." + comment_is_hidden: "Ce commentaire n’est pas disponible." comment_is_ignored: "Ce commentaire est caché car vous avez ignoré cet utilisateur." comments: commentaires - configure_stream: "Configure le fil" + configure_stream: "Configurer le fil" content_not_available: "Ce contenu n'est pas disponible" edit_name: button: Soumettre @@ -266,7 +266,7 @@ fr: label: "Nouveau nom d'utilisateur" msg: "Votre compte est actuellement suspendu car votre nom d'utilisateur a été jugé inapproprié. Pour restaurer votre compte, entrez un nouveau nom d'utilisateur. Contactez-nous si vous avez des questions." changed_name: - msg: "Your username change is under review by our moderation team." + msg: "Votre modification de nom d’utilisateur est sous révision par notre équipe de modération." my_comments: "Mes commentaires" my_profile: "Mon profil" new_count: "Voir {0} plus {1}" @@ -291,27 +291,27 @@ fr: username_nolike: "Dislike" username_impersonating: "Impersonation" username_spam: "Spam" - username_other: "Other" + username_other: "Autre" comment: comment_offensive: "Offensive" comment_spam: "Spam" - comment_noagree: "Disagree" - comment_other: "Other" - suspect_word: "Suspect Word" - banned_word: "Banned Word" - body_count: "Body exceeds max length" + comment_noagree: "Pas d’accord" + comment_other: "Autre" + suspect_word: "Mot suspect" + banned_word: "Mot banni" + body_count: "Le texte dépasse la longueure maximale" trust: "Trust" - links: "Link" + links: "Lien" modqueue: account: "Signalements du compte" actions: Actions all: tous all_streams: "Tous les fils" - notify_edited: '{0} edited comment "{1}"' - notify_accepted: '{0} accepted comment "{1}"' - notify_rejected: '{0} rejected comment "{1}"' - notify_flagged: '{0} flagged comment "{1}"' - notify_reset: '{0} reset status of comment "{1}"' + notify_edited: '{0} a modifié le commentaire "{1}"' + notify_accepted: '{0} a accepté le commentaire "{1}"' + notify_rejected: '{0} a rejeté le commentaire "{1}"' + notify_flagged: '{0} a signalé le commentaire "{1}"' + notify_reset: '{0} a réinitialisé le status du commentaire "{1}"' approve: "Approuver" approved: "Approuvé" ban_user: "Bannir" @@ -326,26 +326,26 @@ fr: mod_faster: "Modérer plus rapidement avec les raccourcis clavier" moderate: "Modérer →" more_detail: "Plus de détails" - new: New + new: Nouveau newest_first: "Le plus récent d'abord" navigation: Navigation next_comment: "Aller au prochain commentaire" - toggle_search: "Open search" - next_queue: "Switch queues" + toggle_search: "Ouvrir la recherche" + next_queue: "Changer de file" oldest_first: "Le plus ancien d'abord" premod: Pre-modérer prev_comment: "Aller au commentaire précédent" reject: "Rejeter" rejected: "Rejeté" - reply: "Reply" + reply: "Répondre" select_stream: "Sélectionnez le fil" shift_key: ⇧ shortcuts: Raccourcis - sort: "Sort" + sort: "Trier" show_shortcuts: "Afficher les raccourcis" singleview: "Mode zen" thismenu: "Ouvrir ce menu" - jump_to_queue: "Jump to specific queue" + jump_to_queue: "Aller dans une file d'attente spécifique" thousand: k try_these: "Essayez ces" view_more_shortcuts: "Afficher plus de raccourcis" @@ -354,7 +354,7 @@ fr: no_agree_comment: "Je ne suis pas d'accord avec ce commentaire" no_like_bio: "Je n'aime pas cette biographie" no_like_username: "Je n'aime pas ce nom d'utilisateur" - already_flagged_username: "You have already flagged this username." + already_flagged_username: "Vous avez déjà signalé ce nom d’utilisateur." other: Autre permalink: Partager personal_info: "Ce commentaire révèle des informations personnelles identifiables" @@ -363,12 +363,12 @@ fr: profile_settings: "Paramètres" reply: Répondre report: Signaler - report_notif: "Merci de signaler ce commentaire. Notre équipe de modération a é té informée." + report_notif: "Merci de signaler ce commentaire. Notre équipe de modération a été informée." report_notif_remove: "Votre signalement a été supprimé." reported: Signalé comment_history_blank: - title: You have not written any comments - info: A history of your comments will appear here + title: Vous n’avez écrit aucun commentaire + info: Un historique de vos commentaires apparaîtra ici settings: from_settings_page: "Dans la page Profil, vous pouvez voir l'historique de vos commentaires." my_comment_history: "Mon historique de commentaires" @@ -381,8 +381,8 @@ fr: all_comments: "Tous les commentaires" temporarily_suspended: "Conformément à la charte d'utilisation des commentaires de {0}, votre compte a été temporairement suspendu. Merci de revenir dans la conversation {1}." comment_not_found: "Ce commentaire a été supprimé ou n'existe pas." - no_comments: "There are no comments yet, why don’t you write one?" - no_comments_and_closed: "There were no comments on this article." + no_comments: "Il n’y a aucun commentaire pour le moment. Soyez le premier à commenter !" + no_comments_and_closed: "Il n'y avait aucun commentaire sur cet article." step_1_header: "Signaler un problème" step_2_header: "Aidez-nous à comprendre" step_3_header: "Merci pour votre participation" @@ -407,8 +407,8 @@ fr: one_hour: "1 heure" hours: "{0} heures" days: "{0} jours" - hour: "{0} hours" - day: "{0} days" + hour: "{0} heures" + day: "{0} jours" cancel: "Annuler" suspend_user: "Suspendre l'utilisateur" email_message_suspend: "Cher {0},\n\nConformément à la charte des commentaires de {1}, votre compte a été temporairement suspendu. Pendant cette période, vous ne pourrez pas commenter, signaler ou participer à d'autres commentaires. \n\nMerci de revenir dans la conversation {2}." @@ -435,28 +435,28 @@ fr: user_bio: "Bio de l'utilisateur" username_flags: "Signaler pour ce nom d'utilisateur" user_detail: - remove_suspension: "Remove Suspension" - suspend: "Suspend User" - remove_ban: "Remove Ban" - ban: "Ban User" - member_since: "Member Since" - email: "Email" - total_comments: "Total Comments" - reject_rate: "Reject Rate" - reports: "Reports" - all: "All" - rejected: "Rejected" - account_history: "Account History" + remove_suspension: "Lever la suspension" + suspend: "Suspendre l’utilisateur" + remove_ban: "Lever le bannissement" + ban: "Banir l’utilisateur" + member_since: "Membre depuis" + email: "adresse électronique" + total_comments: "Nombre total de commentaires" + reject_rate: "Fréquence de rejet" + reports: "Signalements" + all: "Tous" + rejected: "Rejeté" + account_history: "Historique de compte" account_history: - user_banned: "User banned" - ban_removed: "Ban removed" - username_status: "Username {0}" - suspended: "Suspended, {0}" - suspension_removed: "Suspension removed" - system: "System" + user_banned: "Utilisateur banni" + ban_removed: "Bannissement levé" + username_status: "Nom d’utilisateur {0}" + suspended: "Suspendu, {0}" + suspension_removed: "Suspension levée" + system: "Système" date: "Date" action: "Action" - taken_by: "Taken By" + taken_by: "Prise par" user_impersonating: "Cet utilisateur se fait passer pour quelqu'un d'autre" user_no_comment: "Vous n'avez jamais laissé de commentaire. Rejoignez la conversation !" username_offensive: "Ce nom d'utilisateur est offensant" @@ -484,5 +484,5 @@ fr: launch: "Lancer Talk" close: "Fermez cet installateur" admin_sidebar: - view_options: "View Options" - sort_comments: "Sort Comments" + view_options: "Afficher les options" + sort_comments: "Trier les commentaires" From 80987a1e468a45870a45da7dadcef10599b9f7da Mon Sep 17 00:00:00 2001 From: Ville Kauppi Date: Tue, 10 Apr 2018 13:21:52 +0300 Subject: [PATCH 51/84] New version of the translation --- locales/fi_FI.yml | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/locales/fi_FI.yml b/locales/fi_FI.yml index 88f58479f..71416b4ff 100644 --- a/locales/fi_FI.yml +++ b/locales/fi_FI.yml @@ -146,7 +146,7 @@ fi_FI: moderation_settings: "Moderointiasetukset" open: "Avaa" open_stream: "Avaa kommentointi" - open_stream_configuration: "Tämän artikkelin kommentointi on tällä hetkellä auki. Jos se suljetaan, ei kommentointi ole enää mahdollista, mutta vanhat kommentit jäävät näkyviin. + open_stream_configuration: "Tämän artikkelin kommentointi on tällä hetkellä auki. Jos se suljetaan, ei kommentointi ole enää mahdollista, mutta vanhat kommentit jäävät näkyviin." require_email_verification: "Vaadi sähköpostin vahvistus" require_email_verification_text: "Uusien käyttäjien täytyy vahvistaa sähköpostiosoitteensa ennen kommentoinnin aloittamista" save_changes: "Tallenna muutokset" @@ -180,7 +180,7 @@ fi_FI: save_button: "Tallenna muutokset" edit_window_expired: "Et voi enää muokata tätä kommenttia, koska muokkauksen aikaikkuna on umpeutunut. Jätä sen sijaan uusi kommentti?" edit_window_expired_close: "Sulje" - edit_window_timer_prefix: "Muokkaa aikaikkunaa: " + edit_window_timer_prefix: "Muokkauksen aikaikkunaa jäljellä: " second: "sekunti" seconds_plural: "sekuntia" minute: "minuutti" @@ -216,7 +216,7 @@ fi_FI: USERNAME_IN_USE: "Käyttäjänimi jo käytössä" USERNAME_REQUIRED: "Syötä käyttäjänimi" EMAIL_NOT_VERIFIED: "Sähköpostiosoitetta ei ole vahvistettu" - EDIT_WINDOW_ENDED: ""Et voi enää muokata tätä kommenttia, koska muokkauksen aikaikkuna on umpeutunut." + EDIT_WINDOW_ENDED: "Et voi enää muokata tätä kommenttia, koska muokkauksen aikaikkuna on umpeutunut." EDIT_USERNAME_NOT_AUTHORIZED: "Sinulla ei ole oikeutta päivittää tai muokata käyttäjänimeä." SAME_USERNAME_PROVIDED: "Anna eri käyttäjänimi." EMAIL_IN_USE: "Sähköpostiosoite on jo käytössä" @@ -241,13 +241,13 @@ fi_FI: temporarily_suspended: "Tilisi on suljettu väliaikaisesti. Se aktivoituu uudelleen {0}. Ota yhteyttä, jos on sinulla on aiheesta kysyttävää." flag_comment: "Ilmianna kommentti" flag_reason: "Ilmiannon syy (ei pakollinen)" - flag_username: "Ilmianna käyttäjänimi" + flag_username: "Ilmianna käyttäjä" framework: banned_account_header: "Tilisi on kirjoituskiellossa" banned_account_body: "Et pysty kirjoittamaan tai ilmiantamaan kommentteja." comment: kommentti comment_is_ignored: "Tämä kommentti on piilossa, koska olet päättänyt jättää kommentin kirjoittajan huomiotta." - comment_is_rejected: "Olet hylännyt tämän kommentin." + comment_is_rejected: "Olet piilottanut tämän kommentin." comment_is_hidden: "Tämä kommentti ei ole saatavilla." comments: kommentit configure_stream: "Muokkaa asetuksia" @@ -311,7 +311,7 @@ fi_FI: close: Sulje empty_queue: "Moderointijono on tyhjä." flagged: liputettu - reported: raportoitu + reported: ilmiannettu less_detail: "Vähemmän yksityiskohtia" likes: tykkäyksiä million: milj. @@ -373,8 +373,8 @@ fi_FI: no_comments: "Ei vielä kommentteja." no_comments_and_closed: "Tässä artikkelissa ei vielä ollut kommentteja." step_1_header: "Ilmianna ongelma" - step_2_header: "Auta meitä ymmärtämään" - step_3_header: "Kiitos panostuksestasi" + step_2_header: "Kerro ilmiannon syy" + step_3_header: "Kiitos panoksestasi" streams: all: Kaikki article: Artikkeli @@ -462,4 +462,4 @@ fi_FI: close: "Sulje asennusnäkymä" admin_sidebar: view_options: "Näytä asetukset" - sort_comments: "Järjestä kommentit" +sort_comments: "Järjestä kommentit" \ No newline at end of file From 254033a29430b1b8e2651cbdb4110e1aacd00269 Mon Sep 17 00:00:00 2001 From: cecile Date: Tue, 10 Apr 2018 09:40:22 -0700 Subject: [PATCH 52/84] FrenchCorrections --- locales/fr.yml | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/locales/fr.yml b/locales/fr.yml index dc6212213..1b140f6ba 100644 --- a/locales/fr.yml +++ b/locales/fr.yml @@ -16,7 +16,7 @@ fr: send: "Envoyer" notify_ban_headline: "Aviser l’utilisateur du bannissement" notify_ban_description: "Ceci avisera l’utilisateur par courrier électronique de son bannissement de la communauté" - email_message_ban: "Cher {0},\n\nUne personne ayant accès à votre compte a transgressée nos directives communautaires. En conséquences, votre compte a été banni. Vous ne pourrez plus écrire, aimer ou signaler des commentaires. Si vous pensez qu’il s’agit d’une erreur, veuillez contacter notre équipe communautaire." + email_message_ban: "Cher {0},\n\nUne personne ayant accès à votre compte a transgressé nos directives communautaires. En conséquence, votre compte a été banni. Vous ne pourrez plus écrire, aimer ou signaler des commentaires. Si vous pensez qu’il s’agit d’une erreur, veuillez contacter notre équipe communautaire." bio_offensive: "Cette biographie est offensante" cancel: "Annuler" confirm_email: @@ -66,7 +66,7 @@ fr: notify_approved: "{0} a approuvé le nom d’utilisateur {1}" notify_rejected: "{0} a rejeté le nom d’utilisateur {1}" notify_flagged: "{0} a signalé le nom d’utilisateur {1}" - notify_changed: "l’utilisateur {0} a modifié son nom d’utilisateur à {1}" + notify_changed: "l’utilisateur {0} a modifié son nom d’utilisateur en {1}" community: account_creation_date: "Date de création du compte" active: Actif @@ -80,7 +80,7 @@ fr: dont_like_username: "Ne pas aimer le nom d’utilisateur" flaggedaccounts: "Noms d'utilisateurs signalés" flags: Signalements - impersonating: "Impersonation" + impersonating: "Usurpation d’identité" loading: "Chargement des résultats" moderator: Modérateur newsroom_role: "Rôle de la salle de presse" @@ -154,7 +154,7 @@ fr: sign_out: "Se Déconnecter" stories: Histoires stream_settings: "Paramètres du fil" - access_message: "Vous devez être un administrateur pour accéder au paramètres de configuration. Veuillez trouver l'administrateur le plus proche et demandez-lui d'augmenter votre niveau d’accès !" + access_message: "Vous devez être un administrateur pour accéder aux paramètres de configuration. Veuillez trouver l'administrateur le plus proche et demandez-lui d'augmenter votre niveau d’accès !" suspect_word_title: "Liste des mots suspects" suspect_word_text: "Les commentaires contenant ces mots ou expressions (non sensibles à la casse) seront mis en évidence dans le flux de commentaires. Tapez un mot et appuyez sur Entrée ou Tabulation pour ajouter. En option, entrez une liste séparée par des virgules." tech_settings: "Paramètres techniques" @@ -163,7 +163,7 @@ fr: wordlist: "Mots interdits" save_changes_dialog: unsaved_changes: "Modifications non enregistrées" - copy: "Vous avez fait une ou plusieures modifications sans enregistrer. Souhaitez vous sauvegarder ou abandonner vos modifications ?" + copy: "Vous avez fait une ou plusieurs modifications sans enregistrer. Souhaitez-vous sauvegarder ou abandonner vos modifications ?" save_settings: "Enregistrer la configuration" discard: "Abandonner" cancel: "Annuler" @@ -174,7 +174,7 @@ fr: error_create: "Une erreur lors du changement de nom d’utilisateur" fake_comment_body: "Ceci est un exemple de commentaire. Les lecteurs peuvent livrer leurs réflexions et avis avec les salles de presse dans la section des commentaires." fake_comment_date: "il y a 1 minute" - if_you_dont_change_your_name: "Si vous ne modifier pas votre nom d’utilisateur à cette étape, votre nom d’affichage Facefook apparaîtra avec tous vos commentaires." + if_you_dont_change_your_name: "Si vous ne modifiez pas votre nom d’utilisateur à cette étape, votre nom d’affichage Facefook apparaîtra avec tous vos commentaires." required_field: "Champ obligatoire" save: Sauvegarder special_characters: "Les noms d'utilisateur ne peuvent contenir que des lettres, des chiffres et \"_\"" @@ -202,17 +202,17 @@ fr: has_been_requested: "Une confirmation de l’adresse électronique a été demandée pour le compte suivant :" to_confirm: "Pour confirmer le compte, veuillez suivre le lien suivant :" confirm_email: "Confirmer l’adresse électronique" - if_you_did_not: "Si vous n'avez pas fait cette requête, vous pouvez ignorer ce courriel en toute sécurité." + if_you_did_not: "Si vous n’êtes pas à l’origine de cette requête, vous pouvez ignorer ce courriel en toute sécurité." subject: "Confirmation adresse électronique" password_reset: we_received_a_request: "Nous avons reçu une demande de réinitialisation de votre mot de passe. Si vous n'avez pas demandé cette modification, vous pouvez ignorer ce courriel." - if_you_did: "Si vous avez fait cette requête," + if_you_did: "Si vous êtes à l’origine de cette requête," please_click: "veuillez cliquez ici pour réinitialiser le mot de passe" embedlink: copy: "Copier dans le presse-papier" error: COMMENT_PARENT_NOT_VISIBLE: "Le commentaire auquel vous répondez a été supprimé ou n’existe plus." - EMAIL_VERIFICATION_TOKEN_INVALID: "Le code de vérification de l'adresse électronique Email n'est pas valide." + EMAIL_VERIFICATION_TOKEN_INVALID: "Le code de vérification de l'adresse électronique n'est pas valide." EMAIL_ALREADY_VERIFIED: "Adresse électronique déjà vérifiée." PASSWORD_RESET_TOKEN_INVALID: "Votre lien de réinitialisation de mot de passe n'est pas valide." COMMENT_TOO_SHORT: "Votre commentaire doit contenir quelque chose" @@ -289,7 +289,7 @@ fr: user: username_offensive: "Offensive" username_nolike: "Dislike" - username_impersonating: "Impersonation" + username_impersonating: "Usurpation d’identité" username_spam: "Spam" username_other: "Autre" comment: @@ -299,7 +299,7 @@ fr: comment_other: "Autre" suspect_word: "Mot suspect" banned_word: "Mot banni" - body_count: "Le texte dépasse la longueure maximale" + body_count: "Le texte dépasse la longueur maximale" trust: "Trust" links: "Lien" modqueue: @@ -311,7 +311,7 @@ fr: notify_accepted: '{0} a accepté le commentaire "{1}"' notify_rejected: '{0} a rejeté le commentaire "{1}"' notify_flagged: '{0} a signalé le commentaire "{1}"' - notify_reset: '{0} a réinitialisé le status du commentaire "{1}"' + notify_reset: '{0} a réinitialisé le statut du commentaire "{1}"' approve: "Approuver" approved: "Approuvé" ban_user: "Bannir" @@ -438,7 +438,7 @@ fr: remove_suspension: "Lever la suspension" suspend: "Suspendre l’utilisateur" remove_ban: "Lever le bannissement" - ban: "Banir l’utilisateur" + ban: "Bannir l’utilisateur" member_since: "Membre depuis" email: "adresse électronique" total_comments: "Nombre total de commentaires" From 46078117c87c60bb00404024065b1ed361db954d Mon Sep 17 00:00:00 2001 From: Wyatt Johnson Date: Tue, 10 Apr 2018 12:05:59 -0600 Subject: [PATCH 53/84] updated version --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index d331f62b4..dd84c7faf 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "talk", - "version": "4.3.0", + "version": "4.3.1", "description": "A better commenting experience from Mozilla, The New York Times, and the Washington Post. https://coralproject.net", "main": "app.js", "private": true, From edfe60b8e0d5d719ab62adf807a54032ed4a6ce0 Mon Sep 17 00:00:00 2001 From: Wyatt Johnson Date: Tue, 10 Apr 2018 15:52:10 -0600 Subject: [PATCH 54/84] removed unused coral-docs --- client/coral-docs/src/index.js | 8 -------- client/coral-docs/src/services/fetcher.js | 10 ---------- jest.config.js | 2 +- 3 files changed, 1 insertion(+), 19 deletions(-) delete mode 100644 client/coral-docs/src/index.js delete mode 100644 client/coral-docs/src/services/fetcher.js diff --git a/client/coral-docs/src/index.js b/client/coral-docs/src/index.js deleted file mode 100644 index d65c6c29e..000000000 --- a/client/coral-docs/src/index.js +++ /dev/null @@ -1,8 +0,0 @@ -import React from 'react'; -import { render } from 'react-dom'; -import { GraphQLDocs } from 'graphql-docs'; - -import fetcher from './services/fetcher'; - -// Render the application into the DOM -render(, document.querySelector('#root')); diff --git a/client/coral-docs/src/services/fetcher.js b/client/coral-docs/src/services/fetcher.js deleted file mode 100644 index 32b412f67..000000000 --- a/client/coral-docs/src/services/fetcher.js +++ /dev/null @@ -1,10 +0,0 @@ -export default function fetcher(query) { - return fetch(`${window.location.origin}/api/v1/graph/ql`, { - method: 'POST', - headers: { - Accept: 'application/json', - 'Content-Type': 'application/json', - }, - body: JSON.stringify({ query }), - }).then(res => res.json()); -} diff --git a/jest.config.js b/jest.config.js index 7150d1066..7c3755bea 100644 --- a/jest.config.js +++ b/jest.config.js @@ -1,7 +1,7 @@ const path = require('path'); const { pluginsPath } = require('./plugins'); -const buildTargets = ['coral-admin', 'coral-docs']; +const buildTargets = ['coral-admin']; const buildEmbeds = ['stream']; From f1b8d71cba854847290f2caddf257ac0cbf14c5b Mon Sep 17 00:00:00 2001 From: Wyatt Johnson Date: Tue, 10 Apr 2018 16:51:07 -0600 Subject: [PATCH 55/84] Refactored Errors --- bin/cli-setup | 12 +- errors.js | 369 +++++++++++------- graph/errorHandler.js | 4 +- graph/loaders/assets.js | 6 +- graph/mutators/action.js | 10 +- graph/mutators/asset.js | 10 +- graph/mutators/comment.js | 8 +- graph/mutators/settings.js | 4 +- graph/mutators/tag.js | 6 +- graph/mutators/token.js | 6 +- graph/mutators/user.js | 22 +- jobs/mailer.js | 4 +- middleware/authorization.js | 2 +- plugin-api/beta/server/getReactionConfig.js | 6 +- plugins/talk-plugin-akismet/errors.js | 14 +- plugins/talk-plugin-akismet/index.js | 2 +- .../server/mutators.js | 5 +- .../server/errors.js | 14 +- .../server/hooks.js | 2 +- routes/api/v1/users.js | 15 +- routes/index.js | 12 +- serve.js | 24 +- services/assets.js | 15 +- services/comments.js | 23 +- services/limit.js | 4 +- services/moderation/index.js | 6 +- services/moderation/phases/commentLength.js | 2 +- services/passport.js | 21 +- services/settings.js | 4 +- services/setup.js | 29 +- services/tags.js | 8 +- services/users.js | 69 ++-- services/wordlist.js | 8 +- test/server/graph/context.js | 4 +- test/server/services/wordlist.js | 5 +- 35 files changed, 436 insertions(+), 319 deletions(-) diff --git a/bin/cli-setup b/bin/cli-setup index 572e8c7fc..11da478c2 100755 --- a/bin/cli-setup +++ b/bin/cli-setup @@ -14,7 +14,7 @@ const SettingsService = require('../services/settings'); const SetupService = require('../services/setup'); const UsersService = require('../services/users'); const MigrationService = require('../services/migration'); -const errors = require('../errors'); +const { ErrSettingsInit, ErrSettingsNotInit } = require('../errors'); const Context = require('../graph/context'); // Register the shutdown criteria. @@ -41,13 +41,15 @@ const performSetup = async () => { // We should NOT have gotten a settings object, this means that the // application is already setup. Error out here. - throw errors.ErrSettingsInit; - } catch (e) { + throw new ErrSettingsInit(); + } catch (err) { // If the error is `not init`, then we're good, otherwise, it's something // else. - if (e !== errors.ErrSettingsNotInit) { - throw e; + if (err instanceof ErrSettingsNotInit) { + return; } + + throw err; } if (program.defaults) { diff --git a/errors.js b/errors.js index 53e8bf351..37f42f4ff 100644 --- a/errors.js +++ b/errors.js @@ -10,21 +10,21 @@ class ExtendableError { } /** - * APIError is the base error that all application issued errors originate, they - * are composed of data used by the front end and backend to handle errors + * TalkError is the base error that all application issued errors originate, + * they are composed of data used by the front end and backend to handle errors * consistently. */ -class APIError extends ExtendableError { +class TalkError extends ExtendableError { constructor( message, - { status = 500, translation_key = null }, + { status = 500, translation_key = null } = {}, metadata = {} ) { super(message); - this.status = status; - this.translation_key = translation_key; - this.metadata = metadata; + this.status = status || 500; + this.translation_key = translation_key || null; + this.metadata = metadata || {}; } toJSON() { @@ -38,85 +38,114 @@ class APIError extends ExtendableError { } // ErrPasswordTooShort is returned when the password length is too short. -const ErrPasswordTooShort = new APIError( - 'password must be at least 8 characters', - { - status: 400, - translation_key: 'PASSWORD_LENGTH', +class ErrPasswordTooShort extends TalkError { + constructor() { + super('password must be at least 8 characters', { + status: 400, + translation_key: 'PASSWORD_LENGTH', + }); } -); +} -const ErrMissingEmail = new APIError('email is required', { - translation_key: 'EMAIL_REQUIRED', - status: 400, -}); - -const ErrMissingPassword = new APIError('password is required', { - translation_key: 'PASSWORD_REQUIRED', - status: 400, -}); - -const ErrEmailTaken = new APIError('Email address already in use', { - translation_key: 'EMAIL_IN_USE', - status: 400, -}); - -const ErrUsernameTaken = new APIError('Username already in use', { - translation_key: 'USERNAME_IN_USE', - status: 400, -}); - -const ErrSameUsernameProvided = new APIError( - 'Username provided for change is the same as current', - { - translation_key: 'SAME_USERNAME_PROVIDED', - status: 400, +class ErrMissingEmail extends TalkError { + constructor() { + super('email is required', { + translation_key: 'EMAIL_REQUIRED', + status: 400, + }); } -); +} -const ErrSpecialChars = new APIError( - 'No special characters are allowed in a username', - { - translation_key: 'NO_SPECIAL_CHARACTERS', - status: 400, +class ErrMissingPassword extends TalkError { + constructor() { + super('password is required', { + translation_key: 'PASSWORD_REQUIRED', + status: 400, + }); } -); +} -const ErrMissingUsername = new APIError( - 'A username is required to create a user', - { - translation_key: 'USERNAME_REQUIRED', - status: 400, +class ErrEmailTaken extends TalkError { + constructor() { + super('Email address already in use', { + translation_key: 'EMAIL_IN_USE', + status: 400, + }); } -); +} + +class ErrUsernameTaken extends TalkError { + constructor() { + super('Username already in use', { + translation_key: 'USERNAME_IN_USE', + status: 400, + }); + } +} + +class ErrSameUsernameProvided extends TalkError { + constructor() { + super('Username provided for change is the same as current', { + translation_key: 'SAME_USERNAME_PROVIDED', + status: 400, + }); + } +} + +class ErrSpecialChars extends TalkError { + constructor() { + super('No special characters are allowed in a username', { + translation_key: 'NO_SPECIAL_CHARACTERS', + status: 400, + }); + } +} + +class ErrMissingUsername extends TalkError { + constructor() { + super('A username is required to create a user', { + translation_key: 'USERNAME_REQUIRED', + status: 400, + }); + } +} // ErrEmailVerificationToken is returned in the event that the password reset is requested // without a token. -const ErrEmailVerificationToken = new APIError('token is required', { - translation_key: 'EMAIL_VERIFICATION_TOKEN_INVALID', - status: 400, -}); +class ErrEmailVerificationToken extends TalkError { + constructor() { + super('token is required', { + translation_key: 'EMAIL_VERIFICATION_TOKEN_INVALID', + status: 400, + }); + } +} // ErrEmailAlreadyVerified is returned when the user tries to verify an email // address that has already been verified. -const ErrEmailAlreadyVerified = new APIError( - 'email address is already verified', - { - translation_key: 'EMAIL_ALREADY_VERIFIED', - status: 409, +class ErrEmailAlreadyVerified extends TalkError { + constructor() { + super('email address is already verified', { + translation_key: 'EMAIL_ALREADY_VERIFIED', + status: 409, + }); } -); +} // ErrPasswordResetToken is returned in the event that the password reset is requested // without a token. -const ErrPasswordResetToken = new APIError('token is required', { - translation_key: 'PASSWORD_RESET_TOKEN_INVALID', - status: 400, -}); +class ErrPasswordResetToken extends TalkError { + constructor() { + super('token is required', { + translation_key: 'PASSWORD_RESET_TOKEN_INVALID', + status: 400, + }); + } +} // ErrAssetCommentingClosed is returned when a comment or action is attempted on // a stream where commenting has been closed. -class ErrAssetCommentingClosed extends APIError { +class ErrAssetCommentingClosed extends TalkError { constructor(closedMessage = null) { super( 'asset commenting is closed', @@ -136,7 +165,7 @@ class ErrAssetCommentingClosed extends APIError { * ErrAuthentication is returned when there is an error authenticating and the * message is provided. */ -class ErrAuthentication extends APIError { +class ErrAuthentication extends TalkError { constructor(message = null) { super( 'authentication error occurred', @@ -154,7 +183,7 @@ class ErrAuthentication extends APIError { /** * ErrAlreadyExists is returned when an attempt to create a resource failed due to an existing one. */ -class ErrAlreadyExists extends APIError { +class ErrAlreadyExists extends TalkError { constructor(existing = null) { super( 'resource already exists', @@ -171,121 +200,179 @@ class ErrAlreadyExists extends APIError { // ErrContainsProfanity is returned in the event that the middleware detects // profanity/banned/suspect words in the payload. -const ErrContainsProfanity = new APIError( - 'This username contains elements which are not permitted in our community. If you think this is in error, please contact us or try again.', - { - translation_key: 'PROFANITY_ERROR', - status: 400, +class ErrContainsProfanity extends TalkError { + constructor(phrase) { + super( + 'This username contains elements which are not permitted in our community. If you think this is in error, please contact us or try again.', + { + translation_key: 'PROFANITY_ERROR', + status: 400, + }, + { phrase } + ); } -); +} -const ErrNotFound = new APIError('not found', { - translation_key: 'NOT_FOUND', - status: 404, -}); +class ErrNotFound extends TalkError { + constructor() { + super('not found', { + translation_key: 'NOT_FOUND', + status: 404, + }); + } +} -const ErrInvalidAssetURL = new APIError('asset_url is invalid', { - translation_key: 'INVALID_ASSET_URL', - status: 400, -}); +class ErrInvalidAssetURL extends TalkError { + constructor() { + super('asset_url is invalid', { + translation_key: 'INVALID_ASSET_URL', + status: 400, + }); + } +} // ErrNotAuthorized is an error that is returned in the event an operation is // deemed not authorized. -const ErrNotAuthorized = new APIError('not authorized', { - translation_key: 'NOT_AUTHORIZED', - status: 401, -}); +class ErrNotAuthorized extends TalkError { + constructor() { + super('not authorized', { + translation_key: 'NOT_AUTHORIZED', + status: 401, + }); + } +} // ErrSettingsNotInit is returned when the settings are required but not // initialized. -const ErrSettingsNotInit = new Error( - 'Talk is currently not setup. Please proceed to our web installer at $ROOT_URL/admin/install or run ./bin/cli-setup. Visit https://docs.coralproject.net/talk/ for more information on installation and configuration instructions' -); +class ErrSettingsNotInit extends TalkError { + constructor() { + super( + 'Talk is currently not setup. Please proceed to our web installer at $ROOT_URL/admin/install or run ./bin/cli-setup. Visit https://docs.coralproject.net/talk/ for more information on installation and configuration instructions' + ); + } +} // ErrSettingsInit is returned when the setup endpoint is hit and we are already // initialized. -const ErrSettingsInit = new APIError('settings are already initialized', { - status: 500, -}); +class ErrSettingsInit extends TalkError { + constructor() { + super('settings are already initialized', { + status: 500, + }); + } +} // ErrInstallLock is returned when the setup endpoint is hit and the install // lock is present. -const ErrInstallLock = new APIError('install lock active', { - status: 500, -}); +class ErrInstallLock extends TalkError { + constructor() { + super('install lock active', { + status: 500, + }); + } +} // ErrPermissionUpdateUsername is returned when the user does not have permission to update their username. -const ErrPermissionUpdateUsername = new APIError( - 'You do not have permission to update your username.', - { - translation_key: 'EDIT_USERNAME_NOT_AUTHORIZED', - status: 403, +class ErrPermissionUpdateUsername extends TalkError { + constructor() { + super('You do not have permission to update your username.', { + translation_key: 'EDIT_USERNAME_NOT_AUTHORIZED', + status: 403, + }); } -); +} // ErrLoginAttemptMaximumExceeded is returned when the login maximum is exceeded. -const ErrLoginAttemptMaximumExceeded = new APIError( - 'You have made too many incorrect password attempts.', - { - translation_key: 'LOGIN_MAXIMUM_EXCEEDED', - status: 429, +class ErrLoginAttemptMaximumExceeded extends TalkError { + constructor() { + super('You have made too many incorrect password attempts.', { + translation_key: 'LOGIN_MAXIMUM_EXCEEDED', + status: 429, + }); } -); +} // ErrEditWindowHasEnded is returned when the edit window has expired. -const ErrEditWindowHasEnded = new APIError('Edit window is over', { - translation_key: 'EDIT_WINDOW_ENDED', - status: 403, -}); +class ErrEditWindowHasEnded extends TalkError { + constructor() { + super('Edit window is over', { + translation_key: 'EDIT_WINDOW_ENDED', + status: 403, + }); + } +} // ErrCommentTooShort is returned when the comment is too short. -const ErrCommentTooShort = new APIError('Comment was too short', { - translation_key: 'COMMENT_TOO_SHORT', - status: 400, -}); +class ErrCommentTooShort extends TalkError { + constructor(length) { + super( + 'Comment was too short', + { + translation_key: 'COMMENT_TOO_SHORT', + status: 400, + }, + { length } + ); + } +} // ErrAssetURLAlreadyExists is returned when a rename operation is requested // but an asset already exists with the new url. -const ErrAssetURLAlreadyExists = new APIError( - 'Asset URL already exists, cannot rename', - { - translation_key: 'ASSET_URL_ALREADY_EXISTS', - status: 409, +class ErrAssetURLAlreadyExists extends TalkError { + constructor() { + super('Asset URL already exists, cannot rename', { + translation_key: 'ASSET_URL_ALREADY_EXISTS', + status: 409, + }); } -); +} // ErrNotVerified is returned when a user tries to login with valid credentials // but their email address is not yet verified. -const ErrNotVerified = new APIError( - 'User does not have a verified email address', - { - translation_key: 'EMAIL_NOT_VERIFIED', - status: 401, +class ErrNotVerified extends TalkError { + constructor() { + super('User does not have a verified email address', { + translation_key: 'EMAIL_NOT_VERIFIED', + status: 401, + }); } -); +} -const ErrMaxRateLimit = new APIError('Rate limit exceeded', { - translation_key: 'RATE_LIMIT_EXCEEDED', - status: 429, -}); +class ErrMaxRateLimit extends TalkError { + constructor(max, tries) { + super( + 'Rate limit exceeded', + { + translation_key: 'RATE_LIMIT_EXCEEDED', + status: 429, + }, + { tries, max } + ); + } +} // ErrCannotIgnoreStaff is returned when a user tries to ignore a staff member. -const ErrCannotIgnoreStaff = new APIError('Cannot ignore staff members.', { - translation_key: 'CANNOT_IGNORE_STAFF', - status: 400, -}); +class ErrCannotIgnoreStaff extends TalkError { + constructor() { + super('Cannot ignore staff members.', { + translation_key: 'CANNOT_IGNORE_STAFF', + status: 400, + }); + } +} // ErrParentDoesNotVisible is returned when the user tries to reply to a comment // that isn't visible. -const ErrParentDoesNotVisible = new APIError( - 'Cannot reply to a comment that is not visible', - { - translation_key: 'COMMENT_PARENT_NOT_VISIBLE', +class ErrParentDoesNotVisible extends TalkError { + constructor() { + super('Cannot reply to a comment that is not visible', { + translation_key: 'COMMENT_PARENT_NOT_VISIBLE', + }); } -); +} module.exports = { - APIError, + TalkError, ErrAlreadyExists, ErrAssetCommentingClosed, ErrAssetURLAlreadyExists, diff --git a/graph/errorHandler.js b/graph/errorHandler.js index 89cd5d8a2..9e8d98f74 100644 --- a/graph/errorHandler.js +++ b/graph/errorHandler.js @@ -1,6 +1,6 @@ const { forEachField } = require('./utils'); const { maskErrors } = require('graphql-errors'); -const errors = require('../errors'); +const { TalkError } = require('../errors'); const { Error: { ValidationError } } = require('mongoose'); // If an APIError happens in a mutation, then respond with `{errors: Array}` @@ -11,7 +11,7 @@ const decorateWithMutationErrorHandler = field => { try { return await fieldResolver(obj, args, ctx, info); } catch (err) { - if (err instanceof errors.APIError) { + if (err instanceof TalkError) { return { errors: [err], }; diff --git a/graph/loaders/assets.js b/graph/loaders/assets.js index 2f0595e81..40305760c 100644 --- a/graph/loaders/assets.js +++ b/graph/loaders/assets.js @@ -57,7 +57,7 @@ const findOrCreateAssetByURL = async (ctx, url) => { try { new URL(url); } catch (err) { - throw ErrInvalidAssetURL; + throw new ErrInvalidAssetURL(url); } // Try the easy lookup first. @@ -76,7 +76,7 @@ const findOrCreateAssetByURL = async (ctx, url) => { // If the domain wasn't whitelisted, then we shouldn't create this asset! if (!whitelisted) { - throw ErrInvalidAssetURL; + throw new ErrInvalidAssetURL(url); } // Construct the update operator that we'll use to create the asset. @@ -135,7 +135,7 @@ const findByUrl = async ( try { new URL(asset_url); } catch (err) { - throw errors.ErrInvalidAssetURL; + throw new errors.ErrInvalidAssetURL(asset_url); } return Assets.findByUrl(asset_url); diff --git a/graph/mutators/action.js b/graph/mutators/action.js index 20f9db4fe..d759449b0 100644 --- a/graph/mutators/action.js +++ b/graph/mutators/action.js @@ -1,4 +1,4 @@ -const errors = require('../../errors'); +const { ErrNotFound, ErrNotAuthorized } = require('../../errors'); const { CREATE_ACTION, DELETE_ACTION } = require('../../perms/constants'); const { IGNORE_FLAGS_AGAINST_STAFF } = require('../../config'); @@ -40,7 +40,7 @@ const createAction = async ( // Gets the item referenced by the action. const item = await getActionItem(ctx, { item_id, item_type }); if (!item || item === null) { - throw errors.ErrNotFound; + throw new ErrNotFound(); } // If we are ignoring flags against staff, ensure that the target isn't a @@ -59,7 +59,7 @@ const createAction = async ( // The item is a user, and this is a flag. Check to see if they are staff, // if they are, don't permit the flag. if (item.isStaff()) { - throw errors.ErrNotAuthorized; + throw new ErrNotAuthorized(); } } @@ -108,8 +108,8 @@ const deleteAction = (ctx, { id }) => { module.exports = ctx => { let mutators = { Action: { - create: () => Promise.reject(errors.ErrNotAuthorized), - delete: () => Promise.reject(errors.ErrNotAuthorized), + create: () => Promise.reject(new ErrNotAuthorized()), + delete: () => Promise.reject(new ErrNotAuthorized()), }, }; diff --git a/graph/mutators/asset.js b/graph/mutators/asset.js index 2997e7cd4..d5b72102d 100644 --- a/graph/mutators/asset.js +++ b/graph/mutators/asset.js @@ -1,4 +1,4 @@ -const errors = require('../../errors'); +const { ErrNotAuthorized } = require('../../errors'); const { UPDATE_ASSET_SETTINGS, UPDATE_ASSET_STATUS, @@ -71,10 +71,10 @@ const scrapeAsset = async (ctx, id) => { module.exports = ctx => { let mutators = { Asset: { - updateSettings: () => Promise.reject(errors.ErrNotAuthorized), - updateStatus: () => Promise.reject(errors.ErrNotAuthorized), - closeNow: () => Promise.reject(errors.ErrNotAuthorized), - scrape: () => Promise.reject(errors.ErrNotAuthorized), + updateSettings: () => Promise.reject(new ErrNotAuthorized()), + updateStatus: () => Promise.reject(new ErrNotAuthorized()), + closeNow: () => Promise.reject(new ErrNotAuthorized()), + scrape: () => Promise.reject(new ErrNotAuthorized()), }, }; diff --git a/graph/mutators/comment.js b/graph/mutators/comment.js index f63951812..b57aedeb3 100644 --- a/graph/mutators/comment.js +++ b/graph/mutators/comment.js @@ -1,4 +1,4 @@ -const errors = require('../../errors'); +const { ErrNotAuthorized } = require('../../errors'); const ActionModel = require('../../models/action'); const ActionsService = require('../../services/actions'); const TagsService = require('../../services/tags'); @@ -312,9 +312,9 @@ const editComment = async ( module.exports = ctx => { let mutators = { Comment: { - create: () => Promise.reject(errors.ErrNotAuthorized), - setStatus: () => Promise.reject(errors.ErrNotAuthorized), - edit: () => Promise.reject(errors.ErrNotAuthorized), + create: () => Promise.reject(new ErrNotAuthorized()), + setStatus: () => Promise.reject(new ErrNotAuthorized()), + edit: () => Promise.reject(new ErrNotAuthorized()), }, }; diff --git a/graph/mutators/settings.js b/graph/mutators/settings.js index 639b0619b..d9c04cea0 100644 --- a/graph/mutators/settings.js +++ b/graph/mutators/settings.js @@ -1,4 +1,4 @@ -const errors = require('../../errors'); +const { ErrNotAuthorized } = require('../../errors'); const { UPDATE_SETTINGS } = require('../../perms/constants'); @@ -9,7 +9,7 @@ const update = async (ctx, settings) => SettingsService.update(settings); module.exports = ctx => { let mutators = { Settings: { - update: () => Promise.reject(errors.ErrNotAuthorized), + update: () => Promise.reject(new ErrNotAuthorized()), }, }; diff --git a/graph/mutators/tag.js b/graph/mutators/tag.js index c6d8d4c40..d78b020d9 100644 --- a/graph/mutators/tag.js +++ b/graph/mutators/tag.js @@ -1,5 +1,5 @@ const TagsService = require('../../services/tags'); -const errors = require('../../errors'); +const { ErrNotAuthorized } = require('../../errors'); const { ADD_COMMENT_TAG, REMOVE_COMMENT_TAG, @@ -31,8 +31,8 @@ const modify = async ( module.exports = context => { let mutators = { Tag: { - add: () => Promise.reject(errors.ErrNotAuthorized), - remove: () => Promise.reject(errors.ErrNotAuthorized), + add: () => Promise.reject(new ErrNotAuthorized()), + remove: () => Promise.reject(new ErrNotAuthorized()), }, }; diff --git a/graph/mutators/token.js b/graph/mutators/token.js index 5883a2f09..c4d9a1121 100644 --- a/graph/mutators/token.js +++ b/graph/mutators/token.js @@ -1,4 +1,4 @@ -const errors = require('../../errors'); +const { ErrNotAuthorized } = require('../../errors'); const TokensService = require('../../services/tokens'); const { CREATE_TOKEN, REVOKE_TOKEN } = require('../../perms/constants'); @@ -21,8 +21,8 @@ const revokeToken = async ({ user }, { id }) => { module.exports = context => { let mutators = { Token: { - create: () => Promise.reject(errors.ErrNotAuthorized), - revoke: () => Promise.reject(errors.ErrNotAuthorized), + create: () => Promise.reject(new ErrNotAuthorized()), + revoke: () => Promise.reject(new ErrNotAuthorized()), }, }; diff --git a/graph/mutators/user.js b/graph/mutators/user.js index 9d31e6dbd..e0312533f 100644 --- a/graph/mutators/user.js +++ b/graph/mutators/user.js @@ -1,4 +1,4 @@ -const errors = require('../../errors'); +const { ErrNotFound, ErrNotAuthorized } = require('../../errors'); const UsersService = require('../../services/users'); const migrationHelpers = require('../../services/migration/helpers'); const { @@ -92,7 +92,7 @@ const delUser = async (ctx, id) => { // Find the user we're removing. const user = await User.findOne({ id }); if (!user) { - throw errors.ErrNotFound; + throw new ErrNotFound(); } // Get the query transformer we'll use to help batch process the user @@ -156,15 +156,15 @@ const delUser = async (ctx, id) => { module.exports = ctx => { let mutators = { User: { - changeUsername: () => Promise.reject(errors.ErrNotAuthorized), - ignoreUser: () => Promise.reject(errors.ErrNotAuthorized), - setRole: () => Promise.reject(errors.ErrNotAuthorized), - setUserBanStatus: () => Promise.reject(errors.ErrNotAuthorized), - setUserSuspensionStatus: () => Promise.reject(errors.ErrNotAuthorized), - setUserUsernameStatus: () => Promise.reject(errors.ErrNotAuthorized), - setUsername: () => Promise.reject(errors.ErrNotAuthorized), - stopIgnoringUser: () => Promise.reject(errors.ErrNotAuthorized), - del: () => Promise.reject(errors.ErrNotAuthorized), + changeUsername: () => Promise.reject(new ErrNotAuthorized()), + ignoreUser: () => Promise.reject(new ErrNotAuthorized()), + setRole: () => Promise.reject(new ErrNotAuthorized()), + setUserBanStatus: () => Promise.reject(new ErrNotAuthorized()), + setUserSuspensionStatus: () => Promise.reject(new ErrNotAuthorized()), + setUserUsernameStatus: () => Promise.reject(new ErrNotAuthorized()), + setUsername: () => Promise.reject(new ErrNotAuthorized()), + stopIgnoringUser: () => Promise.reject(new ErrNotAuthorized()), + del: () => Promise.reject(new ErrNotAuthorized()), }, }; diff --git a/jobs/mailer.js b/jobs/mailer.js index aa3afa585..a3b21f565 100644 --- a/jobs/mailer.js +++ b/jobs/mailer.js @@ -4,7 +4,6 @@ const { createLogger } = require('../services/logging'); const logger = createLogger('jobs:mailer'); const Context = require('../graph/context'); const { get } = require('lodash'); - const { SMTP_HOST, SMTP_USERNAME, @@ -12,6 +11,7 @@ const { SMTP_PASSWORD, SMTP_FROM_ADDRESS, } = require('../config'); +const { ErrMissingEmail } = require('../errors'); // parseSMTPPort will return the port for SMTP. const parseSMTPPort = () => { @@ -99,7 +99,7 @@ const getEmailAddress = async ({ email, user }) => { const email = get(data, 'user.email'); if (!email) { - throw errors.ErrMissingEmail; + throw new ErrMissingEmail(); } return email; diff --git a/middleware/authorization.js b/middleware/authorization.js index 97003d970..77376f08b 100644 --- a/middleware/authorization.js +++ b/middleware/authorization.js @@ -7,7 +7,7 @@ const authorization = (module.exports = { }); const debug = require('debug')('talk:middleware:authorization'); -const ErrNotAuthorized = require('../errors').ErrNotAuthorized; +const { ErrNotAuthorized } = require('../errors'); /** * has returns true if the user has at least one of the roles specified, diff --git a/plugin-api/beta/server/getReactionConfig.js b/plugin-api/beta/server/getReactionConfig.js index 3baf65806..ffb1a3c07 100644 --- a/plugin-api/beta/server/getReactionConfig.js +++ b/plugin-api/beta/server/getReactionConfig.js @@ -1,5 +1,5 @@ const { SEARCH_OTHER_USERS } = require('../../../perms/constants'); -const errors = require('../../../errors'); +const { ErrNotFound, ErrAlreadyExists } = require('../../../errors'); const pluralize = require('pluralize'); const sc = require('snake-case'); const CommentModel = require('../../../models/comment'); @@ -192,7 +192,7 @@ function getReactionConfig(reaction) { ) => { const comment = await Comments.get.load(item_id); if (!comment) { - throw errors.ErrNotFound; + throw new ErrNotFound(); } try { @@ -211,7 +211,7 @@ function getReactionConfig(reaction) { [reaction]: action, }; } catch (err) { - if (err instanceof errors.ErrAlreadyExists) { + if (err instanceof ErrAlreadyExists) { return err.metadata.existing; } diff --git a/plugins/talk-plugin-akismet/errors.js b/plugins/talk-plugin-akismet/errors.js index b93d178b9..458242ca5 100644 --- a/plugins/talk-plugin-akismet/errors.js +++ b/plugins/talk-plugin-akismet/errors.js @@ -1,12 +1,16 @@ -const { APIError } = require('errors'); +const { TalkError } = require('errors'); // ErrSpam is sent during a `CreateComment` mutation where // `input.checkSpam` is set to true and the comment contains // detected spam as determined by the akismet service. -const ErrSpam = new APIError('Comment is spam', { - status: 400, - translation_key: 'COMMENT_IS_SPAM', -}); +class ErrSpam extends TalkError { + constructor() { + super('Comment is spam', { + status: 400, + translation_key: 'COMMENT_IS_SPAM', + }); + } +} module.exports = { ErrSpam, diff --git a/plugins/talk-plugin-akismet/index.js b/plugins/talk-plugin-akismet/index.js index 4b0e7f997..c57f5da5d 100644 --- a/plugins/talk-plugin-akismet/index.js +++ b/plugins/talk-plugin-akismet/index.js @@ -100,7 +100,7 @@ module.exports = { if (spam) { if (input.checkSpam) { - throw ErrSpam; + throw new ErrSpam(); } // Attach reason information for the flag being added. diff --git a/plugins/talk-plugin-notifications/server/mutators.js b/plugins/talk-plugin-notifications/server/mutators.js index 5c0db9c8d..46954faa5 100644 --- a/plugins/talk-plugin-notifications/server/mutators.js +++ b/plugins/talk-plugin-notifications/server/mutators.js @@ -29,10 +29,11 @@ async function updateNotificationSettings(ctx, settings) { } module.exports = ctx => { + const { connectors: { errors: ErrNotAuthorized } } = ctx; + let mutators = { User: { - updateNotificationSettings: () => - Promise.reject(ctx.connectors.errors.ErrNotAuthorized), + updateNotificationSettings: () => Promise.reject(new ErrNotAuthorized()), }, }; diff --git a/plugins/talk-plugin-toxic-comments/server/errors.js b/plugins/talk-plugin-toxic-comments/server/errors.js index 60135a8f8..a60bd549b 100644 --- a/plugins/talk-plugin-toxic-comments/server/errors.js +++ b/plugins/talk-plugin-toxic-comments/server/errors.js @@ -1,12 +1,16 @@ -const { APIError } = require('errors'); +const { TalkError } = require('errors'); // ErrToxic is sent during a `CreateComment` mutation where // `input.checkToxicity` is set to true and the comment contains // toxic language as determined by the perspective service. -const ErrToxic = new APIError('Comment is toxic', { - status: 400, - translation_key: 'COMMENT_IS_TOXIC', -}); +class ErrToxic extends TalkError { + constructor() { + super('Comment is toxic', { + status: 400, + translation_key: 'COMMENT_IS_TOXIC', + }); + } +} module.exports = { ErrToxic, diff --git a/plugins/talk-plugin-toxic-comments/server/hooks.js b/plugins/talk-plugin-toxic-comments/server/hooks.js index 7b9c93dad..0be363ea1 100644 --- a/plugins/talk-plugin-toxic-comments/server/hooks.js +++ b/plugins/talk-plugin-toxic-comments/server/hooks.js @@ -27,7 +27,7 @@ module.exports = { if (isToxic(scores)) { if (input.checkToxicity) { - throw ErrToxic; + throw new ErrToxic(); } input.status = 'SYSTEM_WITHHELD'; diff --git a/routes/api/v1/users.js b/routes/api/v1/users.js index e0de3b5db..481e5650c 100644 --- a/routes/api/v1/users.js +++ b/routes/api/v1/users.js @@ -1,7 +1,7 @@ const express = require('express'); const router = express.Router(); const UsersService = require('../../../services/users'); -const errors = require('../../../errors'); +const { ErrMissingEmail, ErrNotFound } = require('../../../errors'); const authorization = require('../../../middleware/authorization'); const Limit = require('../../../services/limit'); @@ -40,17 +40,12 @@ router.post('/resend-verify', async (req, res, next) => { // Clean up and validate the email. email = email.toLowerCase().trim(); if (email.length < 5) { - return next(errors.ErrMissingEmail); + return next(new ErrMissingEmail()); } // Check if we're past the rate limit, if we are, stop now. Otherwise, record // this as an attempt to send a verification email. try { - const tries = await resendRateLimiter.get(email); - if (tries > 0) { - throw errors.ErrMaxRateLimit; - } - await resendRateLimiter.test(email); } catch (err) { return next(err); @@ -59,7 +54,7 @@ router.post('/resend-verify', async (req, res, next) => { try { const user = await UsersService.findLocalUser(email); if (!user) { - throw errors.ErrNotFound; + throw new ErrNotFound(); } await UsersService.sendEmailConfirmation(user, email, redirectUri); @@ -81,13 +76,13 @@ router.post( try { let user = await UsersService.findById(user_id); if (!user) { - return next(errors.ErrNotFound); + return next(new ErrNotFound()); } // Find the first local profile. const email = user.firstEmail; if (!email) { - return next(errors.ErrMissingEmail); + return next(new ErrMissingEmail()); } // Send the email to the first local profile that was found. diff --git a/routes/index.js b/routes/index.js index 489a5e8b5..bbdeb8928 100644 --- a/routes/index.js +++ b/routes/index.js @@ -2,7 +2,7 @@ const SetupService = require('../services/setup'); const authentication = require('../middleware/authentication'); const cookieParser = require('cookie-parser'); const enabled = require('debug').enabled; -const errors = require('../errors'); +const { TalkError, ErrNotFound } = require('../errors'); const express = require('express'); const i18n = require('../middleware/i18n'); const path = require('path'); @@ -149,19 +149,19 @@ router.use(require('./plugins')); // Catch 404 and forward to error handler. router.use((req, res, next) => { - next(errors.ErrNotFound); + next(new ErrNotFound()); }); // General API error handler. Respond with the message and error if we have it // while returning a status code that makes sense. router.use('/api', (err, req, res, next) => { - if (err !== errors.ErrNotFound) { + if (!(err instanceof ErrNotFound)) { if (process.env.NODE_ENV !== 'test' || enabled('talk:errors')) { console.error(err); } } - if (err instanceof errors.APIError) { + if (err instanceof TalkError) { res.status(err.status).json({ message: res.locals.t(`error.${err.translation_key}`), error: err, @@ -172,11 +172,11 @@ router.use('/api', (err, req, res, next) => { }); router.use('/', (err, req, res, next) => { - if (err !== errors.ErrNotFound) { + if (!(err instanceof ErrNotFound)) { console.error(err); } - if (err instanceof errors.APIError) { + if (err instanceof TalkError) { res.status(err.status); res.render('error', { message: res.locals.t(`error.${err.translation_key}`), diff --git a/serve.js b/serve.js index ea06a6342..8b4d4556c 100644 --- a/serve.js +++ b/serve.js @@ -1,5 +1,5 @@ const app = require('./app'); -const errors = require('./errors'); +const { ErrSettingsInit, ErrInstallLock } = require('./errors'); const { createServer } = require('http'); const jobs = require('./jobs'); const MigrationService = require('./services/migration'); @@ -95,20 +95,16 @@ async function serve({ await SetupService.isAvailable(); logger.info('Setup is currently available, migrations not being checked'); - } catch (e) { + } catch (err) { // Check the error. - switch (e) { - case errors.ErrInstallLock: - case errors.ErrSettingsInit: - logger.info( - 'Setup is not currently available, migrations now being checked' - ); - - // The error was expected, just continue. - break; - default: - // The error was not expected, throw the error! - throw e; + if (err instanceof ErrInstallLock || err instanceof ErrSettingsInit) { + // The error was expected, just continue. + logger.info( + 'Setup is not currently available, migrations now being checked' + ); + } else { + // The error was not expected, throw the error! + throw err; } // Now try and check the migration status. diff --git a/services/assets.js b/services/assets.js index f229bb353..fbed22287 100644 --- a/services/assets.js +++ b/services/assets.js @@ -2,9 +2,12 @@ const CommentModel = require('../models/comment'); const AssetModel = require('../models/asset'); const SettingsService = require('./settings'); const DomainList = require('./domain_list'); -const errors = require('../errors'); -const merge = require('lodash/merge'); -const isEmpty = require('lodash/isEmpty'); +const { + ErrAssetURLAlreadyExists, + ErrNotFound, + ErrInvalidAssetURL, +} = require('../errors'); +const { merge, isEmpty } = require('lodash'); const { dotize } = require('./utils'); module.exports = class AssetsService { @@ -73,7 +76,7 @@ module.exports = class AssetsService { } if (!whitelisted) { - return Promise.reject(errors.ErrInvalidAssetURL); + throw new ErrInvalidAssetURL(url); } else { return AssetModel.findOneAndUpdate({ url }, update, { // Ensure that if it's new, we return the new object created. @@ -211,7 +214,7 @@ module.exports = class AssetsService { // Try to see if an asset already exists with the given url. let asset = await AssetsService.findByUrl(url); if (asset !== null) { - throw errors.ErrAssetURLAlreadyExists; + throw new ErrAssetURLAlreadyExists(); } // Seems that there was no other asset with the same url, try and perform @@ -227,7 +230,7 @@ module.exports = class AssetsService { dstAssetID, ]); if (!srcAsset || !dstAsset) { - throw errors.ErrNotFound; + throw new ErrNotFound(); } // Resolve the merge operation, this invloves moving all resources attached diff --git a/services/comments.js b/services/comments.js index a1f59c1e1..b9d73d91c 100644 --- a/services/comments.js +++ b/services/comments.js @@ -2,10 +2,13 @@ const CommentModel = require('../models/comment'); const { dotize } = require('./utils'); const debug = require('debug')('talk:services:comments'); const SettingsService = require('./settings'); - -const cloneDeep = require('lodash/cloneDeep'); -const errors = require('../errors'); -const merge = require('lodash/merge'); +const { merge, cloneDeep } = require('lodash'); +const { + ErrParentDoesNotVisible, + ErrNotFound, + ErrNotAuthorized, + ErrEditWindowHasEnded, +} = require('../errors'); const incrReplyCount = async (comment, value) => { try { @@ -40,7 +43,7 @@ module.exports = { if (parent_id !== null) { const parent = await CommentModel.findOne({ id: parent_id }); if (parent === null || !parent.visible) { - throw errors.ErrParentDoesNotVisible; + throw new ErrParentDoesNotVisible(); } } @@ -126,7 +129,7 @@ module.exports = { const comment = await CommentModel.findOne({ id }); if (comment == null) { debug('rejecting comment edit because comment was not found'); - throw errors.ErrNotFound; + throw new ErrNotFound(); } // Check to see if the user was't allowed to edit it. @@ -134,7 +137,7 @@ module.exports = { debug( 'rejecting comment edit because author id does not match editing user' ); - throw errors.ErrNotAuthorized; + throw new ErrNotAuthorized(); } // Check to see if the comment had a status that was editable. @@ -142,13 +145,13 @@ module.exports = { debug( 'rejecting comment edit because original comment has a non-editable status' ); - throw errors.ErrNotAuthorized; + throw new ErrNotAuthorized(); } // Check to see if the edit window expired. if (comment.created_at <= lastEditableCommentCreatedAt) { debug('rejecting comment edit because outside edit time window'); - throw errors.ErrEditWindowHasEnded; + throw new ErrEditWindowHasEnded(); } throw new Error('comment edit failed for an unexpected reason'); @@ -198,7 +201,7 @@ module.exports = { ); if (originalComment == null) { - throw errors.ErrNotFound; + throw new ErrNotFound(); } const editedComment = new CommentModel(originalComment.toObject()); diff --git a/services/limit.js b/services/limit.js index 6d46f3715..573d7a815 100644 --- a/services/limit.js +++ b/services/limit.js @@ -1,5 +1,5 @@ const ms = require('ms'); -const errors = require('../errors'); +const { ErrMaxRateLimit } = require('../errors'); const { createClientFactory } = require('./redis'); const client = createClientFactory(); @@ -60,7 +60,7 @@ class Limit { } if (tries > this.max) { - throw errors.ErrMaxRateLimit; + throw new ErrMaxRateLimit(this.max, tries); } return tries; diff --git a/services/moderation/index.js b/services/moderation/index.js index 4d87e190f..530d6f2a6 100644 --- a/services/moderation/index.js +++ b/services/moderation/index.js @@ -1,4 +1,4 @@ -const errors = require('../../errors'); +const { ErrNotFound } = require('../../errors'); const get = require('lodash/get'); // Load in the phases to use. @@ -92,14 +92,14 @@ const fetchOptions = async (ctx, comment) => { const assetID = get(comment, 'asset_id', null); if (assetID === null) { // And leave now if this asset wasn't found. - throw errors.ErrNotFound; + throw new ErrNotFound(); } // Load the asset. const asset = await Assets.getByID.load(assetID); if (!asset) { // And leave now if this asset wasn't found. - throw errors.ErrNotFound; + throw new ErrNotFound(); } // Combine the asset and the settings to get the asset settings. diff --git a/services/moderation/phases/commentLength.js b/services/moderation/phases/commentLength.js index 925115326..e19198ef1 100644 --- a/services/moderation/phases/commentLength.js +++ b/services/moderation/phases/commentLength.js @@ -8,7 +8,7 @@ module.exports = ( ) => { // Check to see if the body is too short, if it is, then complain about it! if (comment.body.length < 2) { - throw ErrCommentTooShort; + throw new ErrCommentTooShort(comment.body.length); } // Reject if the comment is too long diff --git a/services/passport.js b/services/passport.js index 5748c911b..0ae06afb8 100644 --- a/services/passport.js +++ b/services/passport.js @@ -6,7 +6,12 @@ const TokensService = require('./tokens'); const fetch = require('node-fetch'); const FormData = require('form-data'); const LocalStrategy = require('passport-local').Strategy; -const errors = require('../errors'); +const { + ErrLoginAttemptMaximumExceeded, + ErrNotAuthorized, + ErrAuthentication, + ErrNotVerified, +} = require('../errors'); const uuid = require('uuid'); const debug = require('debug')('talk:services:passport'); const bowser = require('bowser'); @@ -75,7 +80,7 @@ const HandleGenerateCredentials = (req, res, next) => (err, user) => { } if (!user) { - return next(errors.ErrNotAuthorized); + return next(new ErrNotAuthorized()); } // Generate the token to re-issue to the frontend. @@ -117,7 +122,7 @@ const HandleAuthPopupCallback = (req, res, next) => (err, user) => { if (!user) { return res.render('auth-callback', { - auth: { err: errors.ErrNotAuthorized, data: null }, + auth: { err: new ErrNotAuthorized(), data: null }, }); } @@ -143,7 +148,7 @@ async function ValidateUserLogin(loginProfile, user, done) { } if (user.disabled) { - return done(new errors.ErrAuthentication('Account disabled')); + return done(new ErrAuthentication('Account disabled')); } // If the user isn't a local user (i.e., a social user). @@ -169,7 +174,7 @@ async function ValidateUserLogin(loginProfile, user, done) { // If the profile doesn't have a metadata field, or it does not have a // confirmed_at field, or that field is null, then send them back. if (_.get(profile, 'metadata.confirmed_at', null) === null) { - return done(errors.ErrNotVerified); + return done(new ErrNotVerified()); } } @@ -209,7 +214,7 @@ const checkGeneralTokenBlacklist = jwt => .get(`jtir[${jwt.jti}]`) .then(expiry => { if (expiry != null) { - throw new errors.ErrAuthentication('token was revoked'); + throw new ErrAuthentication('token was revoked'); } }); @@ -392,7 +397,7 @@ const HandleFailedAttempt = async (email, userNeedsRecaptcha) => { await UsersService.recordLoginAttempt(email); } catch (err) { if ( - err === errors.ErrLoginAttemptMaximumExceeded && + err instanceof ErrLoginAttemptMaximumExceeded && !userNeedsRecaptcha && RECAPTCHA_ENABLED ) { @@ -448,7 +453,7 @@ passport.use( try { await UsersService.checkLoginAttempts(email); } catch (err) { - if (err === errors.ErrLoginAttemptMaximumExceeded) { + if (err instanceof ErrLoginAttemptMaximumExceeded) { // This says, we didn't have a recaptcha, yet we needed one.. Reject // here. diff --git a/services/settings.js b/services/settings.js index f918f9d01..ee5125183 100644 --- a/services/settings.js +++ b/services/settings.js @@ -1,6 +1,6 @@ const SettingModel = require('../models/setting'); const cache = require('./cache'); -const errors = require('../errors'); +const { ErrSettingsNotInit } = require('../errors'); const { dotize } = require('./utils'); const { SETTINGS_CACHE_TIME } = require('../config'); @@ -17,7 +17,7 @@ const retrieve = async fields => { settings = await SettingModel.findOne(selector); } if (!settings) { - throw errors.ErrSettingsNotInit; + throw new ErrSettingsNotInit(); } return settings; diff --git a/services/setup.js b/services/setup.js index 84f915ff2..2517d376b 100644 --- a/services/setup.js +++ b/services/setup.js @@ -2,7 +2,12 @@ const UsersService = require('./users'); const SettingsService = require('./settings'); const MigrationService = require('./migration'); const SettingsModel = require('../models/setting'); -const errors = require('../errors'); +const { + ErrMissingEmail, + ErrInstallLock, + ErrSettingsInit, + ErrSettingsNotInit, +} = require('../errors'); const { INSTALL_LOCK } = require('../config'); /** @@ -16,25 +21,25 @@ module.exports = class SetupService { static async isAvailable() { // Check if we have an install lock present. if (INSTALL_LOCK) { - throw errors.ErrInstallLock; + throw new ErrInstallLock(); } try { - // Get the current settings, we are expecing an error here. + // Get the current settings, we are expecting an error here. await SettingsService.retrieve(); // We should NOT have gotten a settings object, this means that the // application is already setup. Error out here. - throw errors.ErrSettingsInit; - } catch (e) { - // If the error is `not init`, then we're good, otherwise, it's something - // else. - if (e !== errors.ErrSettingsNotInit) { - throw e; + throw new ErrSettingsInit(); + } catch (err) { + // Allow the request to keep going here. + if (err instanceof ErrSettingsNotInit) { + return; } - // Allow the request to keep going here. - return; + // If the error is `not init`, then we're good, otherwise, it's something + // else. + throw err; } } @@ -44,7 +49,7 @@ module.exports = class SetupService { static validate({ settings, user: { email, username, password } }) { // Verify the email address of the user. if (!email) { - return Promise.reject(errors.ErrMissingEmail); + throw new ErrMissingEmail(); } // Create a settings model to use for validation. diff --git a/services/tags.js b/services/tags.js index 8183d0165..cc8934e0a 100644 --- a/services/tags.js +++ b/services/tags.js @@ -1,12 +1,10 @@ const CommentModel = require('../models/comment'); const AssetModel = require('../models/asset'); const UserModel = require('../models/user'); - const AssetsService = require('./assets'); const SettingsService = require('./settings'); const { ADD_COMMENT_TAG } = require('../perms/constants'); - -const errors = require('../errors'); +const { ErrNotAuthorized } = require('../errors'); const updateModel = async (item_type, query, update) => { // Get the model to update with. @@ -120,13 +118,13 @@ class TagsService { return { tagLink, ownership: true }; } - throw errors.ErrNotAuthorized; + throw new ErrNotAuthorized(); } // Only admin/moderators can modify unique tags, these are tags that are not // in the global list. if (!user.can(ADD_COMMENT_TAG)) { - throw errors.ErrNotAuthorized; + throw new ErrNotAuthorized(); } // Generate the tag in the event now that we have to create the tag for this diff --git a/services/users.js b/services/users.js index 0617d5158..d2dd60b0b 100644 --- a/services/users.js +++ b/services/users.js @@ -1,6 +1,21 @@ const uuid = require('uuid'); const bcrypt = require('bcryptjs'); -const errors = require('../errors'); +const { + ErrMaxRateLimit, + ErrLoginAttemptMaximumExceeded, + ErrNotFound, + ErrPermissionUpdateUsername, + ErrSameUsernameProvided, + ErrUsernameTaken, + ErrMissingUsername, + ErrSpecialChars, + ErrMissingPassword, + ErrPasswordTooShort, + ErrMissingEmail, + ErrEmailTaken, + ErrEmailAlreadyVerified, + ErrCannotIgnoreStaff, +} = require('../errors'); const { difference, sample, some, merge, random } = require('lodash'); const { ROOT_URL } = require('../config'); const { jwt: JWT_SECRET } = require('../secrets'); @@ -59,8 +74,8 @@ class UsersService { try { await loginRateLimiter.test(email.toLowerCase().trim()); } catch (err) { - if (err === errors.ErrMaxRateLimit) { - throw errors.ErrLoginAttemptMaximumExceeded; + if (err instanceof ErrMaxRateLimit) { + throw new ErrLoginAttemptMaximumExceeded(); } throw err; @@ -91,7 +106,7 @@ class UsersService { if (user === null) { user = await UserModel.findOne({ id }); if (user === null) { - throw errors.ErrNotFound; + throw new ErrNotFound(); } // Date comparisons are difficult when using MongoDB. Javascript will @@ -150,10 +165,10 @@ class UsersService { runValidators: true, } ); - if (user === null) { + if (!user) { user = await UserModel.findOne({ id }); - if (user === null) { - throw errors.ErrNotFound; + if (!user) { + throw new ErrNotFound(); } if (user.status.banned.status === status) { @@ -204,7 +219,7 @@ class UsersService { if (user === null) { user = await UserModel.findOne({ id }); if (user === null) { - throw errors.ErrNotFound; + throw new ErrNotFound(); } if (user.status.username.status === status) { @@ -259,15 +274,15 @@ class UsersService { if (!user) { user = await UsersService.findById(id); if (user === null) { - throw errors.ErrNotFound; + throw new ErrNotFound(); } if (user.status.username.status !== fromStatus) { - throw errors.ErrPermissionUpdateUsername; + throw new ErrPermissionUpdateUsername(); } if (!resetAllowed && user.username === username) { - throw errors.ErrSameUsernameProvided; + throw new ErrSameUsernameProvided(); } throw new Error('edit username failed for an unexpected reason'); @@ -276,7 +291,7 @@ class UsersService { return user; } catch (err) { if (err.code === 11000) { - throw errors.ErrUsernameTaken; + throw new ErrUsernameTaken(); } throw err; @@ -317,7 +332,7 @@ class UsersService { } if (attempts >= RECAPTCHA_INCORRECT_TRIGGER) { - throw errors.ErrLoginAttemptMaximumExceeded; + throw new ErrLoginAttemptMaximumExceeded(); } } @@ -515,11 +530,11 @@ class UsersService { const onlyLettersNumbersUnderscore = /^[A-Za-z0-9_]+$/; if (!username) { - throw errors.ErrMissingUsername; + throw new ErrMissingUsername(); } if (!onlyLettersNumbersUnderscore.test(username)) { - throw errors.ErrSpecialChars; + throw new ErrSpecialChars(); } if (checkAgainstWordlist) { @@ -539,11 +554,11 @@ class UsersService { */ static isValidPassword(password) { if (!password) { - throw errors.ErrMissingPassword; + throw new ErrMissingPassword(); } if (password.length < 8) { - throw errors.ErrPasswordTooShort; + throw new ErrPasswordTooShort(); } return password; @@ -558,7 +573,7 @@ class UsersService { */ static async createLocalUser(ctx, email, password, username) { if (!email) { - throw errors.ErrMissingEmail; + throw new ErrMissingEmail(); } email = email.toLowerCase().trim(); @@ -596,9 +611,9 @@ class UsersService { } catch (err) { if (err.code === 11000) { if (err.message.match('Username')) { - throw errors.ErrUsernameTaken; + throw new ErrUsernameTaken(); } - throw errors.ErrEmailTaken; + throw new ErrEmailTaken(); } throw err; } @@ -678,9 +693,7 @@ class UsersService { */ static async createPasswordResetToken(email, loc) { if (!email || typeof email !== 'string') { - throw new Error( - 'email is required when creating a JWT for resetting passord' - ); + throw new ErrMissingEmail(); } email = email.toLowerCase(); @@ -837,7 +850,7 @@ class UsersService { // Ensure that the user email hasn't already been verified. if (profile && profile.metadata && profile.metadata.confirmed_at) { - throw errors.ErrEmailAlreadyVerified; + throw new ErrEmailAlreadyVerified(); } return JWT_SECRET.sign( @@ -875,16 +888,16 @@ class UsersService { }, }); if (!user) { - throw errors.ErrNotFound; + throw new ErrNotFound(); } const profile = user.profiles.find(({ id }) => id === decoded.email); if (!profile) { - throw errors.ErrNotFound; + throw new ErrNotFound(); } if (profile.metadata && profile.metadata.confirmed_at !== null) { - throw errors.ErrEmailAlreadyVerified; + throw new ErrEmailAlreadyVerified(); } return decoded; @@ -943,7 +956,7 @@ class UsersService { const users = await UsersService.findByIdArray(usersToIgnore); if (some(users, user => user.isStaff())) { - throw errors.ErrCannotIgnoreStaff; + throw new ErrCannotIgnoreStaff(); } return UserModel.update( diff --git a/services/wordlist.js b/services/wordlist.js index 04012adcb..9e7e1581e 100644 --- a/services/wordlist.js +++ b/services/wordlist.js @@ -1,7 +1,7 @@ const debug = require('debug')('talk:services:wordlist'); const _ = require('lodash'); const SettingsService = require('./settings'); -const Errors = require('../errors'); +const { ErrContainsProfanity } = require('../errors'); const memoize = require('lodash/memoize'); const { escapeRegExp } = require('./regex'); @@ -96,7 +96,7 @@ class Wordlist { `the field "${fieldName}" contained a phrase "${phrase}" which contained a banned word/phrase` ); - errors.banned = Errors.ErrContainsProfanity; + errors.banned = new ErrContainsProfanity(phrase); // Stop looping through the fields now, we discovered the worst possible // situation (a banned word). @@ -109,7 +109,7 @@ class Wordlist { `the field "${fieldName}" contained a phrase "${phrase}" which contained a suspected word/phrase` ); - errors.suspect = Errors.ErrContainsProfanity; + errors.suspect = new ErrContainsProfanity(phrase); // Continue looping through the fields now, we discovered a possible bad // word (suspect). @@ -167,7 +167,7 @@ class Wordlist { return wl.load().then(() => { if (wl.regexp.banned.test(username)) { - return Errors.ErrContainsProfanity; + throw new ErrContainsProfanity(username); } }); } diff --git a/test/server/graph/context.js b/test/server/graph/context.js index a788f509b..b3319d5c4 100644 --- a/test/server/graph/context.js +++ b/test/server/graph/context.js @@ -1,6 +1,6 @@ const User = require('../../../models/user'); const Context = require('../../../graph/context'); -const errors = require('../../../errors'); +const { ErrNotAuthorized } = require('../../../errors'); const SettingsService = require('../../../services/settings'); const { expect } = require('chai'); @@ -54,7 +54,7 @@ describe('graph.Context', () => { throw new Error('should not reach this point'); }) .catch(err => { - expect(err).to.be.equal(errors.ErrNotAuthorized); + expect(err).to.be.an.instanceof(ErrNotAuthorized); }); }); }); diff --git a/test/server/services/wordlist.js b/test/server/services/wordlist.js index 7a11dbf80..313dde49f 100644 --- a/test/server/services/wordlist.js +++ b/test/server/services/wordlist.js @@ -1,4 +1,4 @@ -const Errors = require('../../../errors'); +const { ErrContainsProfanity } = require('../../../errors'); const Wordlist = require('../../../services/wordlist'); const SettingsService = require('../../../services/settings'); @@ -103,7 +103,8 @@ describe('services.Wordlist', () => { 'content' ); - expect(errors).to.have.property('banned', Errors.ErrContainsProfanity); + expect(errors).to.have.property('banned'); + expect(errors.banned).to.be.an.instanceof(ErrContainsProfanity); }); it('does not match on bodies not containing bad words', () => { From 2e74f79b79e305e59db7731c4780edf5ecbb82e4 Mon Sep 17 00:00:00 2001 From: Wyatt Johnson Date: Tue, 10 Apr 2018 17:33:56 -0600 Subject: [PATCH 56/84] Ignored User Reply Notifications --- graph/resolvers/user.js | 4 ++-- .../index.js | 17 +++++++++++++++-- 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/graph/resolvers/user.js b/graph/resolvers/user.js index 46fe0ac3c..67478785a 100644 --- a/graph/resolvers/user.js +++ b/graph/resolvers/user.js @@ -29,9 +29,9 @@ const User = { return Comments.getByQuery(query); }, - ignoredUsers({ ignoresUsers }, args, { user, loaders: { Users } }) { + ignoredUsers({ ignoresUsers }, args, { loaders: { Users } }) { // Return nothing if there is nothing to query for. - if (!user.ignoresUsers || user.ignoresUsers.length <= 0) { + if (!ignoresUsers || ignoresUsers.length <= 0) { return []; } diff --git a/plugins/talk-plugin-notifications-category-reply/index.js b/plugins/talk-plugin-notifications-category-reply/index.js index e214975ac..cce258daa 100644 --- a/plugins/talk-plugin-notifications-category-reply/index.js +++ b/plugins/talk-plugin-notifications-category-reply/index.js @@ -1,4 +1,4 @@ -const { get } = require('lodash'); +const { get, map } = require('lodash'); const path = require('path'); const handle = async (ctx, comment) => { @@ -23,6 +23,9 @@ const handle = async (ctx, comment) => { id user { id + ignoredUsers { + id + } notificationSettings { onReply } @@ -53,13 +56,23 @@ const handle = async (ctx, comment) => { return; } + // Pull out the author of the new comment. + const authorID = get(comment, 'author_id'); + // Check to see if this is yourself replying to yourself, if that's the case // don't send a notification. - if (userID === get(comment, 'author_id')) { + if (userID === authorID) { ctx.log.info('user id of parent comment is the same as the new comment'); return; } + // Check to see if this user is ignoring the user who replied to their + // comment. + if (map(get(comment, 'user.ignoredUsers', []), 'id').indexOf(authorID)) { + ctx.log.info('parent user has ignored the author of the new comment'); + return; + } + // The user does have notifications for replied comments enabled, queue the // notification to be sent. return { userID, date: comment.created_at, context: comment.id }; From 4eff55a0d19492cb16dbbd88e1c5e49b3049f8c3 Mon Sep 17 00:00:00 2001 From: Andrew Losowsky Date: Wed, 11 Apr 2018 14:18:25 -0400 Subject: [PATCH 57/84] Rename 03-04-product-guide-trust.md to 03-05-product-guide-trust.md --- ...{03-04-product-guide-trust.md => 03-05-product-guide-trust.md} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename docs/source/{03-04-product-guide-trust.md => 03-05-product-guide-trust.md} (100%) diff --git a/docs/source/03-04-product-guide-trust.md b/docs/source/03-05-product-guide-trust.md similarity index 100% rename from docs/source/03-04-product-guide-trust.md rename to docs/source/03-05-product-guide-trust.md From dd916a3ef9ed979223fc94c3f6ed5ea376a2d03d Mon Sep 17 00:00:00 2001 From: Andrew Losowsky Date: Wed, 11 Apr 2018 14:18:43 -0400 Subject: [PATCH 58/84] Rename 03-05-product-guide-trust.md to 03-06-product-guide-trust.md --- ...{03-05-product-guide-trust.md => 03-06-product-guide-trust.md} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename docs/source/{03-05-product-guide-trust.md => 03-06-product-guide-trust.md} (100%) diff --git a/docs/source/03-05-product-guide-trust.md b/docs/source/03-06-product-guide-trust.md similarity index 100% rename from docs/source/03-05-product-guide-trust.md rename to docs/source/03-06-product-guide-trust.md From b2dc178fdb80aa44227b6bbeac98a627a22746b1 Mon Sep 17 00:00:00 2001 From: Andrew Losowsky Date: Wed, 11 Apr 2018 14:19:02 -0400 Subject: [PATCH 59/84] Rename 03-06-product-guide-trust.md to 03-07-product-guide-trust.md --- ...{03-06-product-guide-trust.md => 03-07-product-guide-trust.md} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename docs/source/{03-06-product-guide-trust.md => 03-07-product-guide-trust.md} (100%) diff --git a/docs/source/03-06-product-guide-trust.md b/docs/source/03-07-product-guide-trust.md similarity index 100% rename from docs/source/03-06-product-guide-trust.md rename to docs/source/03-07-product-guide-trust.md From 6b7580acaf5610b5b615cb9b1f827ae215c9dc13 Mon Sep 17 00:00:00 2001 From: Andrew Losowsky Date: Wed, 11 Apr 2018 14:30:27 -0400 Subject: [PATCH 60/84] Create 03-03-user-roles.md --- docs/source/03-03-user-roles.md | 35 +++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 docs/source/03-03-user-roles.md diff --git a/docs/source/03-03-user-roles.md b/docs/source/03-03-user-roles.md new file mode 100644 index 000000000..bc9b91d81 --- /dev/null +++ b/docs/source/03-03-user-roles.md @@ -0,0 +1,35 @@ +--- +title: User Roles in Talk +permalink: /roles/ +--- + +We have four preset roles in Talk: + +**Commenter** +• A standard community member +• Could receive a badge (eg. 'Subscriber') via [a custom newsroom Plugin Recipe](https://docs.coralproject.net/talk/plugin-recipes/#recipe-subscriber) +• No moderation abilities +• No configuration abilities + +**Staff** +• A standard community member +• Receives a Staff badge when they comment +• Comments are automatically approved +• No moderation abilities +• No configuration abilities + +**Moderator** +• A standard community member +• Receives a Staff badge when they comment +• Comments are automatically approved +• Has full moderation privileges +• Can configure individual articles via the Configure tab on the article page +• No site-wide configuration abilities + +**Administrator** +• A standard community member +• Receives a Staff badge when they comment +• Comments are automatically approved +• Has full moderation privileges +• Can configure individual articles via the Configure tab on the article page +• Can configure site settings via the Configure tab in the moderation interface From 229428648312613a6a75b94d8e3ba6e696db99bc Mon Sep 17 00:00:00 2001 From: Andrew Losowsky Date: Wed, 11 Apr 2018 14:30:42 -0400 Subject: [PATCH 61/84] Rename 03-03-user-roles.md to 03-04-user-roles.md --- docs/source/{03-03-user-roles.md => 03-04-user-roles.md} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename docs/source/{03-03-user-roles.md => 03-04-user-roles.md} (100%) diff --git a/docs/source/03-03-user-roles.md b/docs/source/03-04-user-roles.md similarity index 100% rename from docs/source/03-03-user-roles.md rename to docs/source/03-04-user-roles.md From aef5076ac121a53643bafa86bbe01fe4cb1c343b Mon Sep 17 00:00:00 2001 From: Andrew Losowsky Date: Wed, 11 Apr 2018 14:31:50 -0400 Subject: [PATCH 62/84] Update 03-04-user-roles.md --- docs/source/03-04-user-roles.md | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/docs/source/03-04-user-roles.md b/docs/source/03-04-user-roles.md index bc9b91d81..e4eafeac7 100644 --- a/docs/source/03-04-user-roles.md +++ b/docs/source/03-04-user-roles.md @@ -6,17 +6,17 @@ permalink: /roles/ We have four preset roles in Talk: **Commenter** -• A standard community member -• Could receive a badge (eg. 'Subscriber') via [a custom newsroom Plugin Recipe](https://docs.coralproject.net/talk/plugin-recipes/#recipe-subscriber) -• No moderation abilities -• No configuration abilities +..• A standard community member +..• Could receive a badge (eg. 'Subscriber') via [a custom newsroom Plugin Recipe](https://docs.coralproject.net/talk/plugin-recipes/#recipe-subscriber) +..• No moderation abilities +..• No configuration abilities **Staff** -• A standard community member -• Receives a Staff badge when they comment -• Comments are automatically approved -• No moderation abilities -• No configuration abilities +..• A standard community member +..• Receives a Staff badge when they comment +..• Comments are automatically approved +..• No moderation abilities +..• No configuration abilities **Moderator** • A standard community member From 1dc3b855aacd8403bef072b6aa470b298ecc1450 Mon Sep 17 00:00:00 2001 From: Andrew Losowsky Date: Wed, 11 Apr 2018 14:33:30 -0400 Subject: [PATCH 63/84] Bullets replaced with asterisks per markdown --- docs/source/03-04-user-roles.md | 42 ++++++++++++++++----------------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/docs/source/03-04-user-roles.md b/docs/source/03-04-user-roles.md index e4eafeac7..6853183d8 100644 --- a/docs/source/03-04-user-roles.md +++ b/docs/source/03-04-user-roles.md @@ -6,30 +6,30 @@ permalink: /roles/ We have four preset roles in Talk: **Commenter** -..• A standard community member -..• Could receive a badge (eg. 'Subscriber') via [a custom newsroom Plugin Recipe](https://docs.coralproject.net/talk/plugin-recipes/#recipe-subscriber) -..• No moderation abilities -..• No configuration abilities +* A standard community member +* Could receive a badge (eg. 'Subscriber') via [a custom newsroom Plugin Recipe](https://docs.coralproject.net/talk/plugin-recipes/#recipe-subscriber) +* No moderation abilities +* No configuration abilities **Staff** -..• A standard community member -..• Receives a Staff badge when they comment -..• Comments are automatically approved -..• No moderation abilities -..• No configuration abilities +* A standard community member +* Receives a Staff badge when they comment +* Comments are automatically approved +* No moderation abilities +* No configuration abilities **Moderator** -• A standard community member -• Receives a Staff badge when they comment -• Comments are automatically approved -• Has full moderation privileges -• Can configure individual articles via the Configure tab on the article page -• No site-wide configuration abilities +* A standard community member +* Receives a Staff badge when they comment +* Comments are automatically approved +* Has full moderation privileges +* Can configure individual articles via the Configure tab on the article page +* No site-wide configuration abilities **Administrator** -• A standard community member -• Receives a Staff badge when they comment -• Comments are automatically approved -• Has full moderation privileges -• Can configure individual articles via the Configure tab on the article page -• Can configure site settings via the Configure tab in the moderation interface +* A standard community member +* Receives a Staff badge when they comment +* Comments are automatically approved +* Has full moderation privileges +* Can configure individual articles via the Configure tab on the article page +* Can configure site settings via the Configure tab in the moderation interface From f81e973315a7c07e138b3cde3d1ab836e1625aa7 Mon Sep 17 00:00:00 2001 From: Andrew Losowsky Date: Wed, 11 Apr 2018 15:50:42 -0400 Subject: [PATCH 64/84] Added User Roles to the menu --- docs/_config.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/_config.yml b/docs/_config.yml index c61fdeb21..6c0da733b 100644 --- a/docs/_config.yml +++ b/docs/_config.yml @@ -122,6 +122,8 @@ sidebar: url: /commenter-features/ - title: Moderator Features url: /moderator-features/ + - title: User Roles in Talk + url: /roles/ - title: Trust url: /trust/ - title: Toxic Comments From b51bb7486ebdcf11e6666a27cec1328ad0874247 Mon Sep 17 00:00:00 2001 From: Wyatt Johnson Date: Wed, 11 Apr 2018 15:56:03 -0600 Subject: [PATCH 65/84] Permission Updates - Restrict status history to ADMIN/MODERATORS - Restrict body history to ADMIN/MODERATORS --- graph/resolvers/comment.js | 22 ++++++++++++++++++++-- graph/typeDefs.graphql | 5 +++-- perms/constants/query.js | 1 + perms/reducers/query.js | 1 + 4 files changed, 25 insertions(+), 4 deletions(-) diff --git a/graph/resolvers/comment.js b/graph/resolvers/comment.js index 7005701ec..7fff6a1ea 100644 --- a/graph/resolvers/comment.js +++ b/graph/resolvers/comment.js @@ -1,6 +1,14 @@ const { property } = require('lodash'); -const { SEARCH_ACTIONS } = require('../../perms/constants'); -const { decorateWithTags, decorateWithPermissionCheck } = require('./util'); +const { + SEARCH_ACTIONS, + SEARCH_COMMENT_STATUS_HISTORY, + VIEW_BODY_HISTORY, +} = require('../../perms/constants'); +const { + decorateWithTags, + decorateWithPermissionCheck, + checkSelfField, +} = require('./util'); const Comment = { hasParent({ parent_id }) { @@ -65,4 +73,14 @@ decorateWithPermissionCheck(Comment, { actions: [SEARCH_ACTIONS], }); +// Protect privileged fields. +decorateWithPermissionCheck( + Comment, + { + status_history: [SEARCH_COMMENT_STATUS_HISTORY], + body_history: [VIEW_BODY_HISTORY], + }, + checkSelfField('author_id') +); + module.exports = Comment; diff --git a/graph/typeDefs.graphql b/graph/typeDefs.graphql index 2414faf1f..c1e7b9ecb 100644 --- a/graph/typeDefs.graphql +++ b/graph/typeDefs.graphql @@ -505,8 +505,9 @@ type Comment { # The actual comment data. body: String! - # The body history of the comment. - body_history: [CommentBodyHistory!]! + # The body history of the comment. Requires the `ADMIN` or `MODERATOR` role or + # the author. + body_history: [CommentBodyHistory!] # The tags on the comment tags: [TagLink!] diff --git a/perms/constants/query.js b/perms/constants/query.js index b846b15ce..197c5d9f9 100644 --- a/perms/constants/query.js +++ b/perms/constants/query.js @@ -10,4 +10,5 @@ module.exports = { LIST_OWN_TOKENS: 'LIST_OWN_TOKENS', VIEW_USER_ROLE: 'VIEW_USER_ROLE', VIEW_USER_EMAIL: 'VIEW_USER_EMAIL', + VIEW_BODY_HISTORY: 'VIEW_BODY_HISTORY', }; diff --git a/perms/reducers/query.js b/perms/reducers/query.js index 0852d8e2b..ed507139d 100644 --- a/perms/reducers/query.js +++ b/perms/reducers/query.js @@ -13,6 +13,7 @@ module.exports = (user, perm) => { case types.VIEW_PROTECTED_SETTINGS: case types.VIEW_USER_ROLE: case types.VIEW_USER_EMAIL: + case types.VIEW_BODY_HISTORY: return check(user, ['ADMIN', 'MODERATOR']); case types.LIST_OWN_TOKENS: return check(user, ['ADMIN']); From dc0c3b4a676072083eb1a202197e3b3ae9602e56 Mon Sep 17 00:00:00 2001 From: Wyatt Johnson Date: Wed, 11 Apr 2018 15:58:04 -0600 Subject: [PATCH 66/84] Authors cannot see their own status history --- graph/resolvers/comment.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/graph/resolvers/comment.js b/graph/resolvers/comment.js index 7fff6a1ea..b174dd5ae 100644 --- a/graph/resolvers/comment.js +++ b/graph/resolvers/comment.js @@ -68,16 +68,16 @@ const Comment = { // Decorate the Comment type resolver with a tags field. decorateWithTags(Comment); -// Protect direct action access. +// Protect direct action and status history access. decorateWithPermissionCheck(Comment, { actions: [SEARCH_ACTIONS], + status_history: [SEARCH_COMMENT_STATUS_HISTORY], }); // Protect privileged fields. decorateWithPermissionCheck( Comment, { - status_history: [SEARCH_COMMENT_STATUS_HISTORY], body_history: [VIEW_BODY_HISTORY], }, checkSelfField('author_id') From 86385e0d867d1a5e063562b4f5f035bb2a373804 Mon Sep 17 00:00:00 2001 From: Wyatt Johnson Date: Wed, 11 Apr 2018 19:02:38 -0600 Subject: [PATCH 67/84] Jest for server - Introduced the Jest testing framework into our server side code so plugins can now have tests that run --- .eslintrc.json | 3 + client/jest.config.js | 36 + jest.config.js | 42 +- package.json | 11 +- plugin-api/beta/server/getReactionConfig.js | 20 +- .../beta/server/getReactionConfig.spec.js | 51 + plugins/talk-plugin-akismet/index.js | 129 +-- .../{ => server}/config.js | 0 .../{ => server}/errors.js | 0 plugins/talk-plugin-akismet/server/hooks.js | 107 ++ .../talk-plugin-akismet/server/resolvers.js | 7 + .../server/resolvers.spec.js | 14 + .../server/typeDefs.graphql | 10 + .../talk-plugin-akismet/server/typeDefs.js | 7 + .../server/__mocks__/perspective.js | 11 + .../server/hooks.js | 7 +- .../server/hooks.spec.js | 31 + services/logging.js | 7 +- services/mongoose.js | 12 +- test/setupJest.js | 21 + yarn.lock | 1020 +++++++---------- 21 files changed, 774 insertions(+), 772 deletions(-) create mode 100644 client/jest.config.js create mode 100644 plugin-api/beta/server/getReactionConfig.spec.js rename plugins/talk-plugin-akismet/{ => server}/config.js (100%) rename plugins/talk-plugin-akismet/{ => server}/errors.js (100%) create mode 100644 plugins/talk-plugin-akismet/server/hooks.js create mode 100644 plugins/talk-plugin-akismet/server/resolvers.js create mode 100644 plugins/talk-plugin-akismet/server/resolvers.spec.js create mode 100644 plugins/talk-plugin-akismet/server/typeDefs.graphql create mode 100644 plugins/talk-plugin-akismet/server/typeDefs.js create mode 100644 plugins/talk-plugin-toxic-comments/server/__mocks__/perspective.js create mode 100644 plugins/talk-plugin-toxic-comments/server/hooks.spec.js create mode 100644 test/setupJest.js diff --git a/.eslintrc.json b/.eslintrc.json index 78f7c2397..d00d21106 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -1,3 +1,6 @@ { + "env": { + "jest": true + }, "extends": "@coralproject/eslint-config-talk" } diff --git a/client/jest.config.js b/client/jest.config.js new file mode 100644 index 000000000..02120d58e --- /dev/null +++ b/client/jest.config.js @@ -0,0 +1,36 @@ +const { pluginsPath } = require('../plugins'); + +const buildTargets = ['coral-admin']; + +const buildEmbeds = ['stream']; + +const specPattern = 'client/**/__tests__/**/*.spec.js?(x)'; + +module.exports = { + rootDir: '../', + testMatch: [ + `/${specPattern}`, + `/plugins/**/${specPattern}`, + ], + setupTestFrameworkScriptFile: '/test/client/setupJest.js', + modulePaths: [ + '/plugins', + '/client', + ...buildTargets.map(target => `/client/${target}/src`), + ...buildEmbeds.map(embed => `/client/coral-embed-${embed}/src`), + ], + moduleFileExtensions: ['js', 'jsx', 'json', 'yaml', 'yml'], + moduleDirectories: ['node_modules'], + transform: { + '^.+\\.jsx?$': 'babel-jest', + '\\.ya?ml$': '/test/client/yamlTransformer.js', + }, + testResultsProcessor: process.env.JEST_REPORTER, + moduleNameMapper: { + '^plugin-api\\/(.*)$': '/plugin-api/$1', + '^plugins\\/(.*)$': '/plugins/$1', + '^pluginsConfig$': pluginsPath, + '\\.(scss|css|less)$': 'identity-obj-proxy', + '\\.(gif|ttf|eot|svg)$': '/test/client/fileMock.js', + }, +}; diff --git a/jest.config.js b/jest.config.js index 7c3755bea..8519ef452 100644 --- a/jest.config.js +++ b/jest.config.js @@ -1,40 +1,8 @@ -const path = require('path'); -const { pluginsPath } = require('./plugins'); - -const buildTargets = ['coral-admin']; - -const buildEmbeds = ['stream']; - -// jest.config.js module.exports = { - testMatch: ['**/client/**/__tests__/**/*.js?(x)'], - setupTestFrameworkScriptFile: '/test/client/setupJest.js', - modulePaths: [ - '/plugins', - '/client', - ...buildTargets.map(target => - path.join('', 'client', target, 'src') - ), - ...buildEmbeds.map(embed => - path.join('', 'client', `coral-embed-${embed}`, 'src') - ), - ], - moduleFileExtensions: ['js', 'jsx', 'json', 'yaml', 'yml'], - moduleDirectories: ['node_modules'], - - transform: { - '^.+\\.jsx?$': 'babel-jest', - '\\.ya?ml$': '/test/client/yamlTransformer.js', - }, - + projects: ['', '/client'], + testPathIgnorePatterns: ['client'], + setupTestFrameworkScriptFile: '/test/setupJest.js', testResultsProcessor: process.env.JEST_REPORTER, - - moduleNameMapper: { - '^plugin-api\\/(.*)$': '/plugin-api/$1', - '^plugins\\/(.*)$': '/plugins/$1', - '^pluginsConfig$': pluginsPath, - - '\\.(scss|css|less)$': 'identity-obj-proxy', - '\\.(gif|ttf|eot|svg)$': '/test/client/fileMock.js', - }, + testEnvironment: 'node', + modulePaths: [''], }; diff --git a/package.json b/package.json index c9243a560..68fb0519e 100644 --- a/package.json +++ b/package.json @@ -18,10 +18,11 @@ "lint:js": "eslint bin/cli* .", "lint": "npm-run-all lint:*", "plugins:reconcile": "./bin/cli plugins reconcile", - "test": "npm-run-all test:client test:server", - "test:server": "TEST_MODE=unit NODE_ENV=test mocha -R ${MOCHA_REPORTER:-spec}", - "test:client": "TEST_MODE=unit NODE_ENV=test jest", - "test:client:watch": "TEST_MODE=unit NODE_ENV=test jest --watch", + "test": "npm-run-all test:jest test:mocha", + "test:jest": "TEST_MODE=unit NODE_ENV=test jest --runInBand", + "test:client": "TEST_MODE=unit NODE_ENV=test jest --projects client", + "test:mocha": "TEST_MODE=unit NODE_ENV=test mocha -R ${MOCHA_REPORTER:-spec}", + "test:server:jest": "TEST_MODE=unit NODE_ENV=test jest --runInBand --projects .", "e2e": "./scripts/e2e.js", "e2e:ci": "./scripts/e2e-ci.sh", "heroku-postbuild": "npm-run-all plugins:reconcile build", @@ -127,6 +128,7 @@ "inquirer-autocomplete-prompt": "^0.12.1", "ioredis": "3.1.4", "ip": "^1.1.5", + "jest": "^22.4.3", "joi": "^13.0.0", "jsonwebtoken": "^8.0.0", "jwt-decode": "^2.2.0", @@ -226,7 +228,6 @@ "eslint-plugin-mocha": "^4.11.0", "husky": "^0.14.3", "identity-obj-proxy": "^3.0.0", - "jest": "^21.2.1", "jest-junit": "^3.6.0", "lint-staged": "^7.0.0", "mocha": "^3.1.2", diff --git a/plugin-api/beta/server/getReactionConfig.js b/plugin-api/beta/server/getReactionConfig.js index ffb1a3c07..40832ef74 100644 --- a/plugin-api/beta/server/getReactionConfig.js +++ b/plugin-api/beta/server/getReactionConfig.js @@ -239,26 +239,14 @@ function getReactionConfig(reaction) { hooks: { Action: { __resolveType: { - post({ action_type }) { - switch (action_type) { - case REACTION: - return `${Reaction}Action`; - default: - return undefined; - } - }, + post: ({ action_type }) => + action_type === REACTION ? `${Reaction}Action` : undefined, }, }, ActionSummary: { __resolveType: { - post({ action_type }) { - switch (action_type) { - case REACTION: - return `${Reaction}ActionSummary`; - default: - return undefined; - } - }, + post: ({ action_type = '' } = {}) => + action_type === REACTION ? `${Reaction}ActionSummary` : undefined, }, }, }, diff --git a/plugin-api/beta/server/getReactionConfig.spec.js b/plugin-api/beta/server/getReactionConfig.spec.js new file mode 100644 index 000000000..564f588b2 --- /dev/null +++ b/plugin-api/beta/server/getReactionConfig.spec.js @@ -0,0 +1,51 @@ +const getReactionConfig = require('./getReactionConfig'); + +describe('plugins-api', () => { + describe('getReactionConfig', () => { + let config; + beforeEach(() => { + config = getReactionConfig('heart'); + }); + + describe('context', () => { + it('provides a sort function', () => { + expect(config.context.Sort).toBeInstanceOf(Function); + const sort = config.context.Sort(); + expect(sort.Comments).toHaveProperty('hearts'); + }); + }); + + describe('hooks', () => { + it('handles the __resolveType properly', () => { + expect(config.hooks.ActionSummary.__resolveType).toHaveProperty('post'); + expect(config.hooks.ActionSummary.__resolveType.post).toBeInstanceOf( + Function + ); + expect( + config.hooks.ActionSummary.__resolveType.post({}) + ).toBeUndefined(); + expect( + config.hooks.ActionSummary.__resolveType.post({ action_type: 'LOVE' }) + ).toBeUndefined(); + expect( + config.hooks.ActionSummary.__resolveType.post({ + action_type: 'HEART', + }) + ).toEqual('HeartActionSummary'); + }); + it('handles the __resolveType properly', () => { + expect(config.hooks.Action.__resolveType).toHaveProperty('post'); + expect(config.hooks.Action.__resolveType.post).toBeInstanceOf(Function); + expect(config.hooks.Action.__resolveType.post({})).toBeUndefined(); + expect( + config.hooks.Action.__resolveType.post({ action_type: 'LOVE' }) + ).toBeUndefined(); + expect( + config.hooks.Action.__resolveType.post({ + action_type: 'HEART', + }) + ).toEqual('HeartAction'); + }); + }); + }); +}); diff --git a/plugins/talk-plugin-akismet/index.js b/plugins/talk-plugin-akismet/index.js index c57f5da5d..823faf182 100644 --- a/plugins/talk-plugin-akismet/index.js +++ b/plugins/talk-plugin-akismet/index.js @@ -1,126 +1,5 @@ -const debug = require('debug')('talk:plugin:akismet'); -const { ErrSpam } = require('./errors'); -const akismet = require('akismet-api'); -const { get, merge } = require('lodash'); -const { KEY, SITE } = require('./config'); -const client = akismet.client({ - key: KEY, - blog: SITE, -}); +const typeDefs = require('./server/typeDefs'); +const hooks = require('./server/hooks'); +const resolvers = require('./server/resolvers'); -let enabled = true; - -// TODO: when using a developer key, this is possible, the plus plan does not -// allow us to check the key. -// let enabled = false; -// client.verifyKey((err, valid) => { -// if (err) { -// throw err; -// } - -// if (valid) { -// enabled = true; -// } else { -// throw new Error('Akismet key is invalid'); -// } -// }); - -module.exports = { - typeDefs: ` - input CreateCommentInput { - - # If true, the mutation will fail when the - # body contains detected spam. - checkSpam: Boolean - } - - type Comment { - spam: Boolean - } - `, - hooks: { - RootMutation: { - createComment: { - async pre(_, { input }, { loaders, parent: req }) { - // If the key validation failed, then we can't run with the client. - if (!enabled) { - debug('not enabled, passing'); - return; - } - - let spam = false; - try { - const user_ip = get(req, 'ip', false); - if (!user_ip) { - debug('no ip on request'); - return; - } - - // Get some headers from the request. - const user_agent = req.get('User-Agent'); - if (!user_agent || user_agent.length === 0) { - debug('no user agent on request'); - return; - } - - const referrer = req.get('Referrer'); - if (!referrer || referrer.length === 0) { - debug('no referrer on request'); - return; - } - - // Get the Asset that the comment is being made against. - const asset = await loaders.Assets.getByID.load(input.asset_id); - if (!asset) { - debug('asset not found for new comment'); - return; - } - - // Send off the comment to Akismet to check to see what they say. - spam = await client.checkSpam({ - user_ip, - user_agent, - referrer, - permalink: asset.url, - comment_type: 'comment', - comment_content: input.body, - is_test: true, - }); - - debug(`comment analyzed as ${spam ? 'being' : 'not being'} spam`); - } catch (err) { - console.trace(err); - return; - } - - // Attach scores to metadata. - input.metadata = merge({}, input.metadata || {}, { - akismet: spam, - }); - - if (spam) { - if (input.checkSpam) { - throw new ErrSpam(); - } - - // Attach reason information for the flag being added. - input.status = 'SYSTEM_WITHHELD'; - input.actions = - input.actions && input.actions.length >= 0 ? input.actions : []; - input.actions.push({ - action_type: 'FLAG', - user_id: null, - group_id: 'SPAM_COMMENT', - metadata: {}, - }); - } - }, - }, - }, - }, - resolvers: { - Comment: { - spam: comment => get(comment, 'metadata.akismet', null), - }, - }, -}; +module.exports = { typeDefs, hooks, resolvers }; diff --git a/plugins/talk-plugin-akismet/config.js b/plugins/talk-plugin-akismet/server/config.js similarity index 100% rename from plugins/talk-plugin-akismet/config.js rename to plugins/talk-plugin-akismet/server/config.js diff --git a/plugins/talk-plugin-akismet/errors.js b/plugins/talk-plugin-akismet/server/errors.js similarity index 100% rename from plugins/talk-plugin-akismet/errors.js rename to plugins/talk-plugin-akismet/server/errors.js diff --git a/plugins/talk-plugin-akismet/server/hooks.js b/plugins/talk-plugin-akismet/server/hooks.js new file mode 100644 index 000000000..80a233584 --- /dev/null +++ b/plugins/talk-plugin-akismet/server/hooks.js @@ -0,0 +1,107 @@ +const debug = require('debug')('talk:plugin:akismet'); +const { ErrSpam } = require('./errors'); +const akismet = require('akismet-api'); +const { get, merge } = require('lodash'); +const { KEY, SITE } = require('./config'); +const client = akismet.client({ + key: KEY, + blog: SITE, +}); + +let enabled = true; + +// TODO: when using a developer key, this is possible, the plus plan does not +// allow us to check the key. +// let enabled = false; +// client.verifyKey((err, valid) => { +// if (err) { +// throw err; +// } + +// if (valid) { +// enabled = true; +// } else { +// throw new Error('Akismet key is invalid'); +// } +// }); + +module.exports = { + RootMutation: { + createComment: { + async pre(_, { input }, { loaders, parent: req }) { + // If the key validation failed, then we can't run with the client. + if (!enabled) { + debug('not enabled, passing'); + return; + } + + let spam = false; + try { + const user_ip = get(req, 'ip', false); + if (!user_ip) { + debug('no ip on request'); + return; + } + + // Get some headers from the request. + const user_agent = req.get('User-Agent'); + if (!user_agent || user_agent.length === 0) { + debug('no user agent on request'); + return; + } + + const referrer = req.get('Referrer'); + if (!referrer || referrer.length === 0) { + debug('no referrer on request'); + return; + } + + // Get the Asset that the comment is being made against. + const asset = await loaders.Assets.getByID.load(input.asset_id); + if (!asset) { + debug('asset not found for new comment'); + return; + } + + // Send off the comment to Akismet to check to see what they say. + spam = await client.checkSpam({ + user_ip, + user_agent, + referrer, + permalink: asset.url, + comment_type: 'comment', + comment_content: input.body, + is_test: true, + }); + + debug(`comment analyzed as ${spam ? 'being' : 'not being'} spam`); + } catch (err) { + console.trace(err); + return; + } + + // Attach scores to metadata. + input.metadata = merge({}, input.metadata || {}, { + akismet: spam, + }); + + if (spam) { + if (input.checkSpam) { + throw new ErrSpam(); + } + + // Attach reason information for the flag being added. + input.status = 'SYSTEM_WITHHELD'; + input.actions = + input.actions && input.actions.length >= 0 ? input.actions : []; + input.actions.push({ + action_type: 'FLAG', + user_id: null, + group_id: 'SPAM_COMMENT', + metadata: {}, + }); + } + }, + }, + }, +}; diff --git a/plugins/talk-plugin-akismet/server/resolvers.js b/plugins/talk-plugin-akismet/server/resolvers.js new file mode 100644 index 000000000..a300f510f --- /dev/null +++ b/plugins/talk-plugin-akismet/server/resolvers.js @@ -0,0 +1,7 @@ +const { get } = require('lodash'); + +module.exports = { + Comment: { + spam: comment => get(comment, 'metadata.akismet', null), + }, +}; diff --git a/plugins/talk-plugin-akismet/server/resolvers.spec.js b/plugins/talk-plugin-akismet/server/resolvers.spec.js new file mode 100644 index 000000000..06ee6168e --- /dev/null +++ b/plugins/talk-plugin-akismet/server/resolvers.spec.js @@ -0,0 +1,14 @@ +const resolvers = require('./resolvers'); + +describe('talk-plugin-akismet', () => { + describe('resolvers', () => { + it('resolves when there is a akismet value', () => { + const spam = resolvers.Comment.spam({ metadata: { akismet: true } }); + expect(spam).toEqual(true); + }); + it('resolves when there not is a akismet value', () => { + const spam = resolvers.Comment.spam({}); + expect(spam).toEqual(null); + }); + }); +}); diff --git a/plugins/talk-plugin-akismet/server/typeDefs.graphql b/plugins/talk-plugin-akismet/server/typeDefs.graphql new file mode 100644 index 000000000..61ded658c --- /dev/null +++ b/plugins/talk-plugin-akismet/server/typeDefs.graphql @@ -0,0 +1,10 @@ +input CreateCommentInput { + + # If true, the mutation will fail when the + # body contains detected spam. + checkSpam: Boolean +} + +type Comment { + spam: Boolean +} diff --git a/plugins/talk-plugin-akismet/server/typeDefs.js b/plugins/talk-plugin-akismet/server/typeDefs.js new file mode 100644 index 000000000..7ab1954e1 --- /dev/null +++ b/plugins/talk-plugin-akismet/server/typeDefs.js @@ -0,0 +1,7 @@ +const fs = require('fs'); +const path = require('path'); + +module.exports = fs.readFileSync( + path.join(__dirname, 'typeDefs.graphql'), + 'utf8' +); diff --git a/plugins/talk-plugin-toxic-comments/server/__mocks__/perspective.js b/plugins/talk-plugin-toxic-comments/server/__mocks__/perspective.js new file mode 100644 index 000000000..cda3cf841 --- /dev/null +++ b/plugins/talk-plugin-toxic-comments/server/__mocks__/perspective.js @@ -0,0 +1,11 @@ +let values = {}; + +const getScores = () => values.getScores; + +const isToxic = () => values.isToxic; + +const setValues = newValues => { + values = newValues; +}; + +module.exports = { getScores, isToxic, setValues }; diff --git a/plugins/talk-plugin-toxic-comments/server/hooks.js b/plugins/talk-plugin-toxic-comments/server/hooks.js index 0be363ea1..d35b5cc20 100644 --- a/plugins/talk-plugin-toxic-comments/server/hooks.js +++ b/plugins/talk-plugin-toxic-comments/server/hooks.js @@ -1,11 +1,6 @@ const { getScores, isToxic } = require('./perspective'); const { ErrToxic } = require('./errors'); -// We don't add the hooks during _test_ as the perspective API is not available. -if (process.env.NODE_ENV === 'test') { - return null; -} - module.exports = { RootMutation: { createComment: { @@ -16,7 +11,7 @@ module.exports = { scores = await getScores(input.body); } catch (err) { // Warn and let mutation pass. - console.trace(err); + console.trace(err); // TODO: log/handle this differently? return; } diff --git a/plugins/talk-plugin-toxic-comments/server/hooks.spec.js b/plugins/talk-plugin-toxic-comments/server/hooks.spec.js new file mode 100644 index 000000000..d9fbe67e6 --- /dev/null +++ b/plugins/talk-plugin-toxic-comments/server/hooks.spec.js @@ -0,0 +1,31 @@ +const hooks = require('./hooks'); +const { ErrToxic } = require('./errors'); + +// Mock out the perspective api call. +jest.mock('./perspective'); + +describe('talk-plugin-toxic-comments', () => { + describe('hooks', () => { + beforeEach(() => { + require('./perspective').setValues({ isToxic: true }); + }); + + it('sets the correct values for a toxic comment', async () => { + let input = { body: 'This is a body.', checkToxicity: false }; + await hooks.RootMutation.createComment.pre(null, { input }, null, null); + expect(input).toHaveProperty('status', 'SYSTEM_WITHHELD'); + }); + + it('throws an error when a toxic comment is sent', async () => { + expect.assertions(1); + await expect( + hooks.RootMutation.createComment.pre( + null, + { input: { checkToxicity: true } }, + null, + null + ) + ).rejects.toBeInstanceOf(ErrToxic); + }); + }); +}); diff --git a/services/logging.js b/services/logging.js index 3e2a6d731..7ae3654b1 100644 --- a/services/logging.js +++ b/services/logging.js @@ -7,11 +7,11 @@ const { LOGGING_LEVEL, REVISION_HASH } = require('../config'); // but will send JSON logs in production that's parsable by a system like ELK. const streams = (() => { // In development, use the debug stream printer. - if (process.env.NODE_ENV === 'development') { + if (process.env.NODE_ENV !== 'production') { const debug = require('bunyan-debug-stream'); return [ { - level: 'debug', + level: LOGGING_LEVEL, type: 'raw', stream: debug({ basepath: path.resolve(__dirname, '..'), @@ -22,7 +22,7 @@ const streams = (() => { } // In production, emit JSON. - return [{ stream: process.stdout, level: 'info' }]; + return [{ stream: process.stdout, level: LOGGING_LEVEL }]; })(); // logger is the base logger used by all logging systems in Talk. @@ -31,7 +31,6 @@ const logger = createBunyanLogger({ name: 'talk', version, revision: REVISION_HASH, - level: LOGGING_LEVEL, streams, serializers: stdSerializers, }); diff --git a/services/mongoose.js b/services/mongoose.js index 5e498cee8..66304ee07 100644 --- a/services/mongoose.js +++ b/services/mongoose.js @@ -37,6 +37,15 @@ if (WEBPACK) { // here just ensures that the application can quit correctly. mongoose.disconnect(); } else { + mongoose.connection.on('connected', () => logger.debug('mongodb connected')); + mongoose.connection.on('disconnected', () => + logger.debug('mongodb disconnected') + ); + + setTimeout(() => { + mongoose.disconnect(); + }, 5000); + // Connect to the Mongo instance. mongoose .connect(MONGO_URL, { @@ -45,9 +54,6 @@ if (WEBPACK) { autoIndex: CREATE_MONGO_INDEXES, }, }) - .then(() => { - logger.debug('mongodb connection established'); - }) .catch(err => { console.error(err); process.exit(1); diff --git a/test/setupJest.js b/test/setupJest.js new file mode 100644 index 000000000..9ff2d8af4 --- /dev/null +++ b/test/setupJest.js @@ -0,0 +1,21 @@ +const mongoose = require('../services/mongoose'); + +beforeEach(async () => { + await Promise.all( + Object.keys(mongoose.connection.collections).map(collection => { + return new Promise((resolve, reject) => { + mongoose.connection.collections[collection].remove(function(err) { + if (err) { + return reject(err); + } + + return resolve(); + }); + }); + }) + ); +}); + +afterAll(async function() { + await mongoose.disconnect(); +}); diff --git a/yarn.lock b/yarn.lock index b5d130ab0..598b56829 100644 --- a/yarn.lock +++ b/yarn.lock @@ -68,7 +68,7 @@ lodash "^4.2.0" to-fast-properties "^2.0.0" -"@coralproject/eslint-config-talk@^0.1.0", "@coralproject/eslint-config-talk@^0.1.1": +"@coralproject/eslint-config-talk@^0.1.0": version "0.1.1" resolved "https://registry.yarnpkg.com/@coralproject/eslint-config-talk/-/eslint-config-talk-0.1.1.tgz#71991b4937a3ffe657128d7f1170da4b5fb75c9e" dependencies: @@ -126,12 +126,6 @@ to-title-case "~1.0.0" url-regex "~4.1.1" -"@types/form-data@*": - version "2.2.1" - resolved "https://registry.yarnpkg.com/@types/form-data/-/form-data-2.2.1.tgz#ee2b3b8eaa11c0938289953606b745b738c54b1e" - dependencies: - "@types/node" "*" - "@types/graphql@0.10.2": version "0.10.2" resolved "https://registry.yarnpkg.com/@types/graphql/-/graphql-0.10.2.tgz#d7c79acbaa17453b6681c80c34b38fcb10c4c08c" @@ -144,25 +138,10 @@ version "0.9.4" resolved "https://registry.yarnpkg.com/@types/graphql/-/graphql-0.9.4.tgz#cdeb6bcbef9b6c584374b81aa7f48ecf3da404fa" -"@types/lodash@^4.14.50": - version "4.14.106" - resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.106.tgz#6093e9a02aa567ddecfe9afadca89e53e5dce4dd" - "@types/node@*": version "8.0.53" resolved "https://registry.yarnpkg.com/@types/node/-/node-8.0.53.tgz#396b35af826fa66aad472c8cb7b8d5e277f4e6d8" -"@types/node@^7.0.0": - version "7.0.59" - resolved "https://registry.yarnpkg.com/@types/node/-/node-7.0.59.tgz#fd7dceba9521c2d62c3e0eda8c5d704bf88b261d" - -"@types/request@^0.0.39": - version "0.0.39" - resolved "https://registry.yarnpkg.com/@types/request/-/request-0.0.39.tgz#168b96cf4253c5d54d403f746f82ee7aed47ce2c" - dependencies: - "@types/form-data" "*" - "@types/node" "*" - "@types/ws@^3.0.0": version "3.2.0" resolved "https://registry.yarnpkg.com/@types/ws/-/ws-3.2.0.tgz#988ff690e6ed10068a86aa0e9f842d0a03c09e21" @@ -180,7 +159,7 @@ a-sync-waterfall@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/a-sync-waterfall/-/a-sync-waterfall-1.0.0.tgz#38e8319d79379e24628845b53b96722b29e0e47c" -abab@^1.0.0, abab@^1.0.3, abab@^1.0.4: +abab@^1.0.0, abab@^1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/abab/-/abab-1.0.4.tgz#5faad9c2c07f60dd76770f71cf025b62a63cfd4e" @@ -195,13 +174,6 @@ accepts@^1.3.4, accepts@~1.3.4: mime-types "~2.1.16" negotiator "0.6.1" -accepts@~1.3.5: - version "1.3.5" - resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.5.tgz#eb777df6011723a3b14e8a72c0805c8e86746bd2" - dependencies: - mime-types "~2.1.18" - negotiator "0.6.1" - acorn-dynamic-import@^2.0.0: version "2.0.2" resolved "https://registry.yarnpkg.com/acorn-dynamic-import/-/acorn-dynamic-import-2.0.2.tgz#c752bd210bef679501b6c6cb7fc84f8f47158cc4" @@ -214,7 +186,7 @@ acorn-globals@^1.0.4: dependencies: acorn "^2.1.0" -acorn-globals@^3.0.0, acorn-globals@^3.1.0: +acorn-globals@^3.0.0: version "3.1.0" resolved "https://registry.yarnpkg.com/acorn-globals/-/acorn-globals-3.1.0.tgz#fd8270f71fbb4996b004fa880ee5d46573a731bf" dependencies: @@ -256,10 +228,6 @@ acorn@^5.3.0: version "5.4.1" resolved "https://registry.yarnpkg.com/acorn/-/acorn-5.4.1.tgz#fdc58d9d17f4a4e98d102ded826a9b9759125102" -acorn@^5.5.0: - version "5.5.3" - resolved "https://registry.yarnpkg.com/acorn/-/acorn-5.5.3.tgz#f473dd47e0277a08e28e9bec5aeeb04751f0b8c9" - addressparser@1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/addressparser/-/addressparser-1.0.1.tgz#47afbe1a2a9262191db6838e4fd1d39b40821746" @@ -888,6 +856,13 @@ babel-jest@^21.2.0: babel-plugin-istanbul "^4.0.0" babel-preset-jest "^21.2.0" +babel-jest@^22.4.3: + version "22.4.3" + resolved "https://registry.yarnpkg.com/babel-jest/-/babel-jest-22.4.3.tgz#4b7a0b6041691bbd422ab49b3b73654a49a6627a" + dependencies: + babel-plugin-istanbul "^4.1.5" + babel-preset-jest "^22.4.3" + babel-loader@^7.1.2: version "7.1.2" resolved "https://registry.yarnpkg.com/babel-loader/-/babel-loader-7.1.2.tgz#f6cbe122710f1aa2af4d881c6d5b54358ca24126" @@ -922,10 +897,23 @@ babel-plugin-istanbul@^4.0.0: istanbul-lib-instrument "^1.7.5" test-exclude "^4.1.1" +babel-plugin-istanbul@^4.1.5: + version "4.1.6" + resolved "https://registry.yarnpkg.com/babel-plugin-istanbul/-/babel-plugin-istanbul-4.1.6.tgz#36c59b2192efce81c5b378321b74175add1c9a45" + dependencies: + babel-plugin-syntax-object-rest-spread "^6.13.0" + find-up "^2.1.0" + istanbul-lib-instrument "^1.10.1" + test-exclude "^4.2.1" + babel-plugin-jest-hoist@^21.2.0: version "21.2.0" resolved "https://registry.yarnpkg.com/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-21.2.0.tgz#2cef637259bd4b628a6cace039de5fcd14dbb006" +babel-plugin-jest-hoist@^22.4.3: + version "22.4.3" + resolved "https://registry.yarnpkg.com/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-22.4.3.tgz#7d8bcccadc2667f96a0dcc6afe1891875ee6c14a" + babel-plugin-syntax-async-functions@^6.8.0: version "6.13.0" resolved "https://registry.yarnpkg.com/babel-plugin-syntax-async-functions/-/babel-plugin-syntax-async-functions-6.13.0.tgz#cad9cad1191b5ad634bf30ae0872391e0647be95" @@ -1258,6 +1246,13 @@ babel-preset-jest@^21.2.0: babel-plugin-jest-hoist "^21.2.0" babel-plugin-syntax-object-rest-spread "^6.13.0" +babel-preset-jest@^22.4.3: + version "22.4.3" + resolved "https://registry.yarnpkg.com/babel-preset-jest/-/babel-preset-jest-22.4.3.tgz#e92eef9813b7026ab4ca675799f37419b5a44156" + dependencies: + babel-plugin-jest-hoist "^22.4.3" + babel-plugin-syntax-object-rest-spread "^6.13.0" + babel-preset-react@^6.23.0: version "6.24.1" resolved "https://registry.yarnpkg.com/babel-preset-react/-/babel-preset-react-6.24.1.tgz#ba69dfaea45fc3ec639b6a4ecea6e17702c91380" @@ -1809,13 +1804,6 @@ caseless@~0.12.0: version "0.12.0" resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc" -casual@^1.5.19: - version "1.5.19" - resolved "https://registry.yarnpkg.com/casual/-/casual-1.5.19.tgz#66fac46f7ae463f468f5913eb139f9c41c58bbf2" - dependencies: - mersenne-twister "^1.0.1" - moment "^2.15.2" - center-align@^0.1.1: version "0.1.3" resolved "https://registry.yarnpkg.com/center-align/-/center-align-0.1.3.tgz#aa0d32629b6ee972200411cbd4461c907bc2b7ad" @@ -2092,6 +2080,14 @@ cliui@^3.0.3, cliui@^3.2.0: strip-ansi "^3.0.1" wrap-ansi "^2.0.0" +cliui@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/cliui/-/cliui-4.0.0.tgz#743d4650e05f36d1ed2575b59638d87322bfbbcc" + dependencies: + string-width "^2.1.1" + strip-ansi "^4.0.0" + wrap-ansi "^2.0.0" + clone@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/clone/-/clone-1.0.2.tgz#260b7a99ebb1edfe247538175f783243cb19d149" @@ -2239,6 +2235,10 @@ commondir@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/commondir/-/commondir-1.0.1.tgz#ddd800da0c66127393cca5950ea968a3aaf1253b" +compare-versions@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/compare-versions/-/compare-versions-3.1.0.tgz#43310256a5c555aaed4193c04d8f154cf9c6efd5" + component-emitter@^1.2.0, component-emitter@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/component-emitter/-/component-emitter-1.2.1.tgz#137918d6d78283f7df7a6b7c5a63e140e69425e6" @@ -2373,10 +2373,6 @@ content-security-policy-builder@1.1.0: dependencies: dashify "^0.2.0" -content-type-parser@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/content-type-parser/-/content-type-parser-1.0.1.tgz#c3e56988c53c65127fb46d4032a3a900246fdc94" - content-type-parser@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/content-type-parser/-/content-type-parser-1.0.2.tgz#caabe80623e63638b2502fd4c7f12ff4ce2352e7" @@ -2385,7 +2381,11 @@ content-type@~1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.4.tgz#e138cc75e040c727b1966fe5e5f8c9aee256fe3b" -convert-source-map@^1.4.0, convert-source-map@^1.5.0: +convert-source-map@^1.4.0: + version "1.5.1" + resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.5.1.tgz#b8278097b9bc229365de5c62cf5fcaed8b5599e5" + +convert-source-map@^1.5.0: version "1.5.0" resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.5.0.tgz#9acd70851c6d5dfdd93d9282e5edf94a03ff46b5" @@ -2477,10 +2477,6 @@ cosmiconfig@^4.0.0, cosmiconfig@~4.0.0: parse-json "^4.0.0" require-from-string "^2.0.1" -crc@3.4.4: - version "3.4.4" - resolved "https://registry.yarnpkg.com/crc/-/crc-3.4.4.tgz#9da1e980e3bd44fc5c93bf5ab3da3378d85e466b" - create-ecdh@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/create-ecdh/-/create-ecdh-4.0.0.tgz#888c723596cdf7612f6498233eebd7a35301737d" @@ -2775,7 +2771,7 @@ debug@*, debug@3.1.0, debug@^3.0.0, debug@^3.0.1, debug@^3.1.0: dependencies: ms "2.0.0" -debug@2, debug@2.6.9, debug@^2.2.0, debug@^2.3.3, debug@^2.6.3, debug@^2.6.8: +debug@2, debug@2.6.9, debug@^2.2.0, debug@^2.3.3, debug@^2.6.8: version "2.6.9" resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" dependencies: @@ -2928,6 +2924,10 @@ detect-libc@^1.0.2: version "1.0.3" resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-1.0.3.tgz#fa137c4bd698edf55cd5cd02ac559f91a4c4ba9b" +detect-newline@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/detect-newline/-/detect-newline-2.1.0.tgz#f41f1c10be4b00e87b5f13da680759f2c5bfd3e2" + dialog-polyfill@^0.4.9: version "0.4.9" resolved "https://registry.yarnpkg.com/dialog-polyfill/-/dialog-polyfill-0.4.9.tgz#c690b3727c3d82e0f947bd5b910b32af8a2ef57d" @@ -2970,7 +2970,7 @@ dns-prefetch-control@0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/dns-prefetch-control/-/dns-prefetch-control-0.1.0.tgz#60ddb457774e178f1f9415f0cabb0e85b0b300b2" -doctrine@^2.0.0, doctrine@^2.1.0: +doctrine@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-2.1.0.tgz#5cd01fc101621b42c4cd7f5d1a66243716d3f39d" dependencies: @@ -3111,10 +3111,6 @@ ejs@2.5.7, ejs@^2.5.7: version "2.5.7" resolved "https://registry.yarnpkg.com/ejs/-/ejs-2.5.7.tgz#cc872c168880ae3c7189762fd5ffc00896c9518a" -ejs@^2.5.8: - version "2.5.8" - resolved "https://registry.yarnpkg.com/ejs/-/ejs-2.5.8.tgz#2ab6954619f225e6193b7ac5f7c39c48fefe4380" - electron-to-chromium@^1.2.7: version "1.3.26" resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.26.tgz#996427294861a74d9c7c82b9260ea301e8c02d66" @@ -3253,6 +3249,16 @@ es-abstract@^1.4.3: is-callable "^1.1.3" is-regex "^1.0.4" +es-abstract@^1.5.1: + version "1.11.0" + resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.11.0.tgz#cce87d518f0496893b1a30cd8461835535480681" + dependencies: + es-to-primitive "^1.1.1" + function-bind "^1.1.1" + has "^1.0.1" + is-callable "^1.1.3" + is-regex "^1.0.4" + es-abstract@^1.6.1, es-abstract@^1.7.0: version "1.10.0" resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.10.0.tgz#1ecb36c197842a00d8ee4c2dfd8646bb97d60864" @@ -3410,49 +3416,6 @@ eslint-visitor-keys@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-1.0.0.tgz#3f3180fb2e291017716acb4c9d6d5b5c34a6a81d" -eslint@^4.19.1: - version "4.19.1" - resolved "https://registry.yarnpkg.com/eslint/-/eslint-4.19.1.tgz#32d1d653e1d90408854bfb296f076ec7e186a300" - dependencies: - ajv "^5.3.0" - babel-code-frame "^6.22.0" - chalk "^2.1.0" - concat-stream "^1.6.0" - cross-spawn "^5.1.0" - debug "^3.1.0" - doctrine "^2.1.0" - eslint-scope "^3.7.1" - eslint-visitor-keys "^1.0.0" - espree "^3.5.4" - esquery "^1.0.0" - esutils "^2.0.2" - file-entry-cache "^2.0.0" - functional-red-black-tree "^1.0.1" - glob "^7.1.2" - globals "^11.0.1" - ignore "^3.3.3" - imurmurhash "^0.1.4" - inquirer "^3.0.6" - is-resolvable "^1.0.0" - js-yaml "^3.9.1" - json-stable-stringify-without-jsonify "^1.0.1" - levn "^0.3.0" - lodash "^4.17.4" - minimatch "^3.0.2" - mkdirp "^0.5.1" - natural-compare "^1.4.0" - optionator "^0.8.2" - path-is-inside "^1.0.2" - pluralize "^7.0.0" - progress "^2.0.0" - regexpp "^1.0.1" - require-uncached "^1.0.3" - semver "^5.3.0" - strip-ansi "^4.0.0" - strip-json-comments "~2.0.1" - table "4.0.2" - text-table "~0.2.0" - eslint@^4.5.0: version "4.13.1" resolved "https://registry.yarnpkg.com/eslint/-/eslint-4.13.1.tgz#0055e0014464c7eb7878caf549ef2941992b444f" @@ -3502,13 +3465,6 @@ espree@^3.5.2: acorn "^5.2.1" acorn-jsx "^3.0.0" -espree@^3.5.4: - version "3.5.4" - resolved "https://registry.yarnpkg.com/espree/-/espree-3.5.4.tgz#b0f447187c8a8bed944b815a660bddf5deb5d1a7" - dependencies: - acorn "^5.5.0" - acorn-jsx "^3.0.0" - esprima@3.x.x, esprima@^3.1.3: version "3.1.3" resolved "https://registry.yarnpkg.com/esprima/-/esprima-3.1.3.tgz#fdca51cee6133895e3c88d535ce49dbff62a4633" @@ -3628,6 +3584,10 @@ exit-hook@^1.0.0: version "1.1.1" resolved "https://registry.yarnpkg.com/exit-hook/-/exit-hook-1.1.1.tgz#f05ca233b48c05d54fff07765df8507e95c02ff8" +exit@^0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/exit/-/exit-0.1.2.tgz#0632638f8d877cc82107d30a0fff1a17cba1cd0c" + expand-brackets@^0.1.4: version "0.1.5" resolved "https://registry.yarnpkg.com/expand-brackets/-/expand-brackets-0.1.5.tgz#df07284e342a807cd733ac5af72411e581d1177b" @@ -3660,17 +3620,6 @@ expect-ct@0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/expect-ct/-/expect-ct-0.1.0.tgz#52735678de18530890d8d7b95f0ac63640958094" -expect@^21.2.1: - version "21.2.1" - resolved "https://registry.yarnpkg.com/expect/-/expect-21.2.1.tgz#003ac2ac7005c3c29e73b38a272d4afadd6d1d7b" - dependencies: - ansi-styles "^3.2.0" - jest-diff "^21.2.1" - jest-get-type "^21.2.0" - jest-matcher-utils "^21.2.1" - jest-message-util "^21.2.1" - jest-regex-util "^21.2.0" - expect@^22.4.0: version "22.4.0" resolved "https://registry.yarnpkg.com/expect/-/expect-22.4.0.tgz#371edf1ae15b83b5bf5ec34b42f1584660a36c16" @@ -3682,6 +3631,17 @@ expect@^22.4.0: jest-message-util "^22.4.0" jest-regex-util "^22.1.0" +expect@^22.4.3: + version "22.4.3" + resolved "https://registry.yarnpkg.com/expect/-/expect-22.4.3.tgz#d5a29d0a0e1fb2153557caef2674d4547e914674" + dependencies: + ansi-styles "^3.2.0" + jest-diff "^22.4.3" + jest-get-type "^22.4.3" + jest-matcher-utils "^22.4.3" + jest-message-util "^22.4.3" + jest-regex-util "^22.4.3" + exports-loader@^0.6.4: version "0.6.4" resolved "https://registry.yarnpkg.com/exports-loader/-/exports-loader-0.6.4.tgz#d70fc6121975b35fc12830cf52754be2740fc886" @@ -3689,20 +3649,6 @@ exports-loader@^0.6.4: loader-utils "^1.0.2" source-map "0.5.x" -express-session@^1.15.6: - version "1.15.6" - resolved "https://registry.yarnpkg.com/express-session/-/express-session-1.15.6.tgz#47b4160c88f42ab70fe8a508e31cbff76757ab0a" - dependencies: - cookie "0.3.1" - cookie-signature "1.0.6" - crc "3.4.4" - debug "2.6.9" - depd "~1.1.1" - on-headers "~1.0.1" - parseurl "~1.3.2" - uid-safe "~2.1.5" - utils-merge "1.0.1" - express-static-gzip@^0.3.1: version "0.3.2" resolved "https://registry.yarnpkg.com/express-static-gzip/-/express-static-gzip-0.3.2.tgz#89ede84547a5717de3146315f62dc996c071a88d" @@ -3744,41 +3690,6 @@ express@4.16.0, express@^4.12.2: utils-merge "1.0.1" vary "~1.1.2" -express@^4.16.3: - version "4.16.3" - resolved "https://registry.yarnpkg.com/express/-/express-4.16.3.tgz#6af8a502350db3246ecc4becf6b5a34d22f7ed53" - dependencies: - accepts "~1.3.5" - array-flatten "1.1.1" - body-parser "1.18.2" - content-disposition "0.5.2" - content-type "~1.0.4" - cookie "0.3.1" - cookie-signature "1.0.6" - debug "2.6.9" - depd "~1.1.2" - encodeurl "~1.0.2" - escape-html "~1.0.3" - etag "~1.8.1" - finalhandler "1.1.1" - fresh "0.5.2" - merge-descriptors "1.0.1" - methods "~1.1.2" - on-finished "~2.3.0" - parseurl "~1.3.2" - path-to-regexp "0.1.7" - proxy-addr "~2.0.3" - qs "6.5.1" - range-parser "~1.2.0" - safe-buffer "5.1.1" - send "0.16.2" - serve-static "1.13.2" - setprototypeof "1.1.0" - statuses "~1.4.0" - type-is "~1.6.16" - utils-merge "1.0.1" - vary "~1.1.2" - extend-shallow@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/extend-shallow/-/extend-shallow-2.0.1.tgz#51af7d614ad9a9f610ea1bafbb989d6b1c56890f" @@ -3976,18 +3887,6 @@ finalhandler@1.1.0: statuses "~1.3.1" unpipe "~1.0.0" -finalhandler@1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-1.1.1.tgz#eebf4ed840079c83f4249038c9d703008301b105" - dependencies: - debug "2.6.9" - encodeurl "~1.0.2" - escape-html "~1.0.3" - on-finished "~2.3.0" - parseurl "~1.3.2" - statuses "~1.4.0" - unpipe "~1.0.0" - find-cache-dir@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/find-cache-dir/-/find-cache-dir-1.0.0.tgz#9288e3e9e3cc3748717d39eade17cf71fc30ee6f" @@ -4174,20 +4073,13 @@ fs.realpath@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" -fsevents@^1.0.0: +fsevents@^1.0.0, fsevents@^1.1.1: version "1.1.3" resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-1.1.3.tgz#11f82318f5fe7bb2cd22965a108e9306208216d8" dependencies: nan "^2.3.0" node-pre-gyp "^0.6.39" -fsevents@^1.1.1: - version "1.1.2" - resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-1.1.2.tgz#3282b713fb3ad80ede0e9fcf4611b5aa6fc033f4" - dependencies: - nan "^2.3.0" - node-pre-gyp "^0.6.36" - fstream-ignore@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/fstream-ignore/-/fstream-ignore-1.0.5.tgz#9c31dae34767018fe1d249b24dada67d092da105" @@ -4298,16 +4190,6 @@ getpass@^0.1.1: dependencies: assert-plus "^1.0.0" -gigya@2.0.33: - version "2.0.33" - resolved "https://registry.yarnpkg.com/gigya/-/gigya-2.0.33.tgz#c5845cd16fac8ebcfb5e727e1ebe9e51352482fb" - dependencies: - "@types/lodash" "^4.14.50" - "@types/node" "^7.0.0" - "@types/request" "^0.0.39" - lodash "^4.17.4" - request "^2.79.0" - git-up@^2.0.0: version "2.0.9" resolved "https://registry.yarnpkg.com/git-up/-/git-up-2.0.9.tgz#219bfd27c82daeead8495beb386dc18eae63636d" @@ -4974,12 +4856,6 @@ html-comment-regex@^1.1.0: version "1.1.1" resolved "https://registry.yarnpkg.com/html-comment-regex/-/html-comment-regex-1.1.1.tgz#668b93776eaae55ebde8f3ad464b307a4963625e" -html-encoding-sniffer@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/html-encoding-sniffer/-/html-encoding-sniffer-1.0.1.tgz#79bf7a785ea495fe66165e734153f363ff5437da" - dependencies: - whatwg-encoding "^1.0.1" - html-encoding-sniffer@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/html-encoding-sniffer/-/html-encoding-sniffer-1.0.2.tgz#e70d84b94da53aa375e11fe3a351be6642ca46f8" @@ -5142,6 +5018,13 @@ import-lazy@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/import-lazy/-/import-lazy-2.1.0.tgz#05698e3d45c88e8d7e9d92cb0584e77f096f3e43" +import-local@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/import-local/-/import-local-1.0.0.tgz#5e4ffdc03f4fe6c009c6729beb29631c2f8227bc" + dependencies: + pkg-dir "^2.0.0" + resolve-cwd "^2.0.0" + imports-loader@^0.7.1: version "0.7.1" resolved "https://registry.yarnpkg.com/imports-loader/-/imports-loader-0.7.1.tgz#f204b5f34702a32c1db7d48d89d5e867a0441253" @@ -5304,10 +5187,6 @@ ipaddr.js@1.5.2: version "1.5.2" resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.5.2.tgz#d4b505bde9946987ccf0fc58d9010ff9607e3fa0" -ipaddr.js@1.6.0: - version "1.6.0" - resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.6.0.tgz#e3fa357b773da619f26e95f049d055c72796f86b" - is-absolute-url@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/is-absolute-url/-/is-absolute-url-2.1.0.tgz#50530dfb84fcc9aa7dbe7852e83a37b93b9f2aa6" @@ -5689,33 +5568,50 @@ isstream@0.1.x, isstream@~0.1.2: version "0.1.2" resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a" -istanbul-api@^1.1.1: - version "1.1.14" - resolved "https://registry.yarnpkg.com/istanbul-api/-/istanbul-api-1.1.14.tgz#25bc5701f7c680c0ffff913de46e3619a3a6e680" +istanbul-api@^1.1.14: + version "1.3.1" + resolved "https://registry.yarnpkg.com/istanbul-api/-/istanbul-api-1.3.1.tgz#4c3b05d18c0016d1022e079b98dc82c40f488954" dependencies: async "^2.1.4" + compare-versions "^3.1.0" fileset "^2.0.2" - istanbul-lib-coverage "^1.1.1" - istanbul-lib-hook "^1.0.7" - istanbul-lib-instrument "^1.8.0" - istanbul-lib-report "^1.1.1" - istanbul-lib-source-maps "^1.2.1" - istanbul-reports "^1.1.2" + istanbul-lib-coverage "^1.2.0" + istanbul-lib-hook "^1.2.0" + istanbul-lib-instrument "^1.10.1" + istanbul-lib-report "^1.1.4" + istanbul-lib-source-maps "^1.2.4" + istanbul-reports "^1.3.0" js-yaml "^3.7.0" mkdirp "^0.5.1" once "^1.4.0" -istanbul-lib-coverage@^1.0.1, istanbul-lib-coverage@^1.1.1: +istanbul-lib-coverage@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/istanbul-lib-coverage/-/istanbul-lib-coverage-1.1.1.tgz#73bfb998885299415c93d38a3e9adf784a77a9da" -istanbul-lib-hook@^1.0.7: - version "1.0.7" - resolved "https://registry.yarnpkg.com/istanbul-lib-hook/-/istanbul-lib-hook-1.0.7.tgz#dd6607f03076578fe7d6f2a630cf143b49bacddc" +istanbul-lib-coverage@^1.1.2, istanbul-lib-coverage@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/istanbul-lib-coverage/-/istanbul-lib-coverage-1.2.0.tgz#f7d8f2e42b97e37fe796114cb0f9d68b5e3a4341" + +istanbul-lib-hook@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/istanbul-lib-hook/-/istanbul-lib-hook-1.2.0.tgz#ae556fd5a41a6e8efa0b1002b1e416dfeaf9816c" dependencies: append-transform "^0.4.0" -istanbul-lib-instrument@^1.4.2, istanbul-lib-instrument@^1.7.5, istanbul-lib-instrument@^1.8.0: +istanbul-lib-instrument@^1.10.1, istanbul-lib-instrument@^1.8.0: + version "1.10.1" + resolved "https://registry.yarnpkg.com/istanbul-lib-instrument/-/istanbul-lib-instrument-1.10.1.tgz#724b4b6caceba8692d3f1f9d0727e279c401af7b" + dependencies: + babel-generator "^6.18.0" + babel-template "^6.16.0" + babel-traverse "^6.18.0" + babel-types "^6.18.0" + babylon "^6.18.0" + istanbul-lib-coverage "^1.2.0" + semver "^5.3.0" + +istanbul-lib-instrument@^1.7.5: version "1.8.0" resolved "https://registry.yarnpkg.com/istanbul-lib-instrument/-/istanbul-lib-instrument-1.8.0.tgz#66f6c9421cc9ec4704f76f2db084ba9078a2b532" dependencies: @@ -5727,28 +5623,38 @@ istanbul-lib-instrument@^1.4.2, istanbul-lib-instrument@^1.7.5, istanbul-lib-ins istanbul-lib-coverage "^1.1.1" semver "^5.3.0" -istanbul-lib-report@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/istanbul-lib-report/-/istanbul-lib-report-1.1.1.tgz#f0e55f56655ffa34222080b7a0cd4760e1405fc9" +istanbul-lib-report@^1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/istanbul-lib-report/-/istanbul-lib-report-1.1.4.tgz#e886cdf505c4ebbd8e099e4396a90d0a28e2acb5" dependencies: - istanbul-lib-coverage "^1.1.1" + istanbul-lib-coverage "^1.2.0" mkdirp "^0.5.1" path-parse "^1.0.5" supports-color "^3.1.2" -istanbul-lib-source-maps@^1.1.0, istanbul-lib-source-maps@^1.2.1: - version "1.2.1" - resolved "https://registry.yarnpkg.com/istanbul-lib-source-maps/-/istanbul-lib-source-maps-1.2.1.tgz#a6fe1acba8ce08eebc638e572e294d267008aa0c" +istanbul-lib-source-maps@^1.2.1: + version "1.2.3" + resolved "https://registry.yarnpkg.com/istanbul-lib-source-maps/-/istanbul-lib-source-maps-1.2.3.tgz#20fb54b14e14b3fb6edb6aca3571fd2143db44e6" dependencies: - debug "^2.6.3" - istanbul-lib-coverage "^1.1.1" + debug "^3.1.0" + istanbul-lib-coverage "^1.1.2" mkdirp "^0.5.1" rimraf "^2.6.1" source-map "^0.5.3" -istanbul-reports@^1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/istanbul-reports/-/istanbul-reports-1.1.2.tgz#0fb2e3f6aa9922bd3ce45d05d8ab4d5e8e07bd4f" +istanbul-lib-source-maps@^1.2.4: + version "1.2.4" + resolved "https://registry.yarnpkg.com/istanbul-lib-source-maps/-/istanbul-lib-source-maps-1.2.4.tgz#cc7ccad61629f4efff8e2f78adb8c522c9976ec7" + dependencies: + debug "^3.1.0" + istanbul-lib-coverage "^1.2.0" + mkdirp "^0.5.1" + rimraf "^2.6.1" + source-map "^0.5.3" + +istanbul-reports@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/istanbul-reports/-/istanbul-reports-1.3.0.tgz#2f322e81e1d9520767597dca3c20a0cce89a3554" dependencies: handlebars "^4.0.3" @@ -5760,61 +5666,50 @@ iterall@^1.1.0, iterall@^1.1.1: version "1.1.3" resolved "https://registry.yarnpkg.com/iterall/-/iterall-1.1.3.tgz#1cbbff96204056dde6656e2ed2e2226d0e6d72c9" -jest-changed-files@^21.2.0: - version "21.2.0" - resolved "https://registry.yarnpkg.com/jest-changed-files/-/jest-changed-files-21.2.0.tgz#5dbeecad42f5d88b482334902ce1cba6d9798d29" +jest-changed-files@^22.4.3: + version "22.4.3" + resolved "https://registry.yarnpkg.com/jest-changed-files/-/jest-changed-files-22.4.3.tgz#8882181e022c38bd46a2e4d18d44d19d90a90fb2" dependencies: throat "^4.0.0" -jest-cli@^21.2.1: - version "21.2.1" - resolved "https://registry.yarnpkg.com/jest-cli/-/jest-cli-21.2.1.tgz#9c528b6629d651911138d228bdb033c157ec8c00" +jest-cli@^22.4.3: + version "22.4.3" + resolved "https://registry.yarnpkg.com/jest-cli/-/jest-cli-22.4.3.tgz#bf16c4a5fb7edc3fa5b9bb7819e34139e88a72c7" dependencies: ansi-escapes "^3.0.0" chalk "^2.0.1" + exit "^0.1.2" glob "^7.1.2" graceful-fs "^4.1.11" + import-local "^1.0.0" is-ci "^1.0.10" - istanbul-api "^1.1.1" - istanbul-lib-coverage "^1.0.1" - istanbul-lib-instrument "^1.4.2" - istanbul-lib-source-maps "^1.1.0" - jest-changed-files "^21.2.0" - jest-config "^21.2.1" - jest-environment-jsdom "^21.2.1" - jest-haste-map "^21.2.0" - jest-message-util "^21.2.1" - jest-regex-util "^21.2.0" - jest-resolve-dependencies "^21.2.0" - jest-runner "^21.2.1" - jest-runtime "^21.2.1" - jest-snapshot "^21.2.1" - jest-util "^21.2.1" + istanbul-api "^1.1.14" + istanbul-lib-coverage "^1.1.1" + istanbul-lib-instrument "^1.8.0" + istanbul-lib-source-maps "^1.2.1" + jest-changed-files "^22.4.3" + jest-config "^22.4.3" + jest-environment-jsdom "^22.4.3" + jest-get-type "^22.4.3" + jest-haste-map "^22.4.3" + jest-message-util "^22.4.3" + jest-regex-util "^22.4.3" + jest-resolve-dependencies "^22.4.3" + jest-runner "^22.4.3" + jest-runtime "^22.4.3" + jest-snapshot "^22.4.3" + jest-util "^22.4.3" + jest-validate "^22.4.3" + jest-worker "^22.4.3" micromatch "^2.3.11" - node-notifier "^5.0.2" - pify "^3.0.0" + node-notifier "^5.2.1" + realpath-native "^1.0.0" + rimraf "^2.5.4" slash "^1.0.0" string-length "^2.0.0" strip-ansi "^4.0.0" which "^1.2.12" - worker-farm "^1.3.1" - yargs "^9.0.0" - -jest-config@^21.2.1: - version "21.2.1" - resolved "https://registry.yarnpkg.com/jest-config/-/jest-config-21.2.1.tgz#c7586c79ead0bcc1f38c401e55f964f13bf2a480" - dependencies: - chalk "^2.0.1" - glob "^7.1.1" - jest-environment-jsdom "^21.2.1" - jest-environment-node "^21.2.1" - jest-get-type "^21.2.0" - jest-jasmine2 "^21.2.1" - jest-regex-util "^21.2.0" - jest-resolve "^21.2.0" - jest-util "^21.2.1" - jest-validate "^21.2.1" - pretty-format "^21.2.1" + yargs "^10.0.3" jest-config@^22.4.2: version "22.4.2" @@ -5832,14 +5727,21 @@ jest-config@^22.4.2: jest-validate "^22.4.2" pretty-format "^22.4.0" -jest-diff@^21.2.1: - version "21.2.1" - resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-21.2.1.tgz#46cccb6cab2d02ce98bc314011764bb95b065b4f" +jest-config@^22.4.3: + version "22.4.3" + resolved "https://registry.yarnpkg.com/jest-config/-/jest-config-22.4.3.tgz#0e9d57db267839ea31309119b41dc2fa31b76403" dependencies: chalk "^2.0.1" - diff "^3.2.0" - jest-get-type "^21.2.0" - pretty-format "^21.2.1" + glob "^7.1.1" + jest-environment-jsdom "^22.4.3" + jest-environment-node "^22.4.3" + jest-get-type "^22.4.3" + jest-jasmine2 "^22.4.3" + jest-regex-util "^22.4.3" + jest-resolve "^22.4.3" + jest-util "^22.4.3" + jest-validate "^22.4.3" + pretty-format "^22.4.3" jest-diff@^22.4.0: version "22.4.0" @@ -5850,17 +5752,24 @@ jest-diff@^22.4.0: jest-get-type "^22.1.0" pretty-format "^22.4.0" -jest-docblock@^21.0.0, jest-docblock@^21.2.0: +jest-diff@^22.4.3: + version "22.4.3" + resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-22.4.3.tgz#e18cc3feff0aeef159d02310f2686d4065378030" + dependencies: + chalk "^2.0.1" + diff "^3.2.0" + jest-get-type "^22.4.3" + pretty-format "^22.4.3" + +jest-docblock@^21.0.0: version "21.2.0" resolved "https://registry.yarnpkg.com/jest-docblock/-/jest-docblock-21.2.0.tgz#51529c3b30d5fd159da60c27ceedc195faf8d414" -jest-environment-jsdom@^21.2.1: - version "21.2.1" - resolved "https://registry.yarnpkg.com/jest-environment-jsdom/-/jest-environment-jsdom-21.2.1.tgz#38d9980c8259b2a608ec232deee6289a60d9d5b4" +jest-docblock@^22.4.3: + version "22.4.3" + resolved "https://registry.yarnpkg.com/jest-docblock/-/jest-docblock-22.4.3.tgz#50886f132b42b280c903c592373bb6e93bb68b19" dependencies: - jest-mock "^21.2.0" - jest-util "^21.2.1" - jsdom "^9.12.0" + detect-newline "^2.1.0" jest-environment-jsdom@^22.4.1: version "22.4.1" @@ -5870,12 +5779,13 @@ jest-environment-jsdom@^22.4.1: jest-util "^22.4.1" jsdom "^11.5.1" -jest-environment-node@^21.2.1: - version "21.2.1" - resolved "https://registry.yarnpkg.com/jest-environment-node/-/jest-environment-node-21.2.1.tgz#98c67df5663c7fbe20f6e792ac2272c740d3b8c8" +jest-environment-jsdom@^22.4.3: + version "22.4.3" + resolved "https://registry.yarnpkg.com/jest-environment-jsdom/-/jest-environment-jsdom-22.4.3.tgz#d67daa4155e33516aecdd35afd82d4abf0fa8a1e" dependencies: - jest-mock "^21.2.0" - jest-util "^21.2.1" + jest-mock "^22.4.3" + jest-util "^22.4.3" + jsdom "^11.5.1" jest-environment-node@^22.4.1: version "22.4.1" @@ -5884,37 +5794,32 @@ jest-environment-node@^22.4.1: jest-mock "^22.2.0" jest-util "^22.4.1" -jest-get-type@^21.2.0: - version "21.2.0" - resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-21.2.0.tgz#f6376ab9db4b60d81e39f30749c6c466f40d4a23" +jest-environment-node@^22.4.3: + version "22.4.3" + resolved "https://registry.yarnpkg.com/jest-environment-node/-/jest-environment-node-22.4.3.tgz#54c4eaa374c83dd52a9da8759be14ebe1d0b9129" + dependencies: + jest-mock "^22.4.3" + jest-util "^22.4.3" jest-get-type@^22.1.0: version "22.1.0" resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-22.1.0.tgz#4e90af298ed6181edc85d2da500dbd2753e0d5a9" -jest-haste-map@^21.2.0: - version "21.2.0" - resolved "https://registry.yarnpkg.com/jest-haste-map/-/jest-haste-map-21.2.0.tgz#1363f0a8bb4338f24f001806571eff7a4b2ff3d8" +jest-get-type@^22.4.3: + version "22.4.3" + resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-22.4.3.tgz#e3a8504d8479342dd4420236b322869f18900ce4" + +jest-haste-map@^22.4.3: + version "22.4.3" + resolved "https://registry.yarnpkg.com/jest-haste-map/-/jest-haste-map-22.4.3.tgz#25842fa2ba350200767ac27f658d58b9d5c2e20b" dependencies: fb-watchman "^2.0.0" graceful-fs "^4.1.11" - jest-docblock "^21.2.0" + jest-docblock "^22.4.3" + jest-serializer "^22.4.3" + jest-worker "^22.4.3" micromatch "^2.3.11" sane "^2.0.0" - worker-farm "^1.3.1" - -jest-jasmine2@^21.2.1: - version "21.2.1" - resolved "https://registry.yarnpkg.com/jest-jasmine2/-/jest-jasmine2-21.2.1.tgz#9cc6fc108accfa97efebce10c4308548a4ea7592" - dependencies: - chalk "^2.0.1" - expect "^21.2.1" - graceful-fs "^4.1.11" - jest-diff "^21.2.1" - jest-matcher-utils "^21.2.1" - jest-message-util "^21.2.1" - jest-snapshot "^21.2.1" - p-cancelable "^0.3.0" jest-jasmine2@^22.4.2: version "22.4.2" @@ -5932,6 +5837,22 @@ jest-jasmine2@^22.4.2: jest-util "^22.4.1" source-map-support "^0.5.0" +jest-jasmine2@^22.4.3: + version "22.4.3" + resolved "https://registry.yarnpkg.com/jest-jasmine2/-/jest-jasmine2-22.4.3.tgz#4daf64cd14c793da9db34a7c7b8dcfe52a745965" + dependencies: + chalk "^2.0.1" + co "^4.6.0" + expect "^22.4.3" + graceful-fs "^4.1.11" + is-generator-fn "^1.0.0" + jest-diff "^22.4.3" + jest-matcher-utils "^22.4.3" + jest-message-util "^22.4.3" + jest-snapshot "^22.4.3" + jest-util "^22.4.3" + source-map-support "^0.5.0" + jest-junit@^3.6.0: version "3.6.0" resolved "https://registry.yarnpkg.com/jest-junit/-/jest-junit-3.6.0.tgz#f4c4358e5286364a4324dc14abddd526aadfbd38" @@ -5940,13 +5861,11 @@ jest-junit@^3.6.0: strip-ansi "^4.0.0" xml "^1.0.1" -jest-matcher-utils@^21.2.1: - version "21.2.1" - resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-21.2.1.tgz#72c826eaba41a093ac2b4565f865eb8475de0f64" +jest-leak-detector@^22.4.3: + version "22.4.3" + resolved "https://registry.yarnpkg.com/jest-leak-detector/-/jest-leak-detector-22.4.3.tgz#2b7b263103afae8c52b6b91241a2de40117e5b35" dependencies: - chalk "^2.0.1" - jest-get-type "^21.2.0" - pretty-format "^21.2.1" + pretty-format "^22.4.3" jest-matcher-utils@^22.4.0: version "22.4.0" @@ -5956,13 +5875,13 @@ jest-matcher-utils@^22.4.0: jest-get-type "^22.1.0" pretty-format "^22.4.0" -jest-message-util@^21.2.1: - version "21.2.1" - resolved "https://registry.yarnpkg.com/jest-message-util/-/jest-message-util-21.2.1.tgz#bfe5d4692c84c827d1dcf41823795558f0a1acbe" +jest-matcher-utils@^22.4.3: + version "22.4.3" + resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-22.4.3.tgz#4632fe428ebc73ebc194d3c7b65d37b161f710ff" dependencies: chalk "^2.0.1" - micromatch "^2.3.11" - slash "^1.0.0" + jest-get-type "^22.4.3" + pretty-format "^22.4.3" jest-message-util@^22.4.0: version "22.4.0" @@ -5974,35 +5893,37 @@ jest-message-util@^22.4.0: slash "^1.0.0" stack-utils "^1.0.1" -jest-mock@^21.2.0: - version "21.2.0" - resolved "https://registry.yarnpkg.com/jest-mock/-/jest-mock-21.2.0.tgz#7eb0770e7317968165f61ea2a7281131534b3c0f" +jest-message-util@^22.4.3: + version "22.4.3" + resolved "https://registry.yarnpkg.com/jest-message-util/-/jest-message-util-22.4.3.tgz#cf3d38aafe4befddbfc455e57d65d5239e399eb7" + dependencies: + "@babel/code-frame" "^7.0.0-beta.35" + chalk "^2.0.1" + micromatch "^2.3.11" + slash "^1.0.0" + stack-utils "^1.0.1" jest-mock@^22.2.0: version "22.2.0" resolved "https://registry.yarnpkg.com/jest-mock/-/jest-mock-22.2.0.tgz#444b3f9488a7473adae09bc8a77294afded397a7" -jest-regex-util@^21.2.0: - version "21.2.0" - resolved "https://registry.yarnpkg.com/jest-regex-util/-/jest-regex-util-21.2.0.tgz#1b1e33e63143babc3e0f2e6c9b5ba1eb34b2d530" +jest-mock@^22.4.3: + version "22.4.3" + resolved "https://registry.yarnpkg.com/jest-mock/-/jest-mock-22.4.3.tgz#f63ba2f07a1511772cdc7979733397df770aabc7" jest-regex-util@^22.1.0: version "22.1.0" resolved "https://registry.yarnpkg.com/jest-regex-util/-/jest-regex-util-22.1.0.tgz#5daf2fe270074b6da63e5d85f1c9acc866768f53" -jest-resolve-dependencies@^21.2.0: - version "21.2.0" - resolved "https://registry.yarnpkg.com/jest-resolve-dependencies/-/jest-resolve-dependencies-21.2.0.tgz#9e231e371e1a736a1ad4e4b9a843bc72bfe03d09" - dependencies: - jest-regex-util "^21.2.0" +jest-regex-util@^22.4.3: + version "22.4.3" + resolved "https://registry.yarnpkg.com/jest-regex-util/-/jest-regex-util-22.4.3.tgz#a826eb191cdf22502198c5401a1fc04de9cef5af" -jest-resolve@^21.2.0: - version "21.2.0" - resolved "https://registry.yarnpkg.com/jest-resolve/-/jest-resolve-21.2.0.tgz#068913ad2ba6a20218e5fd32471f3874005de3a6" +jest-resolve-dependencies@^22.4.3: + version "22.4.3" + resolved "https://registry.yarnpkg.com/jest-resolve-dependencies/-/jest-resolve-dependencies-22.4.3.tgz#e2256a5a846732dc3969cb72f3c9ad7725a8195e" dependencies: - browser-resolve "^1.11.2" - chalk "^2.0.1" - is-builtin-module "^1.0.0" + jest-regex-util "^22.4.3" jest-resolve@^22.4.2: version "22.4.2" @@ -6011,53 +5932,57 @@ jest-resolve@^22.4.2: browser-resolve "^1.11.2" chalk "^2.0.1" -jest-runner@^21.2.1: - version "21.2.1" - resolved "https://registry.yarnpkg.com/jest-runner/-/jest-runner-21.2.1.tgz#194732e3e518bfb3d7cbfc0fd5871246c7e1a467" +jest-resolve@^22.4.3: + version "22.4.3" + resolved "https://registry.yarnpkg.com/jest-resolve/-/jest-resolve-22.4.3.tgz#0ce9d438c8438229aa9b916968ec6b05c1abb4ea" dependencies: - jest-config "^21.2.1" - jest-docblock "^21.2.0" - jest-haste-map "^21.2.0" - jest-jasmine2 "^21.2.1" - jest-message-util "^21.2.1" - jest-runtime "^21.2.1" - jest-util "^21.2.1" - pify "^3.0.0" - throat "^4.0.0" - worker-farm "^1.3.1" + browser-resolve "^1.11.2" + chalk "^2.0.1" -jest-runtime@^21.2.1: - version "21.2.1" - resolved "https://registry.yarnpkg.com/jest-runtime/-/jest-runtime-21.2.1.tgz#99dce15309c670442eee2ebe1ff53a3cbdbbb73e" +jest-runner@^22.4.3: + version "22.4.3" + resolved "https://registry.yarnpkg.com/jest-runner/-/jest-runner-22.4.3.tgz#298ddd6a22b992c64401b4667702b325e50610c3" + dependencies: + exit "^0.1.2" + jest-config "^22.4.3" + jest-docblock "^22.4.3" + jest-haste-map "^22.4.3" + jest-jasmine2 "^22.4.3" + jest-leak-detector "^22.4.3" + jest-message-util "^22.4.3" + jest-runtime "^22.4.3" + jest-util "^22.4.3" + jest-worker "^22.4.3" + throat "^4.0.0" + +jest-runtime@^22.4.3: + version "22.4.3" + resolved "https://registry.yarnpkg.com/jest-runtime/-/jest-runtime-22.4.3.tgz#b69926c34b851b920f666c93e86ba2912087e3d0" dependencies: babel-core "^6.0.0" - babel-jest "^21.2.0" - babel-plugin-istanbul "^4.0.0" + babel-jest "^22.4.3" + babel-plugin-istanbul "^4.1.5" chalk "^2.0.1" convert-source-map "^1.4.0" + exit "^0.1.2" graceful-fs "^4.1.11" - jest-config "^21.2.1" - jest-haste-map "^21.2.0" - jest-regex-util "^21.2.0" - jest-resolve "^21.2.0" - jest-util "^21.2.1" + jest-config "^22.4.3" + jest-haste-map "^22.4.3" + jest-regex-util "^22.4.3" + jest-resolve "^22.4.3" + jest-util "^22.4.3" + jest-validate "^22.4.3" json-stable-stringify "^1.0.1" micromatch "^2.3.11" + realpath-native "^1.0.0" slash "^1.0.0" strip-bom "3.0.0" write-file-atomic "^2.1.0" - yargs "^9.0.0" + yargs "^10.0.3" -jest-snapshot@^21.2.1: - version "21.2.1" - resolved "https://registry.yarnpkg.com/jest-snapshot/-/jest-snapshot-21.2.1.tgz#29e49f16202416e47343e757e5eff948c07fd7b0" - dependencies: - chalk "^2.0.1" - jest-diff "^21.2.1" - jest-matcher-utils "^21.2.1" - mkdirp "^0.5.1" - natural-compare "^1.4.0" - pretty-format "^21.2.1" +jest-serializer@^22.4.3: + version "22.4.3" + resolved "https://registry.yarnpkg.com/jest-serializer/-/jest-serializer-22.4.3.tgz#a679b81a7f111e4766235f4f0c46d230ee0f7436" jest-snapshot@^22.4.0: version "22.4.0" @@ -6070,17 +5995,16 @@ jest-snapshot@^22.4.0: natural-compare "^1.4.0" pretty-format "^22.4.0" -jest-util@^21.2.1: - version "21.2.1" - resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-21.2.1.tgz#a274b2f726b0897494d694a6c3d6a61ab819bb78" +jest-snapshot@^22.4.3: + version "22.4.3" + resolved "https://registry.yarnpkg.com/jest-snapshot/-/jest-snapshot-22.4.3.tgz#b5c9b42846ffb9faccb76b841315ba67887362d2" dependencies: - callsites "^2.0.0" chalk "^2.0.1" - graceful-fs "^4.1.11" - jest-message-util "^21.2.1" - jest-mock "^21.2.0" - jest-validate "^21.2.1" + jest-diff "^22.4.3" + jest-matcher-utils "^22.4.3" mkdirp "^0.5.1" + natural-compare "^1.4.0" + pretty-format "^22.4.3" jest-util@^22.4.1: version "22.4.1" @@ -6094,14 +6018,17 @@ jest-util@^22.4.1: mkdirp "^0.5.1" source-map "^0.6.0" -jest-validate@^21.2.1: - version "21.2.1" - resolved "https://registry.yarnpkg.com/jest-validate/-/jest-validate-21.2.1.tgz#cc0cbca653cd54937ba4f2a111796774530dd3c7" +jest-util@^22.4.3: + version "22.4.3" + resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-22.4.3.tgz#c70fec8eec487c37b10b0809dc064a7ecf6aafac" dependencies: + callsites "^2.0.0" chalk "^2.0.1" - jest-get-type "^21.2.0" - leven "^2.1.0" - pretty-format "^21.2.1" + graceful-fs "^4.1.11" + is-ci "^1.0.10" + jest-message-util "^22.4.3" + mkdirp "^0.5.1" + source-map "^0.6.0" jest-validate@^22.4.0, jest-validate@^22.4.2: version "22.4.2" @@ -6113,11 +6040,28 @@ jest-validate@^22.4.0, jest-validate@^22.4.2: leven "^2.1.0" pretty-format "^22.4.0" -jest@^21.2.1: - version "21.2.1" - resolved "https://registry.yarnpkg.com/jest/-/jest-21.2.1.tgz#c964e0b47383768a1438e3ccf3c3d470327604e1" +jest-validate@^22.4.3: + version "22.4.3" + resolved "https://registry.yarnpkg.com/jest-validate/-/jest-validate-22.4.3.tgz#0780954a5a7daaeec8d3c10834b9280865976b30" dependencies: - jest-cli "^21.2.1" + chalk "^2.0.1" + jest-config "^22.4.3" + jest-get-type "^22.4.3" + leven "^2.1.0" + pretty-format "^22.4.3" + +jest-worker@^22.4.3: + version "22.4.3" + resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-22.4.3.tgz#5c421417cba1c0abf64bf56bd5fb7968d79dd40b" + dependencies: + merge-stream "^1.0.1" + +jest@^22.4.3: + version "22.4.3" + resolved "https://registry.yarnpkg.com/jest/-/jest-22.4.3.tgz#2261f4b117dc46d9a4a1a673d2150958dee92f16" + dependencies: + import-local "^1.0.0" + jest-cli "^22.4.3" joi@^13.0.0: version "13.1.2" @@ -6170,13 +6114,20 @@ js-yaml@0.3.x: version "0.3.7" resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-0.3.7.tgz#d739d8ee86461e54b354d6a7d7d1f2ad9a167f62" -js-yaml@^3.4.3, js-yaml@^3.5.2, js-yaml@^3.6.1, js-yaml@^3.7.0, js-yaml@^3.9.0, js-yaml@^3.9.1: +js-yaml@^3.4.3, js-yaml@^3.5.2, js-yaml@^3.6.1, js-yaml@^3.9.0, js-yaml@^3.9.1: version "3.10.0" resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.10.0.tgz#2e78441646bd4682e963f22b6e92823c309c62dc" dependencies: argparse "^1.0.7" esprima "^4.0.0" +js-yaml@^3.7.0: + version "3.11.0" + resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.11.0.tgz#597c1a8bd57152f26d622ce4117851a51f5ebaef" + dependencies: + argparse "^1.0.7" + esprima "^4.0.0" + js-yaml@~3.7.0: version "3.7.0" resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.7.0.tgz#5c967ddd837a9bfdca5f2de84253abe8a1c03b80" @@ -6239,30 +6190,6 @@ jsdom@^7.0.2: whatwg-url-compat "~0.6.5" xml-name-validator ">= 2.0.1 < 3.0.0" -jsdom@^9.12.0: - version "9.12.0" - resolved "https://registry.yarnpkg.com/jsdom/-/jsdom-9.12.0.tgz#e8c546fffcb06c00d4833ca84410fed7f8a097d4" - dependencies: - abab "^1.0.3" - acorn "^4.0.4" - acorn-globals "^3.1.0" - array-equal "^1.0.0" - content-type-parser "^1.0.1" - cssom ">= 0.3.2 < 0.4.0" - cssstyle ">= 0.2.37 < 0.3.0" - escodegen "^1.6.1" - html-encoding-sniffer "^1.0.1" - nwmatcher ">= 1.3.9 < 2.0.0" - parse5 "^1.5.1" - request "^2.79.0" - sax "^1.2.1" - symbol-tree "^3.2.1" - tough-cookie "^2.3.2" - webidl-conversions "^4.0.0" - whatwg-encoding "^1.0.1" - whatwg-url "^4.3.0" - xml-name-validator "^2.0.1" - jsesc@^1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-1.3.0.tgz#46c3fec8c1892b12b0833db9bc7622176dbab34b" @@ -7160,14 +7087,16 @@ merge-descriptors@1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-1.0.1.tgz#b00aaa556dd8b44568150ec9d1b953f3f90cbb61" +merge-stream@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/merge-stream/-/merge-stream-1.0.1.tgz#4041202d508a342ba00174008df0c251b8c135e1" + dependencies: + readable-stream "^2.0.1" + merge@^1.1.3: version "1.2.0" resolved "https://registry.yarnpkg.com/merge/-/merge-1.2.0.tgz#7531e39d4949c281a66b8c5a6e0265e8b05894da" -mersenne-twister@^1.0.1: - version "1.1.0" - resolved "https://registry.yarnpkg.com/mersenne-twister/-/mersenne-twister-1.1.0.tgz#f916618ee43d7179efcf641bec4531eb9670978a" - metascraper-author@^3.9.2: version "3.9.2" resolved "https://registry.yarnpkg.com/metascraper-author/-/metascraper-author-3.9.2.tgz#ff2020ac428f59a875d655df3b0d4bea171fde19" @@ -7308,7 +7237,7 @@ miller-rabin@^4.0.0: version "1.30.0" resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.30.0.tgz#74c643da2dd9d6a45399963465b26d5ca7d71f01" -"mime-db@>= 1.33.0 < 2", mime-db@~1.33.0: +"mime-db@>= 1.33.0 < 2": version "1.33.0" resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.33.0.tgz#a3492050a5cb9b63450541e39d9788d2272783db" @@ -7318,12 +7247,6 @@ mime-types@^2.1.10, mime-types@^2.1.12, mime-types@~2.1.15, mime-types@~2.1.16, dependencies: mime-db "~1.30.0" -mime-types@~2.1.18: - version "2.1.18" - resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.18.tgz#6f323f60a83d11146f831ff11fd66e2fe5503bb8" - dependencies: - mime-db "~1.33.0" - mime@1.4.1, mime@^1.3.4, mime@^1.4.1: version "1.4.1" resolved "https://registry.yarnpkg.com/mime/-/mime-1.4.1.tgz#121f9ebc49e3766f311a76e1fa1c8003c4b03aa6" @@ -7456,10 +7379,6 @@ moment@^2.10.3: version "2.19.1" resolved "https://registry.yarnpkg.com/moment/-/moment-2.19.1.tgz#56da1a2d1cbf01d38b7e1afc31c10bcfa1929167" -moment@^2.15.2: - version "2.22.0" - resolved "https://registry.yarnpkg.com/moment/-/moment-2.22.0.tgz#7921ade01017dd45186e7fee5f424f0b8663a730" - mongodb-core@2.1.17: version "2.1.17" resolved "https://registry.yarnpkg.com/mongodb-core/-/mongodb-core-2.1.17.tgz#a418b337a14a14990fb510b923dee6a813173df8" @@ -7763,16 +7682,16 @@ node-libs-browser@^2.0.0: util "^0.10.3" vm-browserify "0.0.4" -node-notifier@^5.0.2: - version "5.1.2" - resolved "https://registry.yarnpkg.com/node-notifier/-/node-notifier-5.1.2.tgz#2fa9e12605fa10009d44549d6fcd8a63dde0e4ff" +node-notifier@^5.2.1: + version "5.2.1" + resolved "https://registry.yarnpkg.com/node-notifier/-/node-notifier-5.2.1.tgz#fa313dd08f5517db0e2502e5758d664ac69f9dea" dependencies: growly "^1.3.0" - semver "^5.3.0" - shellwords "^0.1.0" - which "^1.2.12" + semver "^5.4.1" + shellwords "^0.1.1" + which "^1.3.0" -node-pre-gyp@^0.6.36, node-pre-gyp@^0.6.39: +node-pre-gyp@^0.6.39: version "0.6.39" resolved "https://registry.yarnpkg.com/node-pre-gyp/-/node-pre-gyp-0.6.39.tgz#c00e96860b23c0e1420ac7befc5044e1d78d8649" dependencies: @@ -8046,7 +7965,7 @@ nunjucks@^3.1.2: optionalDependencies: chokidar "^1.6.0" -"nwmatcher@>= 1.3.7 < 2.0.0", "nwmatcher@>= 1.3.9 < 2.0.0", nwmatcher@^1.4.3: +"nwmatcher@>= 1.3.7 < 2.0.0", nwmatcher@^1.4.3: version "1.4.3" resolved "https://registry.yarnpkg.com/nwmatcher/-/nwmatcher-1.4.3.tgz#64348e3b3d80f035b40ac11563d278f8b72db89c" @@ -8101,6 +8020,13 @@ object.entries@^1.0.4: function-bind "^1.1.0" has "^1.0.1" +object.getownpropertydescriptors@^2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.0.3.tgz#8758c846f5b407adab0f236e0986f14b051caa16" + dependencies: + define-properties "^1.1.2" + es-abstract "^1.5.1" + object.omit@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/object.omit/-/object.omit-2.0.1.tgz#1a9c744829f39dbb858c76ca3579ae2a54ebd1fa" @@ -8224,10 +8150,6 @@ output-file-sync@^1.1.2: mkdirp "^0.5.1" object-assign "^4.1.0" -p-cancelable@^0.3.0: - version "0.3.0" - resolved "https://registry.yarnpkg.com/p-cancelable/-/p-cancelable-0.3.0.tgz#b9e123800bcebb7ac13a479be195b507b98d30fa" - p-finally@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/p-finally/-/p-finally-1.0.0.tgz#3fbcfb15b899a44123b34b6dcc18b724336a2cae" @@ -8417,15 +8339,6 @@ passport-oauth2@1.x.x, passport-oauth2@^1.1.2: uid2 "0.0.x" utils-merge "1.x.x" -passport-openidconnect@^0.0.2: - version "0.0.2" - resolved "https://registry.yarnpkg.com/passport-openidconnect/-/passport-openidconnect-0.0.2.tgz#e488f8bdb386c9a9fd39c91d5ab8c880156e8153" - dependencies: - oauth "0.9.x" - passport-strategy "1.x.x" - request "^2.75.0" - webfinger "0.4.x" - passport-strategy@1.x.x, passport-strategy@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/passport-strategy/-/passport-strategy-1.0.0.tgz#b5539aa8fc225a3d1ad179476ddf236b440f52e4" @@ -9073,16 +8986,16 @@ prettier@^1.10.2: version "1.10.2" resolved "https://registry.yarnpkg.com/prettier/-/prettier-1.10.2.tgz#1af8356d1842276a99a5b5529c82dd9e9ad3cc93" -pretty-format@^21.2.1: - version "21.2.1" - resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-21.2.1.tgz#ae5407f3cf21066cd011aa1ba5fce7b6a2eddb36" +pretty-format@^22.4.0: + version "22.4.0" + resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-22.4.0.tgz#237b1f7e1c50ed03bc65c03ccc29d7c8bb7beb94" dependencies: ansi-regex "^3.0.0" ansi-styles "^3.2.0" -pretty-format@^22.4.0: - version "22.4.0" - resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-22.4.0.tgz#237b1f7e1c50ed03bc65c03ccc29d7c8bb7beb94" +pretty-format@^22.4.3: + version "22.4.3" + resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-22.4.3.tgz#f873d780839a9c02e9664c8a082e9ee79eaac16f" dependencies: ansi-regex "^3.0.0" ansi-styles "^3.2.0" @@ -9150,13 +9063,6 @@ proxy-addr@~2.0.2: forwarded "~0.1.2" ipaddr.js "1.5.2" -proxy-addr@~2.0.3: - version "2.0.3" - resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-2.0.3.tgz#355f262505a621646b3130a728eb647e22055341" - dependencies: - forwarded "~0.1.2" - ipaddr.js "1.6.0" - proxy-agent@2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/proxy-agent/-/proxy-agent-2.0.0.tgz#57eb5347aa805d74ec681cb25649dba39c933499" @@ -9407,10 +9313,6 @@ randexp@^0.4.2: discontinuous-range "1.0.0" ret "~0.1.10" -random-bytes@~1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/random-bytes/-/random-bytes-1.0.0.tgz#4f68a1dc0ae58bd3fb95848c30324db75d64360b" - randomatic@^1.1.3: version "1.1.7" resolved "https://registry.yarnpkg.com/randomatic/-/randomatic-1.1.7.tgz#c7abe9cc8b87c0baa876b19fde83fd464797e38c" @@ -9764,6 +9666,12 @@ readdirp@^2.0.0: readable-stream "^2.0.2" set-immediate-shim "^1.0.1" +realpath-native@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/realpath-native/-/realpath-native-1.0.0.tgz#7885721a83b43bd5327609f0ddecb2482305fdf0" + dependencies: + util.promisify "^1.0.0" + rechoir@^0.6.2: version "0.6.2" resolved "https://registry.yarnpkg.com/rechoir/-/rechoir-0.6.2.tgz#85204b54dba82d5742e28c96756ef43af50e3384" @@ -9881,10 +9789,6 @@ regexp-clone@0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/regexp-clone/-/regexp-clone-0.0.1.tgz#a7c2e09891fdbf38fbb10d376fb73003e68ac589" -regexpp@^1.0.1: - version "1.1.0" - resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-1.1.0.tgz#0e3516dd0b7904f413d2d4193dce4618c3a689ab" - regexpu-core@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/regexpu-core/-/regexpu-core-1.0.0.tgz#86a763f58ee4d7c2f6b102e4764050de7ed90c6b" @@ -9956,7 +9860,7 @@ request-promise-native@^1.0.5: stealthy-require "^1.1.0" tough-cookie ">=2.3.3" -request@2, request@^2.55.0, request@^2.74.0, request@^2.79.0, request@^2.81.0, request@^2.83.0: +request@2, request@^2.55.0, request@^2.74.0, request@^2.81.0, request@^2.83.0: version "2.83.0" resolved "https://registry.yarnpkg.com/request/-/request-2.83.0.tgz#ca0b65da02ed62935887808e6f510381034e3356" dependencies: @@ -10035,33 +9939,6 @@ request@2.81.0: tunnel-agent "^0.6.0" uuid "^3.0.0" -request@^2.75.0: - version "2.85.0" - resolved "https://registry.yarnpkg.com/request/-/request-2.85.0.tgz#5a03615a47c61420b3eb99b7dba204f83603e1fa" - dependencies: - aws-sign2 "~0.7.0" - aws4 "^1.6.0" - caseless "~0.12.0" - combined-stream "~1.0.5" - extend "~3.0.1" - forever-agent "~0.6.1" - form-data "~2.3.1" - har-validator "~5.0.3" - hawk "~6.0.2" - http-signature "~1.2.0" - is-typedarray "~1.0.0" - isstream "~0.1.2" - json-stringify-safe "~5.0.1" - mime-types "~2.1.17" - oauth-sign "~0.8.2" - performance-now "^2.1.0" - qs "~6.5.1" - safe-buffer "^5.1.1" - stringstream "~0.0.5" - tough-cookie "~2.3.3" - tunnel-agent "^0.6.0" - uuid "^3.1.0" - require-directory@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" @@ -10092,6 +9969,12 @@ require_optional@~1.0.0: resolve-from "^2.0.0" semver "^5.1.0" +resolve-cwd@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/resolve-cwd/-/resolve-cwd-2.0.0.tgz#00a9f7387556e27038eae232caa372a6a59b665a" + dependencies: + resolve-from "^3.0.0" + resolve-from@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-1.0.1.tgz#26cbfe935d1aeeeabb29bc3fe5aeb01e93d44226" @@ -10100,6 +9983,10 @@ resolve-from@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-2.0.0.tgz#9480ab20e94ffa1d9e80a804c7ea147611966b57" +resolve-from@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-3.0.0.tgz#b22c7af7d9d6881bc8b6e653335eebcb0a188748" + resolve-from@~4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6" @@ -10235,13 +10122,13 @@ samsam@1.x, samsam@^1.1.3: resolved "https://registry.yarnpkg.com/samsam/-/samsam-1.3.0.tgz#8d1d9350e25622da30de3e44ba692b5221ab7c50" sane@^2.0.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/sane/-/sane-2.2.0.tgz#d6d2e2fcab00e3d283c93b912b7c3a20846f1d56" + version "2.5.0" + resolved "https://registry.yarnpkg.com/sane/-/sane-2.5.0.tgz#6359cd676f5efd9988b264d8ce3b827dd6b27bec" dependencies: - anymatch "^1.3.0" + anymatch "^2.0.0" exec-sh "^0.2.0" fb-watchman "^2.0.0" - minimatch "^3.0.2" + micromatch "^3.1.4" minimist "^1.1.1" walker "~1.0.5" watch "~0.18.0" @@ -10276,7 +10163,7 @@ sax@0.5.x: version "0.5.8" resolved "https://registry.yarnpkg.com/sax/-/sax-0.5.8.tgz#d472db228eb331c2506b0e8c15524adb939d12c1" -sax@>=0.1.1, sax@^1.1.4, sax@^1.2.1, sax@^1.2.4, sax@~1.2.1: +sax@^1.1.4, sax@^1.2.4, sax@~1.2.1: version "1.2.4" resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9" @@ -10415,7 +10302,7 @@ serve-static@1.13.0: parseurl "~1.3.2" send "0.16.0" -serve-static@1.13.2, serve-static@^1.10.0: +serve-static@^1.10.0: version "1.13.2" resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-1.13.2.tgz#095e8472fd5b46237db50ce486a43f4b86c6cec1" dependencies: @@ -10511,7 +10398,7 @@ shelljs@^0.7.0: interpret "^1.0.0" rechoir "^0.6.2" -shellwords@^0.1.0: +shellwords@^0.1.1: version "0.1.1" resolved "https://registry.yarnpkg.com/shellwords/-/shellwords-0.1.1.tgz#d6b9181c1a48d397324c84871efbcfc73fc0654b" @@ -10832,10 +10719,6 @@ stealthy-require@^1.1.0: version "1.1.1" resolved "https://registry.yarnpkg.com/stealthy-require/-/stealthy-require-1.1.1.tgz#35b09875b4ff49f26a777e509b3090a3226bf24b" -step@0.0.x: - version "0.0.6" - resolved "https://registry.yarnpkg.com/step/-/step-0.0.6.tgz#143e7849a5d7d3f4a088fe29af94915216eeede2" - stream-browserify@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/stream-browserify/-/stream-browserify-2.0.1.tgz#66266ee5f9bdb9940a4e4514cafb43bb71e5c9db" @@ -11129,11 +11012,11 @@ symbol-observable@^1.0.2, symbol-observable@^1.0.3, symbol-observable@^1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/symbol-observable/-/symbol-observable-1.0.4.tgz#29bf615d4aa7121bdd898b22d4b3f9bc4e2aa03d" -"symbol-tree@>= 3.1.0 < 4.0.0", symbol-tree@^3.2.1, symbol-tree@^3.2.2: +"symbol-tree@>= 3.1.0 < 4.0.0", symbol-tree@^3.2.2: version "3.2.2" resolved "https://registry.yarnpkg.com/symbol-tree/-/symbol-tree-3.2.2.tgz#ae27db38f660a7ae2e1c3b7d1bc290819b8519e6" -table@4.0.2, table@^4.0.1: +table@^4.0.1: version "4.0.2" resolved "https://registry.yarnpkg.com/table/-/table-4.0.2.tgz#a33447375391e766ad34d3486e6e2aedc84d2e36" dependencies: @@ -11213,6 +11096,16 @@ test-exclude@^4.1.1: read-pkg-up "^1.0.1" require-main-filename "^1.0.1" +test-exclude@^4.2.1: + version "4.2.1" + resolved "https://registry.yarnpkg.com/test-exclude/-/test-exclude-4.2.1.tgz#dfa222f03480bca69207ca728b37d74b45f724fa" + dependencies: + arrify "^1.0.1" + micromatch "^3.1.8" + object-assign "^4.1.0" + read-pkg-up "^1.0.1" + require-main-filename "^1.0.1" + text-encoding@0.6.4, text-encoding@^0.6.4: version "0.6.4" resolved "https://registry.yarnpkg.com/text-encoding/-/text-encoding-0.6.4.tgz#e399a982257a276dae428bb92845cb71bdc26d19" @@ -11380,7 +11273,7 @@ touch@^3.1.0: dependencies: nopt "~1.0.10" -tough-cookie@>=2.3.3, tough-cookie@^2.2.0, tough-cookie@^2.3.2, tough-cookie@^2.3.3, tough-cookie@~2.3.0, tough-cookie@~2.3.3: +tough-cookie@>=2.3.3, tough-cookie@^2.2.0, tough-cookie@^2.3.3, tough-cookie@~2.3.0, tough-cookie@~2.3.3: version "2.3.3" resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.3.3.tgz#0b618a5565b6dea90bf3425d04d55edc475a7561" dependencies: @@ -11392,7 +11285,7 @@ tr46@^1.0.0: dependencies: punycode "^2.1.0" -tr46@~0.0.1, tr46@~0.0.3: +tr46@~0.0.1: version "0.0.3" resolved "https://registry.yarnpkg.com/tr46/-/tr46-0.0.3.tgz#8184fd347dac9cdc185992f3a6622e14b9d9ab6a" @@ -11453,13 +11346,6 @@ type-is@~1.6.15: media-typer "0.3.0" mime-types "~2.1.15" -type-is@~1.6.16: - version "1.6.16" - resolved "https://registry.yarnpkg.com/type-is/-/type-is-1.6.16.tgz#f89ce341541c672b25ee7ae3c73dee3b2be50194" - dependencies: - media-typer "0.3.0" - mime-types "~2.1.18" - typedarray@^0.0.6: version "0.0.6" resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" @@ -11534,12 +11420,6 @@ uid-number@^0.0.6: version "0.0.6" resolved "https://registry.yarnpkg.com/uid-number/-/uid-number-0.0.6.tgz#0ea10e8035e8eb5b8e4449f06da1c730663baa81" -uid-safe@~2.1.5: - version "2.1.5" - resolved "https://registry.yarnpkg.com/uid-safe/-/uid-safe-2.1.5.tgz#2b3d5c7240e8fc2e58f8aa269e5ee49c0857bd3a" - dependencies: - random-bytes "~1.0.0" - uid2@0.0.x: version "0.0.3" resolved "https://registry.yarnpkg.com/uid2/-/uid2-0.0.3.tgz#483126e11774df2f71b8b639dcd799c376162b82" @@ -11721,6 +11601,13 @@ util-deprecate@~1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" +util.promisify@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/util.promisify/-/util.promisify-1.0.0.tgz#440f7165a459c9a16dc145eb8e72f35687097030" + dependencies: + define-properties "^1.1.2" + object.getownpropertydescriptors "^2.0.3" + util@0.10.3, "util@>=0.10.3 <1", util@^0.10.3: version "0.10.3" resolved "https://registry.yarnpkg.com/util/-/util-0.10.3.tgz#7afb1afe50805246489e3db7fe0ed379336ac0f9" @@ -11830,22 +11717,11 @@ watchpack@^1.4.0: chokidar "^1.7.0" graceful-fs "^4.1.2" -webfinger@0.4.x: - version "0.4.2" - resolved "https://registry.yarnpkg.com/webfinger/-/webfinger-0.4.2.tgz#3477a6d97799461896039fcffc650b73468ee76d" - dependencies: - step "0.0.x" - xml2js "0.1.x" - webidl-conversions@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-2.0.1.tgz#3bf8258f7d318c7443c36f2e169402a1a6703506" -webidl-conversions@^3.0.0: - version "3.0.1" - resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-3.0.1.tgz#24534275e2a7bc6be7bc86611cc16ae0a5654871" - -webidl-conversions@^4.0.0, webidl-conversions@^4.0.1, webidl-conversions@^4.0.2: +webidl-conversions@^4.0.1, webidl-conversions@^4.0.2: version "4.0.2" resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-4.0.2.tgz#a855980b1f0b6b359ba1d5d9fb39ae941faa63ad" @@ -11912,13 +11788,6 @@ whatwg-url-compat@~0.6.5: dependencies: tr46 "~0.0.1" -whatwg-url@^4.3.0: - version "4.8.0" - resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-4.8.0.tgz#d2981aa9148c1e00a41c5a6131166ab4683bbcc0" - dependencies: - tr46 "~0.0.3" - webidl-conversions "^3.0.0" - whatwg-url@^6.4.0: version "6.4.0" resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-6.4.0.tgz#08fdf2b9e872783a7a1f6216260a1d66cc722e08" @@ -11939,7 +11808,7 @@ which-module@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/which-module/-/which-module-2.0.0.tgz#d9ef07dce77b9902b8a3a8fa4b31c3e3f7e6e87a" -which@1, which@^1.2.10, which@^1.2.12, which@^1.2.9: +which@1, which@^1.2.10, which@^1.2.12, which@^1.2.9, which@^1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/which/-/which-1.3.0.tgz#ff04bdfc010ee547d780bec38e1ac1c2777d253a" dependencies: @@ -12065,7 +11934,7 @@ xdg-basedir@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/xdg-basedir/-/xdg-basedir-3.0.0.tgz#496b2cc109eca8dbacfe2dc72b603c17c5870ad4" -"xml-name-validator@>= 2.0.1 < 3.0.0", xml-name-validator@^2.0.1: +"xml-name-validator@>= 2.0.1 < 3.0.0": version "2.0.1" resolved "https://registry.yarnpkg.com/xml-name-validator/-/xml-name-validator-2.0.1.tgz#4d8b8f1eccd3419aa362061becef515e1e559635" @@ -12073,12 +11942,6 @@ xml-name-validator@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/xml-name-validator/-/xml-name-validator-3.0.0.tgz#6ae73e06de4d8c6e47f9fb181f78d648ad457c6a" -xml2js@0.1.x: - version "0.1.14" - resolved "https://registry.yarnpkg.com/xml2js/-/xml2js-0.1.14.tgz#5274e67f5a64c5f92974cd85139e0332adc6b90c" - dependencies: - sax ">=0.1.1" - xml@^1.0.0, xml@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/xml/-/xml-1.0.1.tgz#78ba72020029c5bc87b8a81a3cfcd74b4a2fc1e5" @@ -12154,6 +12017,29 @@ yargs-parser@^7.0.0: dependencies: camelcase "^4.1.0" +yargs-parser@^8.1.0: + version "8.1.0" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-8.1.0.tgz#f1376a33b6629a5d063782944da732631e966950" + dependencies: + camelcase "^4.1.0" + +yargs@^10.0.3: + version "10.1.2" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-10.1.2.tgz#454d074c2b16a51a43e2fb7807e4f9de69ccb5c5" + dependencies: + cliui "^4.0.0" + decamelize "^1.1.1" + find-up "^2.1.0" + get-caller-file "^1.0.1" + os-locale "^2.0.0" + require-directory "^2.1.1" + require-main-filename "^1.0.1" + set-blocking "^2.0.0" + string-width "^2.0.0" + which-module "^2.0.0" + y18n "^3.2.1" + yargs-parser "^8.1.0" + yargs@^3.19.0, yargs@^3.32.0: version "3.32.0" resolved "https://registry.yarnpkg.com/yargs/-/yargs-3.32.0.tgz#03088e9ebf9e756b69751611d2a5ef591482c995" @@ -12240,24 +12126,6 @@ yargs@^8.0.2: y18n "^3.2.1" yargs-parser "^7.0.0" -yargs@^9.0.0: - version "9.0.1" - resolved "https://registry.yarnpkg.com/yargs/-/yargs-9.0.1.tgz#52acc23feecac34042078ee78c0c007f5085db4c" - dependencies: - camelcase "^4.1.0" - cliui "^3.2.0" - decamelize "^1.1.1" - get-caller-file "^1.0.1" - os-locale "^2.0.0" - read-pkg-up "^2.0.0" - require-directory "^2.1.1" - require-main-filename "^1.0.1" - set-blocking "^2.0.0" - string-width "^2.0.0" - which-module "^2.0.0" - y18n "^3.2.1" - yargs-parser "^7.0.0" - yargs@~3.10.0: version "3.10.0" resolved "https://registry.yarnpkg.com/yargs/-/yargs-3.10.0.tgz#f7ee7bd857dd7c1d2d38c0e74efbd681d1431fd1" From 322740d1a94aaf7fcdfd8efad29ab84542a497ba Mon Sep 17 00:00:00 2001 From: Wyatt Johnson Date: Wed, 11 Apr 2018 19:29:54 -0600 Subject: [PATCH 68/84] small fixes --- package.json | 3 ++- services/mongoose.js | 4 ---- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index 68fb0519e..f5c1764c9 100644 --- a/package.json +++ b/package.json @@ -21,7 +21,8 @@ "test": "npm-run-all test:jest test:mocha", "test:jest": "TEST_MODE=unit NODE_ENV=test jest --runInBand", "test:client": "TEST_MODE=unit NODE_ENV=test jest --projects client", - "test:mocha": "TEST_MODE=unit NODE_ENV=test mocha -R ${MOCHA_REPORTER:-spec}", + "test:server": "npm-run-all test:server:jest test:server:mocha", + "test:server:mocha": "TEST_MODE=unit NODE_ENV=test mocha -R ${MOCHA_REPORTER:-spec}", "test:server:jest": "TEST_MODE=unit NODE_ENV=test jest --runInBand --projects .", "e2e": "./scripts/e2e.js", "e2e:ci": "./scripts/e2e-ci.sh", diff --git a/services/mongoose.js b/services/mongoose.js index 66304ee07..4e242cf47 100644 --- a/services/mongoose.js +++ b/services/mongoose.js @@ -42,10 +42,6 @@ if (WEBPACK) { logger.debug('mongodb disconnected') ); - setTimeout(() => { - mongoose.disconnect(); - }, 5000); - // Connect to the Mongo instance. mongoose .connect(MONGO_URL, { From 68473e5a29cd88c4efd097d49bfc3a1f09a4f696 Mon Sep 17 00:00:00 2001 From: Wyatt Johnson Date: Thu, 12 Apr 2018 08:36:45 -0600 Subject: [PATCH 69/84] added beforeTest to wait for mongoose to be ready --- test/setupJest.js | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/test/setupJest.js b/test/setupJest.js index 9ff2d8af4..6ed78a9c5 100644 --- a/test/setupJest.js +++ b/test/setupJest.js @@ -1,5 +1,15 @@ const mongoose = require('../services/mongoose'); +beforeAll(function(done) { + mongoose.connection.on('open', function(err) { + if (err) { + return done(err); + } + + return done(); + }); +}, 30000); + beforeEach(async () => { await Promise.all( Object.keys(mongoose.connection.collections).map(collection => { From 0206804bf459007ba17a1c46a5241285d0d3dbdb Mon Sep 17 00:00:00 2001 From: okbel Date: Thu, 12 Apr 2018 12:18:22 -0300 Subject: [PATCH 70/84] autocapitalize off for the username field --- plugins/talk-plugin-auth/client/login/components/SignUp.js | 1 + 1 file changed, 1 insertion(+) diff --git a/plugins/talk-plugin-auth/client/login/components/SignUp.js b/plugins/talk-plugin-auth/client/login/components/SignUp.js index 2a6de0c24..83f81f81d 100644 --- a/plugins/talk-plugin-auth/client/login/components/SignUp.js +++ b/plugins/talk-plugin-auth/client/login/components/SignUp.js @@ -87,6 +87,7 @@ class SignUp extends React.Component { errorMsg={usernameError} onChange={this.handleUsernameChange} autocomplete="off" + autocapitalize="none" /> Date: Thu, 12 Apr 2018 09:37:17 -0600 Subject: [PATCH 71/84] adjusted index creation and managemen --- .circleci/config.yml | 17 + bin/cli | 1 + bin/cli-db | 53 +++ models/action.js | 53 +-- models/asset.js | 99 +---- models/comment.js | 235 +----------- models/migration.js | 10 +- models/schema/action.js | 51 +++ models/schema/asset.js | 97 +++++ models/schema/comment.js | 235 ++++++++++++ models/schema/index.js | 21 ++ models/schema/migration.js | 8 + models/schema/setting.js | 142 ++++++++ models/schema/user.js | 375 +++++++++++++++++++ models/setting.js | 147 +------- models/user.js | 378 +------------------- plugin-api/beta/server/getReactionConfig.js | 31 +- 17 files changed, 1027 insertions(+), 926 deletions(-) create mode 100755 bin/cli-db create mode 100644 models/schema/action.js create mode 100644 models/schema/asset.js create mode 100644 models/schema/comment.js create mode 100644 models/schema/index.js create mode 100644 models/schema/migration.js create mode 100644 models/schema/setting.js create mode 100644 models/schema/user.js diff --git a/.circleci/config.yml b/.circleci/config.yml index 2fe14ad6e..9272a0572 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -1,11 +1,26 @@ + +# job_environment will setup the environment for any job being executed. +job_environment: &job_environment + DISABLE_CREATE_MONGO_INDEXES: TRUE + # job_defaults applies all the defaults for each job. job_defaults: &job_defaults working_directory: ~/coralproject/talk docker: - image: circleci/node:8 + environment: + <<: *job_environment + +# create_indexes will create the mongo indexes and wait until they have been +# built. +create_indexes: &create_indexes + run: + name: Create the database indexes and wait until they are built + command: ./bin/cli db createIndexes # integration_environment is the environment that configures the tests. integration_environment: &integration_environment + <<: *job_environment NODE_ENV: test CIRCLE_TEST_REPORTS: /tmp/circleci-test-results E2E_MAX_RETRIES: 3 @@ -25,6 +40,7 @@ integration_job: &integration_job - checkout - attach_workspace: at: ~/coralproject/talk + - <<: *create_indexes - run: name: Setup the database with defaults command: ./bin/cli setup --defaults @@ -117,6 +133,7 @@ jobs: environment: JEST_JUNIT_OUTPUT: /tmp/circleci-test-results/jest/test-results.xml JEST_REPORTER: jest-junit + - <<: *create_indexes - run: name: Run the server unit tests command: yarn test:server diff --git a/bin/cli b/bin/cli index 314f33dd4..df2253261 100755 --- a/bin/cli +++ b/bin/cli @@ -11,6 +11,7 @@ const Matcher = require('did-you-mean'); program .command('serve', 'serve the application') + .command('db', 'run database commands') .command('settings', 'interact with the application settings') .command('assets', 'interact with assets') .command('setup', 'setup the application') diff --git a/bin/cli-db b/bin/cli-db new file mode 100755 index 000000000..1057b1057 --- /dev/null +++ b/bin/cli-db @@ -0,0 +1,53 @@ +#!/usr/bin/env node + +const util = require('./util'); +const program = require('commander'); +const config = require('../config'); + +async function createIndexes() { + try { + // Ensure we enable the index creation. + config.CREATE_MONGO_INDEXES = true; + + // TODO: handle the plugin index creation? + + // Let's register the shutdown hooks. + util.onshutdown([() => require('../services/mongoose').disconnect()]); + + // Lets create all the database indexes for the application and wait for all + // them to finish their indexing. + const models = [ + require('../models/action'), + require('../models/asset'), + require('../models/comment'), + require('../models/setting'), + require('../models/user'), + require('../models/migration'), + ]; + + // Call the `.init()` method to setup all the indexes on each model. + // `init()` returns a promise that resolves when the indexes have finished + // building successfully. The `init()` function is idempotent, so we don't + // have to worry about triggering an index rebuild. + await Promise.all(models.map(Model => Model.init())); + + console.log('Indexes created'); + util.shutdown(0); + } catch (err) { + console.error(err); + util.shutdown(1); + } +} + +program + .command('createIndexes') + .description('creates the database indexes and waits until they are created') + .action(createIndexes); + +program.parse(process.argv); + +// If there is no command listed, output help. +if (process.argv.length <= 2) { + program.outputHelp(); + util.shutdown(); +} diff --git a/models/action.js b/models/action.js index f3414f3ea..dd2bc4377 100644 --- a/models/action.js +++ b/models/action.js @@ -1,53 +1,4 @@ const mongoose = require('../services/mongoose'); -const uuid = require('uuid'); -const Schema = mongoose.Schema; -const ACTION_TYPES = require('./enum/action_types'); -const ITEM_TYPES = require('./enum/item_types'); +const { Action } = require('./schema'); -const ActionSchema = new Schema( - { - id: { - type: String, - default: uuid.v4, - unique: true, - }, - action_type: { - type: String, - enum: ACTION_TYPES, - }, - item_type: { - type: String, - enum: ITEM_TYPES, - }, - item_id: String, - user_id: String, - - // The element that summaries will additionally group on in addtion to their action_type, item_type, and - // item_id. - group_id: String, - - // Additional metadata stored on the field. - metadata: Schema.Types.Mixed, - }, - { - timestamps: { - createdAt: 'created_at', - updatedAt: 'updated_at', - }, - } -); - -// Create an index on the `item_id` field so that queries looking for -// actions based on the item id can resolve faster. -ActionSchema.index( - { - item_id: 1, - }, - { - background: true, - } -); - -const Action = mongoose.model('Action', ActionSchema); - -module.exports = Action; +module.exports = mongoose.model('Action', Action); diff --git a/models/asset.js b/models/asset.js index 6fdea3b78..6d7f95220 100644 --- a/models/asset.js +++ b/models/asset.js @@ -1,99 +1,4 @@ const mongoose = require('../services/mongoose'); -const Schema = mongoose.Schema; -const uuid = require('uuid'); -const TagLinkSchema = require('./schema/tag_link'); -const get = require('lodash/get'); +const { Asset } = require('./schema'); -const AssetSchema = new Schema( - { - id: { - type: String, - default: uuid.v4, - unique: true, - index: true, - }, - url: { - type: String, - unique: true, - index: true, - }, - type: { - type: String, - default: 'assets', - }, - scraped: { - type: Date, - default: null, - }, - closedAt: { - type: Date, - default: null, - }, - closedMessage: { - type: String, - default: null, - }, - title: String, - description: String, - image: String, - section: String, - subsection: String, - author: String, - publication_date: Date, - modified_date: Date, - - // This object is used exclusively for storing settings that are to override - // the base settings from the base Settings object. This is to be accessed - // always after running `rectifySettings` against it. - settings: { - default: {}, - type: Object, - }, - - // Tags are added by the self or by administrators. - tags: [TagLinkSchema], - - // Additional metadata stored on the field. - metadata: { - default: {}, - type: Object, - }, - }, - { - versionKey: false, - timestamps: { - createdAt: 'created_at', - updatedAt: 'updated_at', - }, - } -); - -AssetSchema.index( - { - title: 'text', - url: 'text', - description: 'text', - section: 'text', - subsection: 'text', - author: 'text', - }, - { - background: true, - } -); - -/** - * Returns true if the asset is closed, false else. - */ -AssetSchema.virtual('isClosed').get(function() { - const closedAt = get(this, 'closedAt', null); - if (closedAt === null) { - return false; - } - - return closedAt.getTime() <= new Date().getTime(); -}); - -const Asset = mongoose.model('Asset', AssetSchema); - -module.exports = Asset; +module.exports = mongoose.model('Asset', Asset); diff --git a/models/comment.js b/models/comment.js index 88f5e5829..f61740ffd 100644 --- a/models/comment.js +++ b/models/comment.js @@ -1,235 +1,4 @@ const mongoose = require('../services/mongoose'); -const Schema = mongoose.Schema; -const TagLinkSchema = require('./schema/tag_link'); -const uuid = require('uuid'); -const COMMENT_STATUS = require('./enum/comment_status'); +const { Comment } = require('./schema'); -/** - * The Mongo schema for a Comment Status. - * @type {Schema} - */ -const StatusSchema = new Schema( - { - type: { - type: String, - enum: COMMENT_STATUS, - }, - - // The User ID of the user that assigned the status. - assigned_by: { - type: String, - default: null, - }, - - created_at: Date, - }, - { - _id: false, - } -); - -/** - * A record of old body values for a Comment - */ -const BodyHistoryItemSchema = new Schema({ - body: { - required: true, - type: String, - }, - - // datetime until the comment body value was this.body - created_at: { - required: true, - type: Date, - default: Date, - }, -}); - -/** - * The Mongo schema for a Comment. - * @type {Schema} - */ -const CommentSchema = new Schema( - { - id: { - type: String, - default: uuid.v4, - unique: true, - }, - body: { - type: String, - required: [true, 'The body is required.'], - minlength: 2, - }, - body_history: [BodyHistoryItemSchema], - asset_id: String, - author_id: String, - status_history: [StatusSchema], - status: { - type: String, - enum: COMMENT_STATUS, - default: 'NONE', - }, - - // parent_id is the id of the parent comment (null if there is none). - parent_id: String, - - // The number of replies to this comment directly. - reply_count: { - type: Number, - default: 0, - }, - - // Counts to store related to actions taken on the given comment. - action_counts: { - default: {}, - type: Object, - }, - - // Tags are added by the self or by administrators. - tags: [TagLinkSchema], - - // Additional metadata stored on the field. - metadata: { - default: {}, - type: Object, - }, - }, - { - timestamps: { - createdAt: 'created_at', - updatedAt: 'updated_at', - }, - toJSON: { - virtuals: true, - }, - } -); - -// Add the indexes for the id of the comment. -CommentSchema.index( - { - id: 1, - }, - { - unique: true, - background: false, - } -); - -CommentSchema.index( - { - status: 1, - created_at: 1, - }, - { - background: true, - } -); - -CommentSchema.index( - { - status: 1, - created_at: 1, - asset_id: 1, - }, - { - background: true, - } -); - -// Create a sparse index to search across. -CommentSchema.index( - { - created_at: 1, - 'action_counts.flag': 1, - status: 1, - }, - { - background: true, - sparse: true, - } -); - -// Create a sparse index to search across. -CommentSchema.index( - { - 'action_counts.flag': 1, - status: 1, - }, - { - background: true, - sparse: true, - } -); - -// Add an index that is optimized for finding flagged comments. -CommentSchema.index( - { - asset_id: 1, - created_at: 1, - 'action_counts.flag': 1, - }, - { - background: true, - } -); - -// Add an index for the reply sort. -CommentSchema.index( - { - asset_id: 1, - created_at: -1, - reply_count: -1, - }, - { - background: true, - } -); - -// Optimize for tag searches/counts. -CommentSchema.index( - { - asset_id: 1, - 'tags.tag.name': 1, - status: 1, - }, - { - background: true, - } -); - -// Optimize for tag searches/counts. -CommentSchema.index( - { - 'tags.tag.name': 1, - status: 1, - }, - { - background: true, - sparse: true, - } -); - -// Add an index that is optimized for sorting based on the created_at timestamp -// but also good at locating comments that have a specific asset id. -CommentSchema.index( - { - asset_id: 1, - created_at: 1, - }, - { - background: true, - } -); - -CommentSchema.virtual('edited').get(function() { - return this.body_history.length > 1; -}); - -// Visable is true when the comment is visible to the public. -CommentSchema.virtual('visible').get(function() { - return ['ACCEPTED', 'NONE'].includes(this.status); -}); - -module.exports = mongoose.model('Comment', CommentSchema); +module.exports = mongoose.model('Comment', Comment); diff --git a/models/migration.js b/models/migration.js index d60a4c0d6..86982108e 100644 --- a/models/migration.js +++ b/models/migration.js @@ -1,10 +1,4 @@ const mongoose = require('../services/mongoose'); -const Schema = mongoose.Schema; +const { Migration } = require('./schema'); -const MigrationSchema = new Schema({ - version: Number, -}); - -const Migration = mongoose.model('Migration', MigrationSchema); - -module.exports = Migration; +module.exports = mongoose.model('Migration', Migration); diff --git a/models/schema/action.js b/models/schema/action.js new file mode 100644 index 000000000..1df7bd793 --- /dev/null +++ b/models/schema/action.js @@ -0,0 +1,51 @@ +const mongoose = require('../../services/mongoose'); +const uuid = require('uuid'); +const Schema = mongoose.Schema; +const ACTION_TYPES = require('../enum/action_types'); +const ITEM_TYPES = require('../enum/item_types'); + +const Action = new Schema( + { + id: { + type: String, + default: uuid.v4, + unique: true, + }, + action_type: { + type: String, + enum: ACTION_TYPES, + }, + item_type: { + type: String, + enum: ITEM_TYPES, + }, + item_id: String, + user_id: String, + + // The element that summaries will additionally group on in addtion to their action_type, item_type, and + // item_id. + group_id: String, + + // Additional metadata stored on the field. + metadata: Schema.Types.Mixed, + }, + { + timestamps: { + createdAt: 'created_at', + updatedAt: 'updated_at', + }, + } +); + +// Create an index on the `item_id` field so that queries looking for +// actions based on the item id can resolve faster. +Action.index( + { + item_id: 1, + }, + { + background: true, + } +); + +module.exports = Action; diff --git a/models/schema/asset.js b/models/schema/asset.js new file mode 100644 index 000000000..043bc9a3f --- /dev/null +++ b/models/schema/asset.js @@ -0,0 +1,97 @@ +const mongoose = require('../../services/mongoose'); +const Schema = mongoose.Schema; +const uuid = require('uuid'); +const TagLinkSchema = require('./tag_link'); +const { get } = require('lodash'); + +const Asset = new Schema( + { + id: { + type: String, + default: uuid.v4, + unique: true, + index: true, + }, + url: { + type: String, + unique: true, + index: true, + }, + type: { + type: String, + default: 'assets', + }, + scraped: { + type: Date, + default: null, + }, + closedAt: { + type: Date, + default: null, + }, + closedMessage: { + type: String, + default: null, + }, + title: String, + description: String, + image: String, + section: String, + subsection: String, + author: String, + publication_date: Date, + modified_date: Date, + + // This object is used exclusively for storing settings that are to override + // the base settings from the base Settings object. This is to be accessed + // always after running `rectifySettings` against it. + settings: { + default: {}, + type: Object, + }, + + // Tags are added by the self or by administrators. + tags: [TagLinkSchema], + + // Additional metadata stored on the field. + metadata: { + default: {}, + type: Object, + }, + }, + { + versionKey: false, + timestamps: { + createdAt: 'created_at', + updatedAt: 'updated_at', + }, + } +); + +Asset.index( + { + title: 'text', + url: 'text', + description: 'text', + section: 'text', + subsection: 'text', + author: 'text', + }, + { + background: true, + } +); + +/** + * Returns true if the asset is closed, false else. + */ +Asset.virtual('isClosed').get(function() { + const closedAt = get(this, 'closedAt', null); + if (closedAt === null) { + return false; + } + + return closedAt.getTime() <= new Date().getTime(); +}); + +module.exports = Asset; diff --git a/models/schema/comment.js b/models/schema/comment.js new file mode 100644 index 000000000..6ef5434d5 --- /dev/null +++ b/models/schema/comment.js @@ -0,0 +1,235 @@ +const mongoose = require('../../services/mongoose'); +const Schema = mongoose.Schema; +const TagLinkSchema = require('./tag_link'); +const uuid = require('uuid'); +const COMMENT_STATUS = require('../enum/comment_status'); + +/** + * The Mongo schema for a Comment Status. + * @type {Schema} + */ +const Status = new Schema( + { + type: { + type: String, + enum: COMMENT_STATUS, + }, + + // The User ID of the user that assigned the status. + assigned_by: { + type: String, + default: null, + }, + + created_at: Date, + }, + { + _id: false, + } +); + +/** + * A record of old body values for a Comment + */ +const BodyHistoryItemSchema = new Schema({ + body: { + required: true, + type: String, + }, + + // datetime until the comment body value was this.body + created_at: { + required: true, + type: Date, + default: Date, + }, +}); + +/** + * The Mongo schema for a Comment. + * @type {Schema} + */ +const Comment = new Schema( + { + id: { + type: String, + default: uuid.v4, + unique: true, + }, + body: { + type: String, + required: [true, 'The body is required.'], + minlength: 2, + }, + body_history: [BodyHistoryItemSchema], + asset_id: String, + author_id: String, + status_history: [Status], + status: { + type: String, + enum: COMMENT_STATUS, + default: 'NONE', + }, + + // parent_id is the id of the parent comment (null if there is none). + parent_id: String, + + // The number of replies to this comment directly. + reply_count: { + type: Number, + default: 0, + }, + + // Counts to store related to actions taken on the given comment. + action_counts: { + default: {}, + type: Object, + }, + + // Tags are added by the self or by administrators. + tags: [TagLinkSchema], + + // Additional metadata stored on the field. + metadata: { + default: {}, + type: Object, + }, + }, + { + timestamps: { + createdAt: 'created_at', + updatedAt: 'updated_at', + }, + toJSON: { + virtuals: true, + }, + } +); + +// Add the indexes for the id of the comment. +Comment.index( + { + id: 1, + }, + { + unique: true, + background: false, + } +); + +Comment.index( + { + status: 1, + created_at: 1, + }, + { + background: true, + } +); + +Comment.index( + { + status: 1, + created_at: 1, + asset_id: 1, + }, + { + background: true, + } +); + +// Create a sparse index to search across. +Comment.index( + { + created_at: 1, + 'action_counts.flag': 1, + status: 1, + }, + { + background: true, + sparse: true, + } +); + +// Create a sparse index to search across. +Comment.index( + { + 'action_counts.flag': 1, + status: 1, + }, + { + background: true, + sparse: true, + } +); + +// Add an index that is optimized for finding flagged comments. +Comment.index( + { + asset_id: 1, + created_at: 1, + 'action_counts.flag': 1, + }, + { + background: true, + } +); + +// Add an index for the reply sort. +Comment.index( + { + asset_id: 1, + created_at: -1, + reply_count: -1, + }, + { + background: true, + } +); + +// Optimize for tag searches/counts. +Comment.index( + { + asset_id: 1, + 'tags.tag.name': 1, + status: 1, + }, + { + background: true, + } +); + +// Optimize for tag searches/counts. +Comment.index( + { + 'tags.tag.name': 1, + status: 1, + }, + { + background: true, + sparse: true, + } +); + +// Add an index that is optimized for sorting based on the created_at timestamp +// but also good at locating comments that have a specific asset id. +Comment.index( + { + asset_id: 1, + created_at: 1, + }, + { + background: true, + } +); + +Comment.virtual('edited').get(function() { + return this.body_history.length > 1; +}); + +// Visible is true when the comment is visible to the public. +Comment.virtual('visible').get(function() { + return ['ACCEPTED', 'NONE'].includes(this.status); +}); + +module.exports = Comment; diff --git a/models/schema/index.js b/models/schema/index.js new file mode 100644 index 000000000..976b437c0 --- /dev/null +++ b/models/schema/index.js @@ -0,0 +1,21 @@ +const { CREATE_MONGO_INDEXES } = require('../../config'); + +const Action = require('./action'); +const Asset = require('./asset'); +const Comment = require('./comment'); +const Migration = require('./migration'); +const Setting = require('./setting'); +const User = require('./user'); + +const schema = { Action, Asset, Comment, Migration, Setting, User }; + +// Provide the schema to each of the plugins so that they can add in indexes if +// it is enabled. +if (CREATE_MONGO_INDEXES) { + const plugins = require('../../services/plugins'); + plugins.get('server', 'indexes').map(({ indexes }) => { + indexes(schema); + }); +} + +module.exports = schema; diff --git a/models/schema/migration.js b/models/schema/migration.js new file mode 100644 index 000000000..a8d0e6db5 --- /dev/null +++ b/models/schema/migration.js @@ -0,0 +1,8 @@ +const mongoose = require('../../services/mongoose'); +const Schema = mongoose.Schema; + +const Migration = new Schema({ + version: Number, +}); + +module.exports = Migration; diff --git a/models/schema/setting.js b/models/schema/setting.js new file mode 100644 index 000000000..5e226e6cb --- /dev/null +++ b/models/schema/setting.js @@ -0,0 +1,142 @@ +const mongoose = require('../../services/mongoose'); +const Schema = mongoose.Schema; +const TagSchema = require('./tag'); +const MODERATION_OPTIONS = require('../enum/moderation_options'); + +/** + * Setting manages application settings that get used on front and backend. + * @type {Schema} + */ +const Setting = new Schema( + { + id: { + type: String, + default: '1', + }, + moderation: { + type: String, + enum: MODERATION_OPTIONS, + default: 'POST', + }, + infoBoxEnable: { + type: Boolean, + default: false, + }, + customCssUrl: { + type: String, + default: '', + }, + infoBoxContent: { + type: String, + default: '', + }, + questionBoxEnable: { + type: Boolean, + default: false, + }, + questionBoxIcon: { + type: String, + default: 'default', + }, + questionBoxContent: { + type: String, + default: '', + }, + premodLinksEnable: { + type: Boolean, + default: false, + }, + organizationName: { + type: String, + }, + autoCloseStream: { + type: Boolean, + default: false, + }, + closedTimeout: { + type: Number, + + // Two weeks default expiry. + default: 60 * 60 * 24 * 7 * 2, + }, + closedMessage: { + type: String, + default: 'Expired', + }, + wordlist: { + banned: { + type: Array, + default: [], + }, + suspect: { + type: Array, + default: [], + }, + }, + charCount: { + type: Number, + default: 5000, + }, + charCountEnable: { + type: Boolean, + default: false, + }, + requireEmailConfirmation: { + type: Boolean, + default: false, + }, + domains: { + whitelist: { + type: Array, + default: ['localhost'], + }, + }, + + // Length of time (in milliseconds) after a comment is posted that it can still be edited by the author + editCommentWindowLength: { + type: Number, + min: [0, 'Edit Comment Window length must be greater than zero'], + default: 30 * 1000, + }, + tags: [TagSchema], + + // Additional metadata to let plugins write settings. + metadata: { + default: {}, + type: Object, + }, + }, + { + timestamps: { + createdAt: 'created_at', + updatedAt: 'updated_at', + }, + toObject: { + transform: (doc, ret) => { + delete ret._id; + delete ret.__v; + + return ret; + }, + }, + } +); + +/** + * Merges two settings objects. + */ +Setting.method('merge', function(src) { + Setting.eachPath(path => { + // Exclude internal fields... + if (['id', '_id', '__v', 'created_at', 'updated_at'].includes(path)) { + return; + } + + // If the source object contains the path, shallow copy it. + if (path in src) { + this[path] = src[path]; + } + }); +}); + +module.exports = Setting; diff --git a/models/schema/user.js b/models/schema/user.js new file mode 100644 index 000000000..ec9c018cc --- /dev/null +++ b/models/schema/user.js @@ -0,0 +1,375 @@ +const mongoose = require('../../services/mongoose'); +const bcrypt = require('bcryptjs'); +const Schema = mongoose.Schema; +const uuid = require('uuid'); +const TagLink = require('./tag_link'); +const Token = require('./token'); +const can = require('../../perms'); +const { get } = require('lodash'); + +// USER_ROLES is the array of roles that is permissible as a user role. +const USER_ROLES = require('../enum/user_roles'); + +// USER_STATUS_USERNAME is the list of statuses that are supported by storing +// the username state. +const USER_STATUS_USERNAME = require('../enum/user_status_username'); + +// Profile is the mongoose schema defined as the representation of a +// User's profile stored in MongoDB. +const Profile = new Schema( + { + // ID provides the identifier for the user profile, in the case of a local + // provider, the id would be an email, in the case of a social provider, + // the id would be the foreign providers identifier. + id: { + type: String, + required: true, + }, + + // Provider is simply the name attached to the authentication mode. In the + // case of a locally provided profile, this will simply be `local`, or a + // social provider which for Facebook would just be `facebook`. + provider: { + type: String, + required: true, + }, + + // Metadata provides a place to put provider specific details. An example of + // something that could be stored here is the `metadata.confirmed_at` could be + // used by the `local` provider to indicate when the email address was + // confirmed. + metadata: { + type: Schema.Types.Mixed, + }, + }, + { + _id: false, + } +); + +// User is the mongoose schema defined as the representation of a User in +// MongoDB. +const User = new Schema( + { + // This ID represents the most unique identifier for a user, it is generated + // when the user is created as a random uuid. + id: { + type: String, + default: uuid.v4, + unique: true, + required: true, + }, + + // This is sourced from the social provider or set manually during user setup + // and simply provides a name to display for the given user. + username: { + type: String, + required: true, + }, + + // TODO: find a way that we can instead utilize MongoDB 3.4's collation + // options to build the index in a case insenstive manner: + // https://docs.mongodb.com/manual/reference/collation/ + lowercaseUsername: { + type: String, + required: true, + unique: true, + }, + + // This provides a source of identity proof for users who login using the + // local provider. A local provider will be assumed for users who do not + // have any social profiles. + password: String, + + // Profiles describes the array of identities for a given user. Any one user + // can have multiple profiles associated with them, including multiple email + // addresses. + profiles: [Profile], + + // Tokens are the individual personal access tokens for a given user. + tokens: [Token], + + // Role is the specific user role that the user holds. + role: { + type: String, + enum: USER_ROLES, + required: true, + default: 'COMMENTER', + }, + + // Status stores the user status information regarding permissions, + // capabilities and moderation state. + status: { + // Username stores the current user status for the username as well as the + // history of changes. + username: { + // Status stores the current username status. + status: { + type: String, + enum: USER_STATUS_USERNAME, + }, + + // History stores the history of username status changes. + history: [ + { + // Status stores the historical username status. + status: { + type: String, + enum: USER_STATUS_USERNAME, + }, + + // assigned_by stores the user id of the user who assigned this status. + assigned_by: { type: String, default: null }, + + // created_at stores the date when this status was assigned. + created_at: { type: Date, default: Date.now }, + }, + ], + }, + + // Banned stores the current user banned status as well as the history of + // changes. + banned: { + // Status stores the current user banned status. + status: { + type: Boolean, + required: true, + default: false, + }, + history: [ + { + // Status stores the historical banned status. + status: Boolean, + + // assigned_by stores the user id of the user who assigned this status. + assigned_by: { type: String, default: null }, + + // message stores the email content sent to the user. + message: { type: String, default: null }, + + // created_at stores the date when this status was assigned. + created_at: { type: Date, default: Date.now }, + }, + ], + }, + + // Suspension stores the current user suspension status as well as the + // history of changes. + suspension: { + // until is the date that the user is suspended until. + until: { + type: Date, + default: null, + }, + history: [ + { + // until is the date that the user is suspended until. + until: Date, + + // assigned_by stores the user id of the user who assigned this status. + assigned_by: { type: String, default: null }, + + // message stores the email content sent to the user. + message: { type: String, default: null }, + + // created_at stores the date when this status was assigned. + created_at: { type: Date, default: Date.now }, + }, + ], + }, + }, + + // IgnoresUsers is an array of user id's that the current user is ignoring. + ignoresUsers: [String], + + // Counts to store related to actions taken on the given user. + action_counts: { + default: {}, + type: Object, + }, + + // Tags are added by the self or by administrators. + tags: [TagLink], + + // Additional metadata stored on the field. + metadata: { + default: {}, + type: Object, + }, + }, + { + // This will ensure that we have proper timestamps available on this model. + timestamps: { + createdAt: 'created_at', + updatedAt: 'updated_at', + }, + + toJSON: { + transform: function(doc, ret) { + delete ret.__v; + delete ret._id; + delete ret.password; + }, + }, + } +); + +// Add the index on the user profile data. +User.index( + { + 'profiles.id': 1, + 'profiles.provider': 1, + }, + { + unique: true, + background: false, + } +); + +User.index( + { + lowercaseUsername: 1, + 'profiles.id': 1, + created_at: -1, + }, + { + background: true, + } +); + +// This query is executed often, to count the number of flagged accounts with +// usernames. +User.index( + { + 'action_counts.flag': 1, + 'status.username.status': 1, + }, + { + background: true, + } +); + +// Sorting users by created at is the default people search. +User.index( + { + created_at: -1, + }, + { + background: true, + } +); + +/** + * returns true if a commenter is staff + */ +User.method('isStaff', function() { + return this.role !== 'COMMENTER'; +}); + +/** + * This verifies that a password is valid. + */ +User.method('verifyPassword', function(password) { + return new Promise((resolve, reject) => { + bcrypt.compare(password, this.password, (err, res) => { + if (err) { + return reject(err); + } + + if (!res) { + return resolve(false); + } + + return resolve(true); + }); + }); +}); + +/** + * Can returns true if the user is allowed to perform a specific graph + * operation. + */ +User.method('can', function(...actions) { + return can(this, ...actions); +}); + +/** + * firstEmail will return the first email on the user. + */ +User.virtual('firstEmail').get(function() { + const emails = this.emails; + if (emails.length === 0) { + return null; + } + + return emails[0]; +}); + +/** + * emails will return all the emails on a user. + */ +User.virtual('emails').get(function() { + return (this.profiles || []) + .filter(({ provider }) => provider === 'local') + .map(({ id }) => id); +}); + +/** + * hasVerifiedEmail will return true if at least one of the local email accounts + * have their email verified. + */ +User.virtual('hasVerifiedEmail').get(function() { + return this.profiles + .filter(({ provider }) => provider === 'local') + .some(profile => { + const confirmedAt = get(profile, 'metadata.confirmed_at') || null; + + // If the profile doesn't have a metadata field, or it does not have a + // confirmed_at field, or that field is null, then send them back. + return confirmedAt !== null; + }); +}); + +User.virtual('system') + .get(function() { + return this._system; + }) + .set(function(system) { + this._system = system; + }); + +/** + * banned returns true when the user is currently banned, and sets the banned + * status locally. + */ +User.virtual('banned') + .get(function() { + return this.status.banned.status; + }) + .set(function(status) { + this.status.banned.status = status; + this.status.banned.history.push({ + status, + created_at: new Date(), + }); + }); + +/** + * suspended returns true when the user is currently suspended, and sets the + * suspension status locally. + */ +User.virtual('suspended') + .get(function() { + return Boolean( + this.status.suspension.until && this.status.suspension.until > new Date() + ); + }) + .set(function(until) { + this.status.suspension.until = until; + this.status.suspension.history.push({ + until, + created_at: new Date(), + }); + }); + +module.exports = User; diff --git a/models/setting.js b/models/setting.js index 1cca9c989..48a495ad2 100644 --- a/models/setting.js +++ b/models/setting.js @@ -1,147 +1,4 @@ const mongoose = require('../services/mongoose'); -const Schema = mongoose.Schema; -const TagSchema = require('./schema/tag'); -const MODERATION_OPTIONS = require('./enum/moderation_options'); +const { Setting } = require('./schema'); -/** - * SettingSchema manages application settings that get used on front and backend. - * @type {Schema} - */ -const SettingSchema = new Schema( - { - id: { - type: String, - default: '1', - }, - moderation: { - type: String, - enum: MODERATION_OPTIONS, - default: 'POST', - }, - infoBoxEnable: { - type: Boolean, - default: false, - }, - customCssUrl: { - type: String, - default: '', - }, - infoBoxContent: { - type: String, - default: '', - }, - questionBoxEnable: { - type: Boolean, - default: false, - }, - questionBoxIcon: { - type: String, - default: 'default', - }, - questionBoxContent: { - type: String, - default: '', - }, - premodLinksEnable: { - type: Boolean, - default: false, - }, - organizationName: { - type: String, - }, - autoCloseStream: { - type: Boolean, - default: false, - }, - closedTimeout: { - type: Number, - - // Two weeks default expiry. - default: 60 * 60 * 24 * 7 * 2, - }, - closedMessage: { - type: String, - default: 'Expired', - }, - wordlist: { - banned: { - type: Array, - default: [], - }, - suspect: { - type: Array, - default: [], - }, - }, - charCount: { - type: Number, - default: 5000, - }, - charCountEnable: { - type: Boolean, - default: false, - }, - requireEmailConfirmation: { - type: Boolean, - default: false, - }, - domains: { - whitelist: { - type: Array, - default: ['localhost'], - }, - }, - - // Length of time (in milliseconds) after a comment is posted that it can still be edited by the author - editCommentWindowLength: { - type: Number, - min: [0, 'Edit Comment Window length must be greater than zero'], - default: 30 * 1000, - }, - tags: [TagSchema], - - // Additional metadata to let plugins write settings. - metadata: { - default: {}, - type: Object, - }, - }, - { - timestamps: { - createdAt: 'created_at', - updatedAt: 'updated_at', - }, - toObject: { - transform: (doc, ret) => { - delete ret._id; - delete ret.__v; - - return ret; - }, - }, - } -); - -/** - * Merges two settings objects. - */ -SettingSchema.method('merge', function(src) { - SettingSchema.eachPath(path => { - // Exclude internal fields... - if (['id', '_id', '__v', 'created_at', 'updated_at'].includes(path)) { - return; - } - - // If the source object contains the path, shallow copy it. - if (path in src) { - this[path] = src[path]; - } - }); -}); - -/** - * The Mongo Mongoose object. - */ -const Setting = mongoose.model('Setting', SettingSchema); - -module.exports = Setting; +module.exports = mongoose.model('Setting', Setting); diff --git a/models/user.js b/models/user.js index 717e43a88..842a8de79 100644 --- a/models/user.js +++ b/models/user.js @@ -1,378 +1,4 @@ const mongoose = require('../services/mongoose'); -const bcrypt = require('bcryptjs'); -const Schema = mongoose.Schema; -const uuid = require('uuid'); -const TagLinkSchema = require('./schema/tag_link'); -const TokenSchema = require('./schema/token'); -const can = require('../perms'); -const { get } = require('lodash'); +const { User } = require('./schema'); -// USER_ROLES is the array of roles that is permissible as a user role. -const USER_ROLES = require('./enum/user_roles'); - -// USER_STATUS_USERNAME is the list of statuses that are supported by storing -// the username state. -const USER_STATUS_USERNAME = require('./enum/user_status_username'); - -// ProfileSchema is the mongoose schema defined as the representation of a -// User's profile stored in MongoDB. -const ProfileSchema = new Schema( - { - // ID provides the identifier for the user profile, in the case of a local - // provider, the id would be an email, in the case of a social provider, - // the id would be the foreign providers identifier. - id: { - type: String, - required: true, - }, - - // Provider is simply the name attached to the authentication mode. In the - // case of a locally provided profile, this will simply be `local`, or a - // social provider which for Facebook would just be `facebook`. - provider: { - type: String, - required: true, - }, - - // Metadata provides a place to put provider specific details. An example of - // something that could be stored here is the `metadata.confirmed_at` could be - // used by the `local` provider to indicate when the email address was - // confirmed. - metadata: { - type: Schema.Types.Mixed, - }, - }, - { - _id: false, - } -); - -// UserSchema is the mongoose schema defined as the representation of a User in -// MongoDB. -const UserSchema = new Schema( - { - // This ID represents the most unique identifier for a user, it is generated - // when the user is created as a random uuid. - id: { - type: String, - default: uuid.v4, - unique: true, - required: true, - }, - - // This is sourced from the social provider or set manually during user setup - // and simply provides a name to display for the given user. - username: { - type: String, - required: true, - }, - - // TODO: find a way that we can instead utilize MongoDB 3.4's collation - // options to build the index in a case insenstive manner: - // https://docs.mongodb.com/manual/reference/collation/ - lowercaseUsername: { - type: String, - required: true, - unique: true, - }, - - // This provides a source of identity proof for users who login using the - // local provider. A local provider will be assumed for users who do not - // have any social profiles. - password: String, - - // Profiles describes the array of identities for a given user. Any one user - // can have multiple profiles associated with them, including multiple email - // addresses. - profiles: [ProfileSchema], - - // Tokens are the individual personal access tokens for a given user. - tokens: [TokenSchema], - - // Role is the specific user role that the user holds. - role: { - type: String, - enum: USER_ROLES, - required: true, - default: 'COMMENTER', - }, - - // Status stores the user status information regarding permissions, - // capabilities and moderation state. - status: { - // Username stores the current user status for the username as well as the - // history of changes. - username: { - // Status stores the current username status. - status: { - type: String, - enum: USER_STATUS_USERNAME, - }, - - // History stores the history of username status changes. - history: [ - { - // Status stores the historical username status. - status: { - type: String, - enum: USER_STATUS_USERNAME, - }, - - // assigned_by stores the user id of the user who assigned this status. - assigned_by: { type: String, default: null }, - - // created_at stores the date when this status was assigned. - created_at: { type: Date, default: Date.now }, - }, - ], - }, - - // Banned stores the current user banned status as well as the history of - // changes. - banned: { - // Status stores the current user banned status. - status: { - type: Boolean, - required: true, - default: false, - }, - history: [ - { - // Status stores the historical banned status. - status: Boolean, - - // assigned_by stores the user id of the user who assigned this status. - assigned_by: { type: String, default: null }, - - // message stores the email content sent to the user. - message: { type: String, default: null }, - - // created_at stores the date when this status was assigned. - created_at: { type: Date, default: Date.now }, - }, - ], - }, - - // Suspension stores the current user suspension status as well as the - // history of changes. - suspension: { - // until is the date that the user is suspended until. - until: { - type: Date, - default: null, - }, - history: [ - { - // until is the date that the user is suspended until. - until: Date, - - // assigned_by stores the user id of the user who assigned this status. - assigned_by: { type: String, default: null }, - - // message stores the email content sent to the user. - message: { type: String, default: null }, - - // created_at stores the date when this status was assigned. - created_at: { type: Date, default: Date.now }, - }, - ], - }, - }, - - // IgnoresUsers is an array of user id's that the current user is ignoring. - ignoresUsers: [String], - - // Counts to store related to actions taken on the given user. - action_counts: { - default: {}, - type: Object, - }, - - // Tags are added by the self or by administrators. - tags: [TagLinkSchema], - - // Additional metadata stored on the field. - metadata: { - default: {}, - type: Object, - }, - }, - { - // This will ensure that we have proper timestamps available on this model. - timestamps: { - createdAt: 'created_at', - updatedAt: 'updated_at', - }, - - toJSON: { - transform: function(doc, ret) { - delete ret.__v; - delete ret._id; - delete ret.password; - }, - }, - } -); - -// Add the index on the user profile data. -UserSchema.index( - { - 'profiles.id': 1, - 'profiles.provider': 1, - }, - { - unique: true, - background: false, - } -); - -UserSchema.index( - { - lowercaseUsername: 1, - 'profiles.id': 1, - created_at: -1, - }, - { - background: true, - } -); - -// This query is executed often, to count the number of flagged accounts with -// usernames. -UserSchema.index( - { - 'action_counts.flag': 1, - 'status.username.status': 1, - }, - { - background: true, - } -); - -// Sorting users by created at is the default people search. -UserSchema.index( - { - created_at: -1, - }, - { - background: true, - } -); - -/** - * returns true if a commenter is staff - */ -UserSchema.method('isStaff', function() { - return this.role !== 'COMMENTER'; -}); - -/** - * This verifies that a password is valid. - */ -UserSchema.method('verifyPassword', function(password) { - return new Promise((resolve, reject) => { - bcrypt.compare(password, this.password, (err, res) => { - if (err) { - return reject(err); - } - - if (!res) { - return resolve(false); - } - - return resolve(true); - }); - }); -}); - -/** - * Can returns true if the user is allowed to perform a specific graph - * operation. - */ -UserSchema.method('can', function(...actions) { - return can(this, ...actions); -}); - -/** - * firstEmail will return the first email on the user. - */ -UserSchema.virtual('firstEmail').get(function() { - const emails = this.emails; - if (emails.length === 0) { - return null; - } - - return emails[0]; -}); - -/** - * emails will return all the emails on a user. - */ -UserSchema.virtual('emails').get(function() { - return (this.profiles || []) - .filter(({ provider }) => provider === 'local') - .map(({ id }) => id); -}); - -/** - * hasVerifiedEmail will return true if at least one of the local email accounts - * have their email verified. - */ -UserSchema.virtual('hasVerifiedEmail').get(function() { - return this.profiles - .filter(({ provider }) => provider === 'local') - .some(profile => { - const confirmedAt = get(profile, 'metadata.confirmed_at') || null; - - // If the profile doesn't have a metadata field, or it does not have a - // confirmed_at field, or that field is null, then send them back. - return confirmedAt !== null; - }); -}); - -UserSchema.virtual('system') - .get(function() { - return this._system; - }) - .set(function(system) { - this._system = system; - }); - -/** - * banned returns true when the user is currently banned, and sets the banned - * status locally. - */ -UserSchema.virtual('banned') - .get(function() { - return this.status.banned.status; - }) - .set(function(status) { - this.status.banned.status = status; - this.status.banned.history.push({ - status, - created_at: new Date(), - }); - }); - -/** - * suspended returns true when the user is currently suspended, and sets the - * suspension status locally. - */ -UserSchema.virtual('suspended') - .get(function() { - return Boolean( - this.status.suspension.until && this.status.suspension.until > new Date() - ); - }) - .set(function(until) { - this.status.suspension.until = until; - this.status.suspension.history.push({ - until, - created_at: new Date(), - }); - }); - -// Create the User model. -const UserModel = mongoose.model('User', UserSchema); - -module.exports = UserModel; +module.exports = mongoose.model('User', User); diff --git a/plugin-api/beta/server/getReactionConfig.js b/plugin-api/beta/server/getReactionConfig.js index 40832ef74..da075d76c 100644 --- a/plugin-api/beta/server/getReactionConfig.js +++ b/plugin-api/beta/server/getReactionConfig.js @@ -2,24 +2,23 @@ const { SEARCH_OTHER_USERS } = require('../../../perms/constants'); const { ErrNotFound, ErrAlreadyExists } = require('../../../errors'); const pluralize = require('pluralize'); const sc = require('snake-case'); -const CommentModel = require('../../../models/comment'); -const { CREATE_MONGO_INDEXES } = require('../../../config'); +// const { CREATE_MONGO_INDEXES } = require('../../../config'); function getReactionConfig(reaction) { reaction = reaction.toLowerCase(); - if (CREATE_MONGO_INDEXES) { - // Create the index on the comment model based on the reaction config. - CommentModel.collection.createIndex( - { - created_at: 1, - [`action_counts.${sc(reaction)}`]: 1, - }, - { - background: true, - } - ); - } + // if (CREATE_MONGO_INDEXES) { + // // Create the index on the comment model based on the reaction config. + // CommentModel.collection.createIndex( + // { + // created_at: 1, + // [`action_counts.${sc(reaction)}`]: 1, + // }, + // { + // background: true, + // } + // ); + // } const reactionPlural = pluralize(reaction); const Reaction = reaction.charAt(0).toUpperCase() + reaction.slice(1); @@ -128,8 +127,8 @@ function getReactionConfig(reaction) { return { typeDefs, - schemas: ({ CommentSchema }) => { - CommentSchema.index( + indexes: ({ Comment }) => { + Comment.index( { created_at: 1, [`action_counts.${sc(reaction)}`]: 1, From 517852908302472a86d857394a08ff96e8b98fee Mon Sep 17 00:00:00 2001 From: Wyatt Johnson Date: Thu, 12 Apr 2018 09:44:17 -0600 Subject: [PATCH 72/84] adjusted env --- .circleci/config.yml | 2 +- package.json | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 9272a0572..848d496e7 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -1,6 +1,7 @@ # job_environment will setup the environment for any job being executed. job_environment: &job_environment + NODE_ENV: test DISABLE_CREATE_MONGO_INDEXES: TRUE # job_defaults applies all the defaults for each job. @@ -21,7 +22,6 @@ create_indexes: &create_indexes # integration_environment is the environment that configures the tests. integration_environment: &integration_environment <<: *job_environment - NODE_ENV: test CIRCLE_TEST_REPORTS: /tmp/circleci-test-results E2E_MAX_RETRIES: 3 diff --git a/package.json b/package.json index f5c1764c9..b0a37528e 100644 --- a/package.json +++ b/package.json @@ -19,11 +19,11 @@ "lint": "npm-run-all lint:*", "plugins:reconcile": "./bin/cli plugins reconcile", "test": "npm-run-all test:jest test:mocha", - "test:jest": "TEST_MODE=unit NODE_ENV=test jest --runInBand", - "test:client": "TEST_MODE=unit NODE_ENV=test jest --projects client", + "test:jest": "NODE_ENV=test jest --runInBand", + "test:client": "NODE_ENV=test jest --projects client", "test:server": "npm-run-all test:server:jest test:server:mocha", - "test:server:mocha": "TEST_MODE=unit NODE_ENV=test mocha -R ${MOCHA_REPORTER:-spec}", - "test:server:jest": "TEST_MODE=unit NODE_ENV=test jest --runInBand --projects .", + "test:server:mocha": "NODE_ENV=test mocha -R ${MOCHA_REPORTER:-spec}", + "test:server:jest": "NODE_ENV=test jest --runInBand --projects .", "e2e": "./scripts/e2e.js", "e2e:ci": "./scripts/e2e-ci.sh", "heroku-postbuild": "npm-run-all plugins:reconcile build", From 142f463211b5222e8d858bc9d970a14245ec56ac Mon Sep 17 00:00:00 2001 From: Wyatt Johnson Date: Thu, 12 Apr 2018 12:47:09 -0600 Subject: [PATCH 73/84] Storage Fixes - Implements storage fixes (fixes #1520) - Implements updates to locale --- client/coral-framework/services/i18n.js | 43 +++++-- client/coral-framework/services/storage.js | 128 ++++++++++++++++----- 2 files changed, 131 insertions(+), 40 deletions(-) diff --git a/client/coral-framework/services/i18n.js b/client/coral-framework/services/i18n.js index 9c65c9c4e..2ba0e4e07 100644 --- a/client/coral-framework/services/i18n.js +++ b/client/coral-framework/services/i18n.js @@ -53,25 +53,44 @@ let timeagoInstance; function setLocale(storage, locale) { try { - if (storage) { - storage.setItem('locale', locale); - } + storage.setItem('locale', locale); } catch (err) { - console.error(err); + console.warn('Error while trying to persist the language detection', err); } } -function getLocale(storage) { +// detectLanguage will try to get the locale from storage if available, +// otherwise will try to get it from the navigator, otherwise, it will fallback +// to the default language. +function detectLanguage(storage) { try { - return ( - (storage && storage.getItem('locale')) || - navigator.language || - defaultLanguage - ).split('-')[0]; + const lang = storage.getItem('locale') || navigator.language; + if (lang) { + return lang; + } } catch (err) { - console.error(err); - return null; + console.warn( + 'Error while trying to detect language, will fallback to', + err + ); } + + console.warn('Could not detect language, will fallback to', defaultLanguage); + return defaultLanguage; +} + +// getLocale will get the users locale from the local detector and parse it to a +// format we can work with. +function getLocale(storage) { + // Get the language from the local detector. + const lang = detectLanguage(storage); + + // Some language strings come with additional subtags as defined in: + // + // https://www.ietf.org/rfc/bcp/bcp47.txt + // + // So we should strip that off if we find it. + return lang.split('-')[0]; } export function setupTranslations() { diff --git a/client/coral-framework/services/storage.js b/client/coral-framework/services/storage.js index 66e2c96cd..2e54718c7 100644 --- a/client/coral-framework/services/storage.js +++ b/client/coral-framework/services/storage.js @@ -1,40 +1,112 @@ import uuid from 'uuid/v4'; -function getStorage(type) { - let storage; - try { - storage = window[type]; - const x = '__storage_test__'; - storage.setItem(x, x); - storage.removeItem(x); - } catch (e) { - const ignore = - e instanceof DOMException && - // everything except Firefox - (e.code === 22 || - // SecurityError related to having 3rd party cookies disabled. - e.code === 18 || - // Firefox +function testStorage(storage) { + const key = '__storage_test__'; - e.code === 1014 || - // test name field too, because code might not be present + // Create a unique test value. + const expectedValue = String(Date.now()); - // everything except Firefox - e.name === 'QuotaExceededError' || - // Firefox - e.name === 'NS_ERROR_DOM_QUOTA_REACHED'); - if (!ignore) { - console.warn(e); + // Try to set, get, and remove that item. + storage.setItem(key, expectedValue); + const canSetGet = expectedValue === storage.getItem(key); + storage.removeItem(key); + + return canSetGet; +} + +// InMemoryStorage is a dumb implementation of the Storage interface that will +// not persist the data at all. It implements the Storage interface found: +// +// https://developer.mozilla.org/en-US/docs/Web/API/Storage +// +class InMemoryStorage { + constructor() { + this.storage = {}; + } + + get length() { + return Object.keys(this.storage).length; + } + + key(n) { + if (this.length <= n) { + return undefined; } - // When third party cookies are disabled, session storage is readable/ - // writable, but localStorage is not. Try to get the sessionStorage to use. - if (type !== 'sessionStorage') { - return getStorage('sessionStorage'); + return this.storage[Object.keys(this.storage)[n]]; + } + + getItem(key) { + return this.storage[key]; + } + + setItem(key, value) { + this.storage[key] = value; + + try { + // Test sessionStorage. We could have been given access recently. + const canSetGet = testStorage(sessionStorage); + + if (canSetGet) { + sessionStorage.setItem(key, value); + console.log( + 'Attempt to persist InMemoryStorage value to sessionStorage succeeded' + ); + } + } catch (err) { + console.warn( + 'Attempt to persist InMemoryStorage value to sessionStorage failed', + err + ); } } - return storage; + removeItem(key) { + delete this.storage[key]; + } +} + +// getStorage will test to see if the requested storage type is available, if it +// is not, it will try sessionStorage, and if that is also not available, it +// will fallback to InMemoryStorage. +function getStorage(type) { + let storage; + try { + // Get the desired storage from the window. + storage = window[type]; + + // Test the storage. + const canSetGet = testStorage(storage); + + // If we can set/get then use that storage. + if (canSetGet) { + console.log('Access to', type, 'is available'); + return storage; + } else { + console.warn( + 'Failed to set/get on', + type, + 'falling back to InMemoryStorage' + ); + } + } catch (err) { + // When third party cookies are disabled, session storage is readable/ + // writable, but localStorage is not. Try to get the sessionStorage to use. + if (type !== 'sessionStorage') { + console.warn('Could not access', type, 'trying sessionStorage', err); + return getStorage('sessionStorage'); + } + + console.warn( + 'Could not access', + type, + 'falling back to InMemoryStorage', + err + ); + } + + // No acceptable storage could be found, returning the InMemoryStorage. + return new InMemoryStorage(); } /** From 1c87181b1709528aacb8bad5b5d44ffaaee0169f Mon Sep 17 00:00:00 2001 From: Wyatt Johnson Date: Thu, 12 Apr 2018 12:58:30 -0600 Subject: [PATCH 74/84] cleanups around storageAccess --- client/coral-admin/src/actions/moderation.js | 9 +------- client/coral-admin/src/index.js | 3 ++- client/coral-framework/actions/auth.js | 22 +++++++------------- client/coral-framework/services/storage.js | 17 +++++++++++++++ 4 files changed, 27 insertions(+), 24 deletions(-) diff --git a/client/coral-admin/src/actions/moderation.js b/client/coral-admin/src/actions/moderation.js index 16c2bd2f7..a46d2dccd 100644 --- a/client/coral-admin/src/actions/moderation.js +++ b/client/coral-admin/src/actions/moderation.js @@ -5,14 +5,7 @@ export const singleView = () => ({ type: actions.SINGLE_VIEW }); // hide shortcuts note export const hideShortcutsNote = () => (dispatch, _, { localStorage }) => { - try { - if (localStorage) { - localStorage.setItem('coral:shortcutsNote', 'hide'); - } - } catch (e) { - // above will fail in Safari private mode - } - + localStorage.setItem('coral:shortcutsNote', 'hide'); dispatch({ type: actions.HIDE_SHORTCUTS_NOTE }); }; diff --git a/client/coral-admin/src/index.js b/client/coral-admin/src/index.js index 0cf0a8cf9..158870dec 100644 --- a/client/coral-admin/src/index.js +++ b/client/coral-admin/src/index.js @@ -15,7 +15,8 @@ import { hideShortcutsNote } from './actions/moderation'; smoothscroll.polyfill(); function init({ store, localStorage }) { - if (localStorage && localStorage.getItem('coral:shortcutsNote') === 'hide') { + const shouldHide = localStorage.getItem('coral:shortcutsNote') === 'hide'; + if (shouldHide) { store.dispatch(hideShortcutsNote()); } } diff --git a/client/coral-framework/actions/auth.js b/client/coral-framework/actions/auth.js index 5390153e6..5e3b0846f 100644 --- a/client/coral-framework/actions/auth.js +++ b/client/coral-framework/actions/auth.js @@ -15,9 +15,7 @@ export const checkLogin = () => ( rest('/auth') .then(result => { if (!result.user) { - if (localStorage) { - cleanAuthData(localStorage); - } + cleanAuthData(localStorage); dispatch(checkLoginSuccess(null)); return; } @@ -52,10 +50,8 @@ const checkLoginSuccess = user => ({ }); export const setAuthToken = token => (dispatch, _, { localStorage }) => { - if (localStorage) { - localStorage.setItem('exp', jwtDecode(token).exp); - localStorage.setItem('token', token); - } + localStorage.setItem('exp', jwtDecode(token).exp); + localStorage.setItem('token', token); // Dispatch the set auth token action. For some browsers and situations, we // may not be able to persist the auth token any other way. Keep it in redux! @@ -70,11 +66,8 @@ export const handleSuccessfulLogin = (user, token) => ( { client, localStorage, postMessage } ) => { const { exp } = jwtDecode(token); - - if (localStorage) { - localStorage.setItem('exp', exp); - localStorage.setItem('token', token); - } + localStorage.setItem('exp', exp); + localStorage.setItem('token', token); // Send the message via the messages service to the window.opener if it // exists. @@ -105,9 +98,8 @@ export const logout = () => async ( ) => { await rest('/auth', { method: 'DELETE' }); - if (localStorage) { - cleanAuthData(localStorage); - } + // Clear the auth data persisted to localStorage. + cleanAuthData(localStorage); // Reset the websocket. client.resetWebsocket(); diff --git a/client/coral-framework/services/storage.js b/client/coral-framework/services/storage.js index 2e54718c7..e206163e6 100644 --- a/client/coral-framework/services/storage.js +++ b/client/coral-framework/services/storage.js @@ -63,6 +63,23 @@ class InMemoryStorage { removeItem(key) { delete this.storage[key]; + + try { + // Test sessionStorage. We could have been given access recently. + const canSetGet = testStorage(sessionStorage); + + if (canSetGet) { + sessionStorage.removeItem(key); + console.log( + 'Attempt to persist InMemoryStorage delete to sessionStorage succeeded' + ); + } + } catch (err) { + console.warn( + 'Attempt to persist InMemoryStorage delete to sessionStorage failed', + err + ); + } } } From 5125bb5d2d77c89dc8558a648c79d3def9c62840 Mon Sep 17 00:00:00 2001 From: Wyatt Johnson Date: Thu, 12 Apr 2018 13:22:20 -0600 Subject: [PATCH 75/84] logic cleanup --- client/coral-framework/services/storage.js | 59 +++++++++------------- 1 file changed, 23 insertions(+), 36 deletions(-) diff --git a/client/coral-framework/services/storage.js b/client/coral-framework/services/storage.js index e206163e6..7b7acf655 100644 --- a/client/coral-framework/services/storage.js +++ b/client/coral-framework/services/storage.js @@ -1,6 +1,6 @@ import uuid from 'uuid/v4'; -function testStorage(storage) { +function testStorageAccess(storage) { const key = '__storage_test__'; // Create a unique test value. @@ -11,7 +11,10 @@ function testStorage(storage) { const canSetGet = expectedValue === storage.getItem(key); storage.removeItem(key); - return canSetGet; + if (!canSetGet) { + // We can't access the desired storage! + throw new Error('Storage access test failed'); + } } // InMemoryStorage is a dumb implementation of the Storage interface that will @@ -45,14 +48,13 @@ class InMemoryStorage { try { // Test sessionStorage. We could have been given access recently. - const canSetGet = testStorage(sessionStorage); + testStorageAccess(sessionStorage); - if (canSetGet) { - sessionStorage.setItem(key, value); - console.log( - 'Attempt to persist InMemoryStorage value to sessionStorage succeeded' - ); - } + // Test passed! Set the item in sessionStorage. + sessionStorage.setItem(key, value); + console.log( + 'Attempt to persist InMemoryStorage value to sessionStorage succeeded' + ); } catch (err) { console.warn( 'Attempt to persist InMemoryStorage value to sessionStorage failed', @@ -66,14 +68,13 @@ class InMemoryStorage { try { // Test sessionStorage. We could have been given access recently. - const canSetGet = testStorage(sessionStorage); + testStorageAccess(sessionStorage); - if (canSetGet) { - sessionStorage.removeItem(key); - console.log( - 'Attempt to persist InMemoryStorage delete to sessionStorage succeeded' - ); - } + // Test passed! Remove the item from sessionStorage. + sessionStorage.removeItem(key); + console.log( + 'Attempt to persist InMemoryStorage delete to sessionStorage succeeded' + ); } catch (err) { console.warn( 'Attempt to persist InMemoryStorage delete to sessionStorage failed', @@ -87,25 +88,13 @@ class InMemoryStorage { // is not, it will try sessionStorage, and if that is also not available, it // will fallback to InMemoryStorage. function getStorage(type) { - let storage; try { - // Get the desired storage from the window. - storage = window[type]; + // Get the desired storage from the window and test it out. + const storage = window[type]; + testStorageAccess(storage); - // Test the storage. - const canSetGet = testStorage(storage); - - // If we can set/get then use that storage. - if (canSetGet) { - console.log('Access to', type, 'is available'); - return storage; - } else { - console.warn( - 'Failed to set/get on', - type, - 'falling back to InMemoryStorage' - ); - } + // Storage test was successful! Return it. + return storage; } catch (err) { // When third party cookies are disabled, session storage is readable/ // writable, but localStorage is not. Try to get the sessionStorage to use. @@ -115,9 +104,7 @@ function getStorage(type) { } console.warn( - 'Could not access', - type, - 'falling back to InMemoryStorage', + 'Could not access sessionStorage falling back to InMemoryStorage', err ); } From bb11de562f9a9014cae9a57826383f84cce97f4f Mon Sep 17 00:00:00 2001 From: Wyatt Johnson Date: Thu, 12 Apr 2018 13:24:56 -0600 Subject: [PATCH 76/84] Removed storage check --- client/coral-framework/services/i18n.js | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/client/coral-framework/services/i18n.js b/client/coral-framework/services/i18n.js index 2ba0e4e07..e1c3ab99e 100644 --- a/client/coral-framework/services/i18n.js +++ b/client/coral-framework/services/i18n.js @@ -52,11 +52,7 @@ let lang; let timeagoInstance; function setLocale(storage, locale) { - try { - storage.setItem('locale', locale); - } catch (err) { - console.warn('Error while trying to persist the language detection', err); - } + storage.setItem('locale', locale); } // detectLanguage will try to get the locale from storage if available, From 4a4357bc4c45130d80e0e26a2dddace9870219ed Mon Sep 17 00:00:00 2001 From: Wyatt Johnson Date: Thu, 12 Apr 2018 13:37:42 -0600 Subject: [PATCH 77/84] version bump --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index c9243a560..02cc45e70 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "talk", - "version": "4.3.1", + "version": "4.3.2", "description": "A better commenting experience from Mozilla, The New York Times, and the Washington Post. https://coralproject.net", "main": "app.js", "private": true, From e5a2152cfdfc1e3b64911fa5b9e3b7833060c75a Mon Sep 17 00:00:00 2001 From: Kit Westneat Date: Thu, 12 Apr 2018 17:51:01 -0400 Subject: [PATCH 78/84] don't show profile tab if user is logged out --- client/coral-embed-stream/src/components/Embed.js | 2 ++ .../src/components/ExtendableTabPanel.js | 8 +++++--- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/client/coral-embed-stream/src/components/Embed.js b/client/coral-embed-stream/src/components/Embed.js index 0c4fed129..cb4803a2d 100644 --- a/client/coral-embed-stream/src/components/Embed.js +++ b/client/coral-embed-stream/src/components/Embed.js @@ -16,6 +16,8 @@ import cn from 'classnames'; export default class Embed extends React.Component { getTabs() { + if (!this.props.currentUser) return false; + const tabs = [ - - {tabs} - + {tabs && ( + + {tabs} + + )} {loading ? (
From 87a97d405354d09c1171c034b6f57ff9076d736b Mon Sep 17 00:00:00 2001 From: Wyatt Johnson Date: Tue, 17 Apr 2018 12:31:31 -0600 Subject: [PATCH 79/84] Adds Author Index for Comments - Fixes #1529 --- models/comment.js | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/models/comment.js b/models/comment.js index 88f5e5829..de4a22826 100644 --- a/models/comment.js +++ b/models/comment.js @@ -163,6 +163,17 @@ CommentSchema.index( } ); +// Add an index that is optimized for finding a user's comments. +CommentSchema.index( + { + author_id: 1, + created_at: -1, + }, + { + background: true, + } +); + // Add an index that is optimized for finding flagged comments. CommentSchema.index( { From a6344d8e717f5e83d13dc30bd1fbb7f66bc26e49 Mon Sep 17 00:00:00 2001 From: Wyatt Johnson Date: Tue, 17 Apr 2018 13:19:03 -0600 Subject: [PATCH 80/84] fixed proptype missing validation --- .../src/components/Embed.js | 23 +++++++++++-------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/client/coral-embed-stream/src/components/Embed.js b/client/coral-embed-stream/src/components/Embed.js index cb4803a2d..a2476527e 100644 --- a/client/coral-embed-stream/src/components/Embed.js +++ b/client/coral-embed-stream/src/components/Embed.js @@ -16,8 +16,6 @@ import cn from 'classnames'; export default class Embed extends React.Component { getTabs() { - if (!this.props.currentUser) return false; - const tabs = [ {t('embed_comments_tab')} , - - {t('framework.my_profile')} - , ]; + + if (this.props.currentUser) { + tabs.push( + + {t('framework.my_profile')} + + ); + } + if (can(this.props.currentUser, 'UPDATE_ASSET_CONFIG')) { tabs.push( ); } + return tabs; } From a257fdb623f838a869cc9a060feca3de6478e7a3 Mon Sep 17 00:00:00 2001 From: Wyatt Johnson Date: Tue, 17 Apr 2018 13:23:02 -0600 Subject: [PATCH 81/84] removed deprecated not logged in ui for profile tab --- .../tabs/profile/components/NotLoggedIn.css | 14 ---------- .../tabs/profile/components/NotLoggedIn.js | 15 ----------- .../src/tabs/profile/containers/Profile.js | 27 +++---------------- 3 files changed, 4 insertions(+), 52 deletions(-) delete mode 100644 client/coral-embed-stream/src/tabs/profile/components/NotLoggedIn.css delete mode 100644 client/coral-embed-stream/src/tabs/profile/components/NotLoggedIn.js diff --git a/client/coral-embed-stream/src/tabs/profile/components/NotLoggedIn.css b/client/coral-embed-stream/src/tabs/profile/components/NotLoggedIn.css deleted file mode 100644 index c15c44c98..000000000 --- a/client/coral-embed-stream/src/tabs/profile/components/NotLoggedIn.css +++ /dev/null @@ -1,14 +0,0 @@ -.message { - padding: 10px 0 20px; - letter-spacing: 0.1px; - font-size: 13px; - line-height: 33px; -} - -.message a { - color: black; - font-weight: bold; - cursor: pointer; - margin: 0px; - padding-bottom: 2px; -} diff --git a/client/coral-embed-stream/src/tabs/profile/components/NotLoggedIn.js b/client/coral-embed-stream/src/tabs/profile/components/NotLoggedIn.js deleted file mode 100644 index 4987d57be..000000000 --- a/client/coral-embed-stream/src/tabs/profile/components/NotLoggedIn.js +++ /dev/null @@ -1,15 +0,0 @@ -import React from 'react'; -import styles from './NotLoggedIn.css'; -import cn from 'classnames'; - -import t from 'coral-framework/services/i18n'; - -export default ({ showSignInDialog }) => ( -
-
- {t('settings.sign_in')}{' '} - {t('settings.to_access')} -
-
{t('from_settings_page')}
-
-); diff --git a/client/coral-embed-stream/src/tabs/profile/containers/Profile.js b/client/coral-embed-stream/src/tabs/profile/containers/Profile.js index 30d634d94..84584ae5d 100644 --- a/client/coral-embed-stream/src/tabs/profile/containers/Profile.js +++ b/client/coral-embed-stream/src/tabs/profile/containers/Profile.js @@ -2,27 +2,17 @@ import React, { Component } from 'react'; import PropTypes from 'prop-types'; import { connect } from 'react-redux'; import { compose, gql } from 'react-apollo'; -import { bindActionCreators } from 'redux'; import { withQuery } from 'coral-framework/hocs'; -import NotLoggedIn from '../components/NotLoggedIn'; import { Spinner } from 'coral-ui'; import Profile from '../components/Profile'; import TabPanel from './TabPanel'; import { getDefinitionName } from 'coral-framework/utils'; -import { showSignInDialog } from 'coral-embed-stream/src/actions/login'; import { getSlotFragmentSpreads } from 'coral-framework/utils'; class ProfileContainer extends Component { - componentWillReceiveProps(nextProps) { - if (!this.props.currentUser && nextProps.currentUser) { - // Refetch after login. - this.props.data.refetch(); - } - } - render() { - const { currentUser, showSignInDialog, root } = this.props; + const { currentUser, root } = this.props; const { me } = this.props.root; const loading = this.props.data.loading; @@ -30,10 +20,6 @@ class ProfileContainer extends Component { return
{this.props.data.error.message}
; } - if (!currentUser) { - return ; - } - if (loading || !me) { return ; } @@ -57,7 +43,6 @@ ProfileContainer.propTypes = { data: PropTypes.object, root: PropTypes.object, currentUser: PropTypes.object, - showSignInDialog: PropTypes.func, }; const slots = ['profileSections']; @@ -85,10 +70,6 @@ const mapStateToProps = state => ({ currentUser: state.auth.user, }); -const mapDispatchToProps = dispatch => - bindActionCreators({ showSignInDialog }, dispatch); - -export default compose( - connect(mapStateToProps, mapDispatchToProps), - withProfileQuery -)(ProfileContainer); +export default compose(connect(mapStateToProps), withProfileQuery)( + ProfileContainer +); From b6299c2d406c75c1bb208bc6014387d59f4d31e3 Mon Sep 17 00:00:00 2001 From: Wyatt Johnson Date: Tue, 17 Apr 2018 13:35:30 -0600 Subject: [PATCH 82/84] fix e2e test --- test/e2e/specs/03_embedStream.js | 6 ------ 1 file changed, 6 deletions(-) diff --git a/test/e2e/specs/03_embedStream.js b/test/e2e/specs/03_embedStream.js index f45c467e8..4b6831271 100644 --- a/test/e2e/specs/03_embedStream.js +++ b/test/e2e/specs/03_embedStream.js @@ -96,12 +96,6 @@ module.exports = { comments.logout(); }, - 'not logged in user clicks my profile tab': client => { - const embedStream = client.page.embedStream(); - const profile = embedStream.goToProfileSection(); - - profile.assert.visible('@notLoggedIn'); - }, 'admin logs in': client => { const { testData: { admin } } = client.globals; const embedStream = client.page.embedStream(); From 3ae105114818aa42bd563dc3629a95d6e1a3abf2 Mon Sep 17 00:00:00 2001 From: Clint Brown Date: Tue, 17 Apr 2018 15:44:33 +1000 Subject: [PATCH 83/84] Add system withheld status to InactiveCommentLabel --- .../src/tabs/stream/components/InactiveCommentLabel.js | 4 ++++ locales/en.yml | 1 + 2 files changed, 5 insertions(+) diff --git a/client/coral-embed-stream/src/tabs/stream/components/InactiveCommentLabel.js b/client/coral-embed-stream/src/tabs/stream/components/InactiveCommentLabel.js index 3f0360099..48ea7767c 100644 --- a/client/coral-embed-stream/src/tabs/stream/components/InactiveCommentLabel.js +++ b/client/coral-embed-stream/src/tabs/stream/components/InactiveCommentLabel.js @@ -18,6 +18,10 @@ const InactiveCommentLabel = ({ status, className, ...rest }) => { label = t('modqueue.rejected'); icon = 'close'; break; + case 'SYSTEM_WITHHELD': + label = t('modqueue.system_withheld'); + icon = 'flag'; + break; default: throw new Error(`Unknown inactive status ${status}`); } diff --git a/locales/en.yml b/locales/en.yml index 6b2330f27..0fbd7bf01 100644 --- a/locales/en.yml +++ b/locales/en.yml @@ -344,6 +344,7 @@ en: sort: "Sort" show_shortcuts: "Show Shortcuts" singleview: "Zen mode" + system_withheld: "System Withheld" thismenu: "Open this menu" jump_to_queue: "Jump to specific queue" thousand: k From c4cbefeb81d73a37045a1a3cc6e87787173b7c2c Mon Sep 17 00:00:00 2001 From: Kim Gardner Date: Wed, 18 Apr 2018 16:02:39 -0400 Subject: [PATCH 84/84] Change to UserHistory in UserDetail.js --- client/coral-admin/src/components/UserDetail.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/client/coral-admin/src/components/UserDetail.js b/client/coral-admin/src/components/UserDetail.js index 7181a6f52..02b0e66f8 100644 --- a/client/coral-admin/src/components/UserDetail.js +++ b/client/coral-admin/src/components/UserDetail.js @@ -3,7 +3,7 @@ import cn from 'classnames'; import PropTypes from 'prop-types'; import capitalize from 'lodash/capitalize'; import styles from './UserDetail.css'; -import AccountHistory from './AccountHistory'; +import UserHistory from './UserHistory'; import { Slot } from 'coral-framework/components'; import UserDetailCommentList from '../components/UserDetailCommentList'; import { @@ -334,7 +334,7 @@ class UserDetail extends React.Component { tabId={'history'} className={'talk-admin-user-detail-history-tab-pane'} > - +