{/* HEADER */}
Composer
@@ -21,28 +30,23 @@ export const SidebarContainer = () => {
{/* TAB SELECTOR */}
{/* SIDEBAR CONTENT */}
- {
- activeTab === "composer" &&
-
- }
- {activeTab === "history" &&
-
- }
-
+ {activeTab === 'composer' &&
}
+ {activeTab === 'history' &&
}
);
-}
+};
+
+export default SidebarContainer;
diff --git a/src/client/components/containers/SingleReqResContainer.jsx b/src/client/components/containers/SingleReqResContainer.jsx
index 796c4cf5a..d96121897 100644
--- a/src/client/components/containers/SingleReqResContainer.jsx
+++ b/src/client/components/containers/SingleReqResContainer.jsx
@@ -1,312 +1,338 @@
-/* eslint-disable jsx-a11y/no-static-element-interactions */
-/* eslint-disable jsx-a11y/click-events-have-key-events */
-import React, { useState } from "react";
-import { useSelector, useDispatch } from "react-redux";
-import * as actions from "../../actions/actions.js";
-
-import connectionController from "../../controllers/reqResController";
-import RestRequestContent from "../display/RestRequestContent.jsx";
-import GraphQLRequestContent from "../display/GraphQLRequestContent.jsx";
-import GRPCRequestContent from "../display/GRPCRequestContent.jsx";
-
-const SingleReqResContainer = (props) => {
- const [showDetails, setShowDetails] = useState(false);
- const dispatch = useDispatch();
-
- const currentResponse = useSelector(
- (store) => store.business.currentResponse
- );
-
- const newRequestFields = useSelector(
- (store) => store.business.newRequestFields
- );
- const newRequestStreams = useSelector(
- (store) => store.business.newRequestStreams
- );
- //content is reqRes drilled down from ReqResContainer. reqRes was created in WSContainer/RestContainer...
- const {
- content,
- content: {
- id,
- graphQL,
- closeCode,
- protocol,
- request,
- response,
- connection,
- connectionType,
- isHTTP2,
- url,
- timeReceived,
- timeSent,
- rpc,
- service,
- },
- reqResUpdate,
- reqResDelete,
- index,
- } = props;
- const network = content.request.network;
- const method = content.request.method;
-
- const copyToComposer = () => {
- let requestFieldObj = {};
- if (network === "rest") {
- requestFieldObj = {
- ...newRequestFields,
- method: content.request.method || "GET",
- protocol: content.protocol || "http://",
- url: content.url,
- restUrl: content.request.restUrl,
- graphQL: content.graphQL || false,
- gRPC: content.gRPC || false,
- network,
- testContent: content.request.testContent,
- };
- }
- if (network === "ws") {
- requestFieldObj = {
- ...newRequestFields,
- method: content.request.method || "GET",
- protocol: content.protocol || "http://",
- url: content.url,
- wsUrl: content.request.wsUrl,
- graphQL: content.graphQL || false,
- gRPC: content.gRPC || false,
- network,
- };
- }
- if (network === "graphQL") {
- requestFieldObj = {
- ...newRequestFields,
- method: content.request.method || "GET",
- protocol: content.protocol || "http://",
- url: content.url,
- gqlUrl: content.request.gqlUrl,
- graphQL: content.graphQL || false,
- gRPC: content.gRPC || false,
- network,
- testContent: content.request.testContent,
- };
- }
- if (network === "grpc") {
- requestFieldObj = {
- ...newRequestFields,
- method: content.request.method || "GET",
- protocol: content.protocol || "http://",
- url: content.url,
- grpcUrl: content.request.grpcUrl,
- graphQL: content.graphQL || false,
- gRPC: content.gRPC || false,
- network,
- testContent: content.request.testContent,
- };
- }
- let headerDeeperCopy;
- if (content.request.headers) {
- headerDeeperCopy = JSON.parse(JSON.stringify(content.request.headers));
- headerDeeperCopy.push({
- id: content.request.headers.length + 1,
- active: false,
- key: "",
- value: "",
- });
- }
- let cookieDeeperCopy;
- if (content.request.cookies && !/ws/.test(protocol)) {
- cookieDeeperCopy = JSON.parse(JSON.stringify(content.request.cookies));
- cookieDeeperCopy.push({
- id: content.request.cookies.length + 1,
- active: false,
- key: "",
- value: "",
- });
- }
- const requestHeadersObj = {
- headersArr: headerDeeperCopy || [],
- count: headerDeeperCopy ? headerDeeperCopy.length : 1,
- };
- const requestCookiesObj = {
- cookiesArr: cookieDeeperCopy || [],
- count: cookieDeeperCopy ? cookieDeeperCopy.length : 1,
- };
- const requestBodyObj = {
- bodyType: content.request.bodyType || "raw",
- bodyContent: content.request.body || "",
- bodyVariables: content.request.bodyVariables || "",
- rawType: content.request.rawType || "Text (text/plain)",
- JSONFormatted: true,
- bodyIsNew: false,
- };
- dispatch(actions.setNewRequestFields(requestFieldObj));
- dispatch(actions.setNewRequestHeaders(requestHeadersObj));
- dispatch(actions.setNewRequestCookies(requestCookiesObj));
- dispatch(actions.setNewRequestBody(requestBodyObj));
- dispatch(actions.setNewRequestSSE(content.request.isSSE));
-
- if (content && content.gRPC) {
- const streamsDeepCopy = JSON.parse(JSON.stringify(content.streamsArr));
- const contentsDeepCopy = JSON.parse(
- JSON.stringify(content.streamContent)
- );
- // construct the streams obj from passed in history content & set state in store
-
- const requestStreamsObj = {
- streamsArr: streamsDeepCopy,
- count: content.queryArr.length,
- streamContent: contentsDeepCopy,
- selectedPackage: content.packageName,
- selectedRequest: content.rpc,
- selectedService: content.service,
- selectedStreamingType: content.request.method,
- initialQuery: content.initialQuery,
- queryArr: content.queryArr,
- protoPath: content.protoPath,
- services: content.servicesObj,
- protoContent: content.protoContent,
- };
- dispatch(actions.setNewRequestStreams(requestStreamsObj));
- }
-
- dispatch(actions.setSidebarActiveTab("composer"));
- };
-
- const removeReqRes = () => {
- connectionController.closeReqRes(content);
- reqResDelete(content);
- };
-
- const getBorderClass = () => {
- let classes = "highlighted-response ";
- if (currentResponse.gRPC) classes += "is-grpc-border";
- else if (currentResponse.graphQL) classes += "is-graphQL-border";
- else if (currentResponse.request.method === "WS") classes += "is-ws-border";
- else classes += "is-rest-border";
- return classes;
- };
-
- const highlightClasses =
- currentResponse.id === content.id ? getBorderClass(currentResponse) : "";
-
- return (
-
- {/* TITLE BAR */}
-
-
- {request.method}
-
-
-
{url}
- {/* RENDER STATUS */}
-
- {connection === "uninitialized" && (
-
- )}
- {connection === "error" &&
}
- {connection === "open" &&
}
- {connection === "closed" &&
- method !== "WS" &&
- method !== "SUBSCRIPTION" && (
-
- )}
- {connection === "closed" &&
- (method === "WS" || method === "SUBSCRIPTION") && (
-
- )}
-
-
-
- {/* VIEW REQUEST DETAILS / MINIMIZE */}
- {network !== "ws" && (
-
{
- setShowDetails(showDetails === false);
- }}
- >
- {showDetails === true && "Hide Request Details"}
- {showDetails === false && "View Request Details"}
- {showDetails === true && (
-
- Copy to Composer
-
- )}
-
- )}
- {/* REQUEST ELEMENTS */}
- {showDetails === true && (
-
- {network === "rest" && (
-
- )}
- {network === "grpc" && (
-
- )}
- {network === "graphQL" && (
-
- )}
-
- )}
- {/* REMOVE / SEND BUTTONS */}
-
- {
- removeReqRes();
- dispatch(actions.saveCurrentResponseData({}));
- }}
- >
- Remove
-
- {/* SEND BUTTON */}
- {connection === "uninitialized" && (
- {
- //check the request type
- //if it's http, dispatch setactivetab to "event" for reqresponsepane
- //otherwise do nothing
- if (connectionType !== "WebSocket") {
- dispatch(actions.setResponsePaneActiveTab("events"));
- }
-
- connectionController.openReqRes(content.id);
- dispatch(
- actions.saveCurrentResponseData(
- content,
- "singleReqResContainercomponentSendHandler"
- )
- ); //dispatch will fire first before the callback of [ipcMain.on('open-ws'] is fired. check async and callback queue concepts
- }}
- >
- Send
-
- )}
- {/* VIEW RESPONSE BUTTON */}
- {connection !== "uninitialized" && (
- {
- dispatch(actions.saveCurrentResponseData(content));
- }}
- >
- View Response
-
- )}
-
-
- );
-};
-export default SingleReqResContainer;
+/* eslint-disable jsx-a11y/no-static-element-interactions */
+/* eslint-disable jsx-a11y/click-events-have-key-events */
+import React, { useEffect, useState } from 'react';
+import { useSelector, useDispatch } from 'react-redux';
+import * as actions from '../../actions/actions.js';
+import connectionController from '../../controllers/reqResController';
+import RestRequestContent from '../display/RestRequestContent.jsx';
+import GraphQLRequestContent from '../display/GraphQLRequestContent.jsx';
+import WebRTCRequestContent from '../display/WebRTCRequestContent.jsx';
+import GRPCRequestContent from '../display/GRPCRequestContent.jsx';
+import OpenAPIRequestContent from '../display/OpenAPIRequestContent.jsx';
+
+const SingleReqResContainer = (props) => {
+ const [showDetails, setShowDetails] = useState(false);
+ const dispatch = useDispatch();
+
+ const currentResponse = useSelector(
+ (store) => store.business.currentResponse
+ );
+
+ const newRequestFields = useSelector(
+ (store) => store.business.newRequestFields
+ );
+
+ const {
+ content,
+ content: { protocol, request, connection, connectionType, isHTTP2, url },
+
+ reqResDelete,
+ index,
+ } = props;
+ const network = content.request.network;
+ const method = content.request.method;
+
+ useEffect(() => {
+ if (content.request.network === 'webrtc') {
+ setShowDetails(true);
+ }
+ }, [content.request.network]);
+
+ const copyToComposer = () => {
+ let requestFieldObj = {};
+
+ if (network === 'rest') {
+ requestFieldObj = {
+ ...newRequestFields,
+ method: content.request.method || 'GET',
+ protocol: content.protocol || 'http://',
+ url: content.url,
+ restUrl: content.request.restUrl,
+ graphQL: content.graphQL || false,
+ gRPC: content.gRPC || false,
+ webrtc: content.webrtc || false,
+ network,
+ testContent: content.request.testContent,
+ };
+ }
+
+ if (network === 'ws') {
+ requestFieldObj = {
+ ...newRequestFields,
+ method: content.request.method || 'GET',
+ protocol: content.protocol || 'http://',
+ url: content.url,
+ wsUrl: content.request.wsUrl,
+ graphQL: content.graphQL || false,
+ gRPC: content.gRPC || false,
+ network,
+ };
+ }
+
+ if (network === 'webrtc') {
+ requestFieldObj = {
+ ...newRequestFields,
+ method: content.request.method || 'GET',
+ protocol: content.protocol || 'http://',
+ url: content.url,
+ wsUrl: content.request.wsUrl,
+ graphQL: content.graphQL || false,
+ gRPC: content.gRPC || false,
+ network,
+ webrtcData: content.webrtcData,
+ };
+ }
+
+ if (network === 'graphQL') {
+ requestFieldObj = {
+ ...newRequestFields,
+ method: content.request.method || 'GET',
+ protocol: content.protocol || 'http://',
+ url: content.url,
+ gqlUrl: content.request.gqlUrl,
+ graphQL: content.graphQL || false,
+ gRPC: content.gRPC || false,
+ network,
+ testContent: content.request.testContent,
+ };
+ }
+
+ if (network === 'grpc') {
+ requestFieldObj = {
+ ...newRequestFields,
+ method: content.request.method || 'GET',
+ protocol: content.protocol || 'http://',
+ url: content.url,
+ grpcUrl: content.request.grpcUrl,
+ graphQL: content.graphQL || false,
+ gRPC: content.gRPC || false,
+ network,
+ testContent: content.request.testContent,
+ };
+ }
+
+ let headerDeeperCopy;
+
+ if (content.request.headers) {
+ headerDeeperCopy = JSON.parse(JSON.stringify(content.request.headers));
+ headerDeeperCopy.push({
+ id: content.request.headers.length + 1,
+ active: false,
+ key: '',
+ value: '',
+ });
+ }
+
+ let cookieDeeperCopy;
+
+ if (content.request.cookies && !/ws/.test(protocol)) {
+ cookieDeeperCopy = JSON.parse(JSON.stringify(content.request.cookies));
+ cookieDeeperCopy.push({
+ id: content.request.cookies.length + 1,
+ active: false,
+ key: '',
+ value: '',
+ });
+ }
+
+ const requestHeadersObj = {
+ headersArr: headerDeeperCopy || [],
+ count: headerDeeperCopy ? headerDeeperCopy.length : 1,
+ };
+
+ const requestCookiesObj = {
+ cookiesArr: cookieDeeperCopy || [],
+ count: cookieDeeperCopy ? cookieDeeperCopy.length : 1,
+ };
+
+ const requestBodyObj = {
+ webrtcData: content.webrtcData,
+ bodyType: content.request.bodyType || 'raw',
+ bodyContent: content.request.body || '',
+ bodyVariables: content.request.bodyVariables || '',
+ rawType: content.request.rawType || 'Text (text/plain)',
+ JSONFormatted: true,
+ bodyIsNew: false,
+ };
+
+ dispatch(actions.setNewRequestFields(requestFieldObj));
+ dispatch(actions.setNewRequestHeaders(requestHeadersObj));
+ dispatch(actions.setNewRequestCookies(requestCookiesObj));
+ dispatch(actions.setNewRequestBody(requestBodyObj));
+ dispatch(actions.setNewRequestSSE(content.request.isSSE));
+
+ if (content && content.gRPC) {
+ const streamsDeepCopy = JSON.parse(JSON.stringify(content.streamsArr));
+ const contentsDeepCopy = JSON.parse(
+ JSON.stringify(content.streamContent)
+ );
+
+ // construct the streams obj from passed in history content & set state in store
+ const requestStreamsObj = {
+ streamsArr: streamsDeepCopy,
+ count: content.queryArr.length,
+ streamContent: contentsDeepCopy,
+ selectedPackage: content.packageName,
+ selectedRequest: content.rpc,
+ selectedService: content.service,
+ selectedStreamingType: content.request.method,
+ initialQuery: content.initialQuery,
+ queryArr: content.queryArr,
+ protoPath: content.protoPath,
+ services: content.servicesObj,
+ protoContent: content.protoContent,
+ };
+
+ dispatch(actions.setNewRequestStreams(requestStreamsObj));
+ }
+
+ dispatch(actions.setSidebarActiveTab('composer'));
+ };
+
+ const removeReqRes = () => {
+ connectionController.closeReqRes(content);
+ reqResDelete(content);
+ };
+
+ const getBorderClass = () => {
+ let classes = 'highlighted-response ';
+ if (currentResponse.gRPC) classes += 'is-grpc-border';
+ else if (currentResponse.graphQL) classes += 'is-graphQL-border';
+ else if (currentResponse.request.method === 'WS') classes += 'is-ws-border';
+ else if (currentResponse.webrtc) classes += 'is-webrtc-border';
+ else classes += 'is-rest-border';
+ return classes;
+ };
+
+ const highlightClasses =
+ currentResponse.id === content.id ? getBorderClass(currentResponse) : '';
+
+ return (
+
+ {/* TITLE BAR */}
+
+
+ {request.method}
+
+
+
{url}
+ {/* RENDER STATUS */}
+
+ {connection === 'uninitialized' && (
+
+ )}
+ {connection === 'error' &&
}
+ {connection === 'open' &&
}
+ {connection === 'closed' &&
+ method !== 'WS' &&
+ method !== 'SUBSCRIPTION' && (
+
+ )}
+ {connection === 'closed' &&
+ (method === 'WS' || method === 'SUBSCRIPTION') && (
+
+ )}
+
+
+
+ {/* VIEW REQUEST DETAILS / MINIMIZE */}
+ {network !== 'ws' && (
+
{
+ setShowDetails(showDetails === false);
+ }}
+ >
+ {showDetails === true && 'Hide Request Details'}
+ {showDetails === false && 'View Request Details'}
+ {network !== 'openapi' && showDetails === true && (
+
+ Copy to Composer
+
+ )}
+
+ )}
+ {/* REQUEST ELEMENTS */}
+ {showDetails === true && (
+
+ {network === 'rest' && (
+
+ )}
+ {network === 'openapi' && (
+
+ )}
+ {network === 'grpc' && (
+
+ )}
+ {network === 'graphQL' && (
+
+ )}
+ {network === 'webrtc' && }
+
+ )}
+ {/* REMOVE / SEND BUTTONS */}
+
+ {
+ removeReqRes();
+ dispatch(actions.saveCurrentResponseData({}));
+ }}
+ >
+ Remove
+
+ {/* SEND BUTTON */}
+ {connection === 'uninitialized' && (
+ {
+ //check the request type
+ //if it's http, dispatch set active tab to "event" for reqResResponse
+ //otherwise do nothing
+ if (connectionType !== 'WebSocket') {
+ dispatch(actions.setResponsePaneActiveTab('events'));
+ }
+ connectionController.openReqRes(content.id);
+ dispatch(
+ actions.saveCurrentResponseData(
+ content,
+ 'singleReqResContainercomponentSendHandler'
+ )
+ ); //dispatch will fire first before the callback of [ipcMain.on('open-ws'] is fired. check async and callback queue concepts
+ }}
+ >
+ Send
+
+ )}
+ {/* VIEW RESPONSE BUTTON */}
+ {connection !== 'uninitialized' && (
+ {
+ dispatch(actions.saveCurrentResponseData(content));
+ }}
+ >
+ View Response
+
+ )}
+
+
+ );
+};
+export default SingleReqResContainer;
diff --git a/src/client/components/containers/SingleScheduleReqResContainer.jsx b/src/client/components/containers/SingleScheduleReqResContainer.jsx
index a59c650bb..58ab8a5f2 100644
--- a/src/client/components/containers/SingleScheduleReqResContainer.jsx
+++ b/src/client/components/containers/SingleScheduleReqResContainer.jsx
@@ -1,159 +1,132 @@
-import React, { useEffect, useState } from "react";
-import { useSelector, useDispatch } from "react-redux";
-import * as actions from "../../actions/actions.js";
-
-import connectionController from "../../controllers/reqResController";
-import RestRequestContent from "../display/RestRequestContent.jsx";
-import GraphQLRequestContent from "../display/GraphQLRequestContent.jsx";
-import GRPCRequestContent from "../display/GRPCRequestContent.jsx";
-import ReqResCtrl from "../../controllers/reqResController";
-
-const SingleScheduleReqResContainer = (props) => {
- const [showDetails, setShowDetails] = useState(false);
- const [checker, setChecker] = useState(false);
- const dispatch = useDispatch();
-
- const currentResponse = useSelector(
- (store) => store.business.currentResponse
- );
-
- const newRequestFields = useSelector(
- (store) => store.business.newRequestFields
- );
- const newRequestStreams = useSelector(
- (store) => store.business.newRequestStreams
- );
-
- const {
- content,
- content: {
- id,
- graphQL,
- closeCode,
- protocol,
- request,
- response,
- connection,
- connectionType,
- isHTTP2,
- url,
- timeReceived,
- timeSent,
- rpc,
- service,
- },
- reqResUpdate,
- reqResDelete,
- index,
- date,
- } = props;
- const network = content.request.network;
- const method = content.request.method;
-
- const getBorderClass = () => {
- let classes = "highlighted-response ";
- if (currentResponse.gRPC) classes += "is-grpc-border";
- else if (currentResponse.graphQL) classes += "is-graphQL-border";
- else if (currentResponse.request.method === "WS") classes += "is-ws-border";
- else classes += "is-rest-border";
- return classes;
- };
-
- const highlightClasses =
- currentResponse.id === content.id ? getBorderClass(currentResponse) : "";
-
- //USE EFFECT
- useEffect(() => {
- dispatch(
- actions.saveCurrentResponseData(
- content,
- "SingleScheduleReqResContainerComponent"
- )
- );
- }, []);
-
- return (
-
- {/* TITLE BAR */}
-
-
- {request.method}
-
-
-
{url}
- {/* RENDER STATUS */}
-
- {connection === "uninitialized" && (
-
- )}
- {connection === "error" &&
}
- {connection === "open" &&
}
- {connection === "closed" &&
- method != "WS" &&
- method !== "SUBSCRIPTION" && (
-
- )}
- {connection === "closed" &&
- (method === "WS" || method === "SUBSCRIPTION") && (
-
- )}
-
-
-
- {/* VIEW REQUEST DETAILS / MINIMIZE */}
- {network !== "ws" && (
-
{
- setShowDetails((showDetails = false));
- }}
- >
- {date}
- {showDetails === true && (
-
- Copy to Composer
-
- )}
-
- )}
- {/* REQUEST ELEMENTS */}
- {showDetails === true && (
-
- {network === "rest" && (
-
- )}
- {network === "grpc" && (
-
- )}
- {network === "graphQL" && (
-
- )}
-
- )}
- {/* REMOVE / SEND BUTTONS */}
-
- {true && (
- {
- dispatch(actions.saveCurrentResponseData(content));
- }}
- >
- View Response
-
- )}
-
-
- );
-};
-export default SingleScheduleReqResContainer;
+/* eslint-disable jsx-a11y/no-static-element-interactions */
+/* eslint-disable jsx-a11y/click-events-have-key-events */
+import React, { useEffect, useState } from 'react';
+import { useSelector, useDispatch } from 'react-redux';
+import * as actions from '../../actions/actions.js';
+import RestRequestContent from '../display/RestRequestContent.jsx';
+import GraphQLRequestContent from '../display/GraphQLRequestContent.jsx';
+import GRPCRequestContent from '../display/GRPCRequestContent.jsx';
+
+const SingleScheduleReqResContainer = (props) => {
+ const [showDetails, setShowDetails] = useState(false);
+ const dispatch = useDispatch();
+
+ const currentResponse = useSelector(
+ (store) => store.business.currentResponse
+ );
+
+ const {
+ content,
+ content: { request, connection, isHTTP2, url },
+ index,
+ date,
+ } = props;
+ const network = content.request.network;
+ const method = content.request.method;
+
+ const getBorderClass = () => {
+ let classes = 'highlighted-response ';
+ if (currentResponse.gRPC) classes += 'is-grpc-border';
+ else if (currentResponse.graphQL) classes += 'is-graphQL-border';
+ else if (currentResponse.request.method === 'WS') classes += 'is-ws-border';
+ else classes += 'is-rest-border';
+ return classes;
+ };
+
+ const highlightClasses =
+ currentResponse.id === content.id ? getBorderClass(currentResponse) : '';
+
+ useEffect(() => {
+ dispatch(
+ actions.saveCurrentResponseData(
+ content,
+ 'SingleScheduleReqResContainerComponent'
+ )
+ );
+ }, [content, dispatch]);
+
+ return (
+
+ {/* TITLE BAR */}
+
+
+ {request.method}
+
+
+
{url}
+ {/* RENDER STATUS */}
+
+ {connection === 'uninitialized' && (
+
+ )}
+ {connection === 'error' &&
}
+ {connection === 'open' &&
}
+ {connection === 'closed' &&
+ method != 'WS' &&
+ method !== 'SUBSCRIPTION' && (
+
+ )}
+ {connection === 'closed' &&
+ (method === 'WS' || method === 'SUBSCRIPTION') && (
+
+ )}
+
+
+
+ {/* VIEW REQUEST DETAILS / MINIMIZE */}
+ {network !== 'ws' && (
+
{
+ setShowDetails((showDetails = false));
+ }}
+ >
+ {date}
+ {showDetails === true && (
+
+ Copy to Composer
+
+ )}
+
+ )}
+ {/* REQUEST ELEMENTS */}
+ {showDetails === true && (
+
+ {network === 'rest' && (
+
+ )}
+ {network === 'grpc' && (
+
+ )}
+ {network === 'graphQL' && (
+
+ )}
+
+ )}
+ {/* REMOVE / SEND BUTTONS */}
+
+ {true && (
+ {
+ dispatch(actions.saveCurrentResponseData(content));
+ }}
+ >
+ View Response
+
+ )}
+
+
+ );
+};
+export default SingleScheduleReqResContainer;
diff --git a/src/client/components/containers/SingleTestContainer.jsx b/src/client/components/containers/SingleTestContainer.jsx
index 7b449ac5f..e736334c0 100644
--- a/src/client/components/containers/SingleTestContainer.jsx
+++ b/src/client/components/containers/SingleTestContainer.jsx
@@ -1,95 +1,97 @@
-import React, {useState, useEffect} from 'react'
-import { Doughnut } from "react-chartjs-2";
-import TestResult from './TestResult'
-import VerticalProgress from '../display/testResultsAnimated'
-
-export default function SingleTestContainer({ currentResponse }) {
- const {
- request,
- response
- } = currentResponse;
-
- const { url } = currentResponse;
-
-
-
- const passFailScripts = [];
-
- let pass = 0;
- let fail = 0;
-
- if (response.testResult !== undefined && response.testResult !== null) {
- response.testResult.forEach((ele, idx) => {
- if (ele.status === 'PASS') pass += 1;
- else fail += 1;
- const test =
-
- passFailScripts.push(test);
- });
- }
-
- const data = {
- datasets: [{data: [pass, fail], backgroundColor: ['#06a568', '#f66b61']}],
- labels: ['Passed','Failed'],
- };
-
-
-
-
- return (
- <>
-
-
- {/*
{passFailScripts}
*/}
-
-
Summary
-
-
-
- {/* Test Results Graph */}
-
-
-
-
- {/* Test Results Pass + Fail */}
-
-
-
- Total Tests: {pass + fail}
-
-
- Percentage Passed: {Math.floor((pass / (pass + fail)) * 100)}%
-
-
-
- Passed: {pass}
-
-
- Failed: {fail}
-
-
-
-
-
-
-
- >
- )
-}
\ No newline at end of file
+import React from 'react';
+import TestResult from './TestResult';
+import VerticalProgress from '../display/testResultsAnimated';
+
+export default function SingleTestContainer({ currentResponse }) {
+ const { request, response } = currentResponse;
+
+ const { url } = currentResponse;
+
+ const passFailScripts = [];
+
+ let pass = 0;
+ let fail = 0;
+
+ if (response.testResult !== undefined && response.testResult !== null) {
+ response.testResult.forEach((ele, idx) => {
+ if (ele.status === 'PASS') pass += 1;
+ else fail += 1;
+ const test = (
+
+ );
+
+ passFailScripts.push(test);
+ });
+ }
+
+ const data = {
+ datasets: [{ data: [pass, fail], backgroundColor: ['#06a568', '#f66b61'] }],
+ labels: ['Passed', 'Failed'],
+ };
+
+ return (
+ <>
+
+
+ {/*
{passFailScripts}
*/}
+
+
Summary
+
+
+
+ {/* Test Results Graph */}
+
+
+
+
+ {/* Test Results Pass + Fail */}
+
+
+
+ Total Tests:{' '}
+ {pass + fail}
+
+
+ Percentage Passed:{' '}
+
+ {Math.floor((pass / (pass + fail)) * 100)}%
+
+
+
+
+
+ Passed:
+ {' '}
+ {pass}
+
+
+
+ Failed:{' '}
+ {' '}
+ {fail}
+
+
+
+
+
+
+
+ >
+ );
+}
diff --git a/src/client/components/containers/StoppedContainer.jsx b/src/client/components/containers/StoppedContainer.jsx
index 9c1f0aa38..f7b479134 100644
--- a/src/client/components/containers/StoppedContainer.jsx
+++ b/src/client/components/containers/StoppedContainer.jsx
@@ -1,68 +1,74 @@
-import React, { useEffect, useState } from "react";
-import { connect, useDispatch } from "react-redux";
-import * as actions from "../../actions/actions";
-import SingleScheduleReqResContainer from "./SingleScheduleReqResContainer.jsx";
-import SingleReqResContainer from "./SingleReqResContainer.jsx";
-import ReqResCtrl from "../../controllers/reqResController";
-
-const mapStateToProps = (store) => ({
- reqResArray: store.business.reqResArray,
- scheduledReqResArray: store.business.scheduledReqResArray,
- currentTab: store.business.currentTab,
-});
-
-const mapDispatchToProps = (dispatch) => ({
- reqResDelete: (reqRes) => {
- dispatch(actions.reqResDelete(reqRes));
- },
- reqResUpdate: (reqRes) => {
- dispatch(actions.reqResUpdate(reqRes));
- },
- scheduledReqResDelete: () => {
- dispatch(actions.scheduledReqResDelete());
- },
- clearAllGraph: () => {
- dispatch(actions.clearAllGraph());
- },
-});
-
-const StoppedContainer = (props) => {
- const { reqResArray, reqResDelete, reqResUpdate, runScheduledTests, scheduledReqResArray, scheduledReqResDelete, clearAllGraph } = props;
- const dispatch = useDispatch();
-
- let scheduledReqResMapped = scheduledReqResArray.map((reqRes, index) => {
- return (
-
- );
- });
-
- return (
- <>
-
-
Scheduled Requests
-
- {
- scheduledReqResDelete();
- clearAllGraph();
- }}
- >
- Clear
-
-
- {scheduledReqResMapped.reverse()}
-
- >
- );
-};
-
-export default connect(mapStateToProps, mapDispatchToProps)(StoppedContainer);
+import React from 'react';
+import { connect, useDispatch } from 'react-redux';
+import * as actions from '../../actions/actions';
+import SingleScheduleReqResContainer from './SingleScheduleReqResContainer.jsx';
+
+const mapStateToProps = (store) => ({
+ reqResArray: store.business.reqResArray,
+ scheduledReqResArray: store.business.scheduledReqResArray,
+ currentTab: store.business.currentTab,
+});
+
+const mapDispatchToProps = (dispatch) => ({
+ reqResDelete: (reqRes) => {
+ dispatch(actions.reqResDelete(reqRes));
+ },
+ reqResUpdate: (reqRes) => {
+ dispatch(actions.reqResUpdate(reqRes));
+ },
+ scheduledReqResDelete: () => {
+ dispatch(actions.scheduledReqResDelete());
+ },
+ clearAllGraph: () => {
+ dispatch(actions.clearAllGraph());
+ },
+});
+
+const StoppedContainer = (props) => {
+ const {
+ reqResArray,
+ reqResDelete,
+ reqResUpdate,
+ runScheduledTests,
+ scheduledReqResArray,
+ scheduledReqResDelete,
+ clearAllGraph,
+ } = props;
+ const dispatch = useDispatch();
+
+ const scheduledReqResMapped = scheduledReqResArray.map((reqRes, index) => {
+ return (
+
+ );
+ });
+
+ return (
+ <>
+
+
Scheduled Requests
+
+ {
+ scheduledReqResDelete();
+ clearAllGraph();
+ }}
+ >
+ Clear
+
+
+ {scheduledReqResMapped.reverse()}
+
+ >
+ );
+};
+
+export default connect(mapStateToProps, mapDispatchToProps)(StoppedContainer);
diff --git a/src/client/components/containers/TestResult.jsx b/src/client/components/containers/TestResult.jsx
index 1abc0dae8..d3a2ec00d 100644
--- a/src/client/components/containers/TestResult.jsx
+++ b/src/client/components/containers/TestResult.jsx
@@ -1,19 +1,22 @@
-import React from 'react';
-
-export default function TestResult({ id, status, message }) {
- const testColor = status === 'PASS' ? 'success' : 'danger';
-
- return (
-
-
- {status}
-
-
- {message}
-
-
- );
-}
+import React from 'react';
+
+function TestResult({ id, status, message }) {
+ const testColor = status === 'PASS' ? 'success' : 'danger';
+
+ return (
+
+
+ {status}
+
+
+ {message}
+
+
+ );
+}
+
+export default TestResult;
diff --git a/src/client/components/containers/TestsContainer.jsx b/src/client/components/containers/TestsContainer.jsx
index bad12dd9c..9519e3682 100644
--- a/src/client/components/containers/TestsContainer.jsx
+++ b/src/client/components/containers/TestsContainer.jsx
@@ -1,13 +1,15 @@
-import React from "react";
-import EmptyState from "../display/EmptyState";
-import SingleTestContainer from './SingleTestContainer';
-
-export default function TestsContainer({ currentResponse }) {
- return (
- currentResponse.response &&
- currentResponse.response.testResult &&
- currentResponse.response.testResult.length > 0 ?
-
- :
- );
-}
+import React from 'react';
+import EmptyState from '../display/EmptyState';
+import SingleTestContainer from './SingleTestContainer';
+
+function TestsContainer({ currentResponse }) {
+ return currentResponse.response &&
+ currentResponse.response.testResult &&
+ currentResponse.response.testResult.length > 0 ? (
+
+ ) : (
+
+ );
+}
+
+export default TestsContainer;
diff --git a/src/client/components/containers/UpdatePopUpContainer.jsx b/src/client/components/containers/UpdatePopUpContainer.jsx
index 12b208038..fb82498ef 100644
--- a/src/client/components/containers/UpdatePopUpContainer.jsx
+++ b/src/client/components/containers/UpdatePopUpContainer.jsx
@@ -1,43 +1,47 @@
-import React, { useState, useEffect } from "react";
-import { connect } from "react-redux";
-
-const { api } = window;
-
-const UpdatePopUpContainer = ({ message, setMessage }) => {
-
- useEffect(()=>{
- api.receive("message", (e, text) => {
- console.log("AUTO-UPDATER STATUS: " + e);
- if (text) setMessage(text);
- });
- });
-
- const handleUpdateClick = () => {
- api.send("quit-and-install");
- setMessage(null);
- };
-
- return message ? (
-
- {message}
- {message === "Update downloaded." && (
- <>
-
- Do you want to restart and install now? (If not, will
- auto-install on restart.)
-
- >
- )}
- setMessage(null)}>
- Dismiss
-
- {message === "Update downloaded." && (
-
- Update
-
- )}
-
- ) : null;
-};
-
-export default UpdatePopUpContainer;
+import React, { useEffect } from 'react';
+
+const { api } = window;
+
+const UpdatePopUpContainer = ({ message, setMessage }) => {
+ useEffect(() => {
+ api.receive('message', (e, text) => {
+ console.log('AUTO-UPDATER STATUS: ' + e);
+ if (text) setMessage(text);
+ });
+ });
+
+ const handleUpdateClick = () => {
+ api.send('quit-and-install');
+ setMessage(null);
+ };
+
+ return message ? (
+
+ {message}
+ {message === 'Update downloaded.' && (
+ <>
+
+ Do you want to restart and install now? (If not, will auto-install
+ on restart.)
+
+ >
+ )}
+ setMessage(null)}
+ >
+ Dismiss
+
+ {message === 'Update downloaded.' && (
+
+ Update
+
+ )}
+
+ ) : null;
+};
+
+export default UpdatePopUpContainer;
diff --git a/src/client/components/containers/ViewRequestDetails.jsx b/src/client/components/containers/ViewRequestDetails.jsx
deleted file mode 100644
index a8951c9a8..000000000
--- a/src/client/components/containers/ViewRequestDetails.jsx
+++ /dev/null
@@ -1,6 +0,0 @@
-//AL: what was the purpose of this one?
-import React from "react";
-
-export default function ViewRequestDetails() {
- return
;
-}
diff --git a/src/client/components/containers/WorkspaceContainer.jsx b/src/client/components/containers/WorkspaceContainer.jsx
index 98c21dba6..584acb2bf 100644
--- a/src/client/components/containers/WorkspaceContainer.jsx
+++ b/src/client/components/containers/WorkspaceContainer.jsx
@@ -1,44 +1,44 @@
-import React, { useState } from "react";
-
-import ReqResCtrl from "../../controllers/reqResController.js";
-import SaveWorkspaceModal from "./SaveWorkspaceModal";
-import ReqResContainer from "./ReqResContainer.jsx";
-
-export default function WorkspaceContainer() {
- // LOCAL STATE HOOKS
- const [showModal, setShowModal] = useState(false);
-
- return (
-
- {/* NAV BAR */}
-
- {
- ReqResCtrl.clearAllReqRes();
- ReqResCtrl.clearAllGraph();
- }}
- >
- Clear Workspace
-
-
- {
- setShowModal(true);
- }}
- >
- Save Workspace
-
-
-
-
- {/* REQUEST CARDS */}
-
-
- );
-}
+import React, { useState } from 'react';
+import ReqResCtrl from '../../controllers/reqResController.js';
+import ReqResContainer from './ReqResContainer.jsx';
+import SaveWorkspaceModal from './SaveWorkspaceModal';
+
+function WorkspaceContainer() {
+ const [showModal, setShowModal] = useState(false);
+
+ return (
+
+ {/* NAV BAR */}
+
+ {
+ ReqResCtrl.clearAllReqRes();
+ ReqResCtrl.clearAllGraph();
+ }}
+ >
+ Clear Workspace
+
+
+ {
+ setShowModal(true);
+ }}
+ >
+ Save Workspace
+
+
+
+
+ {/* REQUEST CARDS */}
+
+
+ );
+}
+
+export default WorkspaceContainer;
diff --git a/src/client/components/display/BarGraph.jsx b/src/client/components/display/BarGraph.jsx
index 469e5bd16..a30cddbbb 100644
--- a/src/client/components/display/BarGraph.jsx
+++ b/src/client/components/display/BarGraph.jsx
@@ -1,220 +1,221 @@
-import React, { useState, useEffect } from "react";
-import { connect } from "react-redux";
-import { HorizontalBar, Line } from "react-chartjs-2";
-import * as store from "../../store";
-import * as actions from "../../actions/actions";
-
-//necessary for graph styling due to CSP
-Chart.platform.disableCSSInjection = true;
-
-const mapStateToProps = (store) => ({
- dataPoints: store.business.dataPoints,
- currentResponse: store.business.currentResponse,
-});
-
-const mapDispatchToProps = (dispatch) => ({
- saveCurrentResponseData: (reqRes) => {
- dispatch(actions.saveCurrentResponseData(reqRes, "bargraph"));
- },
- updateGraph: (reqRes) => {
- dispatch(actions.updateGraph(reqRes));
- },
- clearGraph: (id) => {
- store.default.dispatch(actions.clearGraph(id));
- },
-});
-
-const BarGraph = (props) => {
- const { dataPoints, currentResponse } = props;
-
- const [chartURL, setChartURL] = useState("");
- const [host, setHost] = useState(null);
-
- //state for showing graph, depending on whether there are datapoints or not.
- //must default to true, because graph will not render if initial container's display is 'none'
- const [show, toggleShow] = useState(true);
- //Default state for chart data
- const [chartData, updateChart] = useState({
- labels: [],
- datasets: [
- {
- data: [],
- },
- ],
- });
-
- //default state for chart options
- const [chartOptions, updateOptions] = useState({
- scales: {
- yAxes: [
- {
- scaleLabel: {
- display: true,
- labelString: chartURL,
- },
- ticks: {
- beginAtZero: true,
- },
- },
- ],
- xAxes: [
- {
- ticks: {
- beginAtZero: true,
- },
- },
- ],
- },
- // animation: {
- // duration: 50, //buggy animation, get rid of transition
- // },
- maintainAspectRatio: false,
- });
-
- //helper function that returns chart data object
- const dataUpdater = (labelArr, timesArr, BGsArr, bordersArr, reqResArr) => {
- return {
- labels: labelArr,
- datasets: [
- {
- label: "Response Time",
- data: timesArr,
- backgroundColor: BGsArr,
- borderColor: bordersArr,
- borderWidth: 1,
- fill: false,
- lineTension: 0,
- maxBarThickness: 300,
- reqRes: reqResArr,
- },
- ],
- };
- };
-
- //helper function that returns chart options object
- const optionsUpdater = (arr) => {
- return {
- scales: {
- yAxes: [
- {
- scaleLabel: {
- display: true, //boolean
- labelString: chartURL,
- },
- ticks: {
- beginAtZero: true,
- },
- },
- ],
- xAxes: [
- {
- // scaleLabel: {
- // display: true,
- // },
- },
- ],
- },
- // animation: {
- // duration: 50,
- // },
- maintainAspectRatio: false, //necessary for keeping chart within container
- };
- };
-
- // click handling to load response data in the response panel
- const getElementAtEvent = (element) => {
- if (!element.length) return;
- // get the response data corresponding to the clicked element
- const index = element[0]._index;
- const reqResToSend =
- element[0]._chart.config.data.datasets[0].reqRes[index];
- // send the data to the response panel
- props.saveCurrentResponseData(reqResToSend);
- };
-
- useEffect(() => {
- const { id, host } = currentResponse;
- setHost(host?.slice(7));
-
- let url;
- let urls;
- let times;
- let BGs;
- let borders;
- let reqResObjs;
- if (dataPoints[id]?.length) {
- const point = dataPoints[id][0];
- // // if grpc, just return the server IP
- if (point.reqRes.gRPC) url = `${point.url}`;
- // if point.url is lengthy, just return the domain and the end of the uri string
- const domain = point.url
- .replace("http://", "")
- .replace("https://", "")
- .split(/[/?#]/)[0];
- url = `${domain} ${
- point.url.length > domain.length + 8
- ? `- ..${point.url.slice(point.url.length - 8, point.url.length)}`
- : ""
- }`;
-
- setChartURL(url);
-
- //extract arrays from data point properties to be used in chart data/options that take separate arrays
- urls = dataPoints[id].map((point, index) => index + 1);
- times = dataPoints[id].map(
- (point) => point.timeReceived - point.timeSent
- );
- BGs = dataPoints[id].map((point) => "rgba(" + point.color + ", 0.2)");
- borders = dataPoints[id].map((point) => "rgba(" + point.color + ", 1)");
- reqResObjs = dataPoints[id].map((point) => point.reqRes);
- //show graph upon receiving data points
- toggleShow(true);
- } else {
- setHost(null);
- //hide graph when no data points
- toggleShow(false);
- }
- //update state with updated dataset
- updateChart(dataUpdater(urls, times, BGs, borders, reqResObjs));
- //conditionally update options based on length of dataPoints array
-
- updateOptions(optionsUpdater(dataPoints[id]));
- }, [dataPoints, currentResponse, chartURL]);
-
- // useEffect(updateGraph(currentResponse), [currentResponse])
-
- const chartClass = show ? "chart" : "chart-closed";
-
- return (
-
-
-
-
-
- {
- props.clearGraph(currentResponse.id);
- setChartURL("");
- }}
- >
- Clear Response History
- {host && (
-
- {" "}
- - {host}
-
- )}
-
-
-
- );
-};
-
-export default connect(mapStateToProps, mapDispatchToProps)(BarGraph);
+/* eslint-disable react-hooks/exhaustive-deps */
+import React, { useState, useEffect } from 'react';
+import { connect } from 'react-redux';
+import { Line } from 'react-chartjs-2';
+import * as store from '../../store';
+import * as actions from '../../actions/actions';
+
+//necessary for graph styling due to CSP
+Chart.platform.disableCSSInjection = true;
+
+const mapStateToProps = (store) => ({
+ dataPoints: store.business.dataPoints,
+ currentResponse: store.business.currentResponse,
+});
+
+const mapDispatchToProps = (dispatch) => ({
+ saveCurrentResponseData: (reqRes) => {
+ dispatch(actions.saveCurrentResponseData(reqRes, 'bargraph'));
+ },
+ updateGraph: (reqRes) => {
+ dispatch(actions.updateGraph(reqRes));
+ },
+ clearGraph: (id) => {
+ store.default.dispatch(actions.clearGraph(id));
+ },
+});
+
+const BarGraph = (props) => {
+ const { dataPoints, currentResponse } = props;
+
+ const [chartURL, setChartURL] = useState('');
+ const [host, setHost] = useState(null);
+
+ //state for showing graph, depending on whether there are data points or not.
+ //must default to true, because graph will not render if initial container's display is 'none'
+ const [show, toggleShow] = useState(true);
+ //Default state for chart data
+ const [chartData, updateChart] = useState({
+ labels: [],
+ datasets: [
+ {
+ data: [],
+ },
+ ],
+ });
+
+ //default state for chart options
+ const [chartOptions, updateOptions] = useState({
+ scales: {
+ yAxes: [
+ {
+ scaleLabel: {
+ display: true,
+ labelString: chartURL,
+ },
+ ticks: {
+ beginAtZero: true,
+ },
+ },
+ ],
+ xAxes: [
+ {
+ ticks: {
+ beginAtZero: true,
+ },
+ },
+ ],
+ },
+ // animation: {
+ // duration: 50, //buggy animation, get rid of transition
+ // },
+ maintainAspectRatio: false,
+ });
+
+ //helper function that returns chart data object
+ const dataUpdater = (labelArr, timesArr, BGsArr, bordersArr, reqResArr) => {
+ return {
+ labels: labelArr,
+ datasets: [
+ {
+ label: 'Response Time',
+ data: timesArr,
+ backgroundColor: BGsArr,
+ borderColor: bordersArr,
+ borderWidth: 1,
+ fill: false,
+ lineTension: 0,
+ maxBarThickness: 300,
+ reqRes: reqResArr,
+ },
+ ],
+ };
+ };
+
+ //helper function that returns chart options object
+ const optionsUpdater = (arr) => {
+ return {
+ scales: {
+ yAxes: [
+ {
+ scaleLabel: {
+ display: true, //boolean
+ labelString: chartURL,
+ },
+ ticks: {
+ beginAtZero: true,
+ },
+ },
+ ],
+ xAxes: [
+ {
+ // scaleLabel: {
+ // display: true,
+ // },
+ },
+ ],
+ },
+ // animation: {
+ // duration: 50,
+ // },
+ maintainAspectRatio: false, //necessary for keeping chart within container
+ };
+ };
+
+ // click handling to load response data in the response panel
+ const getElementAtEvent = (element) => {
+ if (!element.length) return;
+ // get the response data corresponding to the clicked element
+ const index = element[0]._index;
+ const reqResToSend =
+ element[0]._chart.config.data.datasets[0].reqRes[index];
+ // send the data to the response panel
+ props.saveCurrentResponseData(reqResToSend);
+ };
+
+ useEffect(() => {
+ const { id, host } = currentResponse;
+ setHost(host?.slice(7));
+
+ let url;
+ let urls;
+ let times;
+ let BGs;
+ let borders;
+ let reqResObjs;
+ if (dataPoints[id]?.length) {
+ const point = dataPoints[id][0];
+ // if grpc, just return the server IP
+ if (point.reqRes.gRPC) url = `${point.url}`;
+ // if point.url is lengthy, just return the domain and the end of the uri string
+ const domain = point.url
+ .replace('http://', '')
+ .replace('https://', '')
+ .split(/[/?#]/)[0];
+ url = `${domain} ${
+ point.url.length > domain.length + 8
+ ? `- ..${point.url.slice(point.url.length - 8, point.url.length)}`
+ : ''
+ }`;
+
+ setChartURL(url);
+
+ //extract arrays from data point properties to be used in chart data/options that take separate arrays
+ urls = dataPoints[id].map((point, index) => index + 1);
+ times = dataPoints[id].map(
+ (point) => point.timeReceived - point.timeSent
+ );
+ BGs = dataPoints[id].map((point) => 'rgba(' + point.color + ', 0.2)');
+ borders = dataPoints[id].map((point) => 'rgba(' + point.color + ', 1)');
+ reqResObjs = dataPoints[id].map((point) => point.reqRes);
+ //show graph upon receiving data points
+ toggleShow(true);
+ } else {
+ setHost(null);
+ //hide graph when no data points
+ toggleShow(false);
+ }
+ //update state with updated dataset
+ updateChart(dataUpdater(urls, times, BGs, borders, reqResObjs));
+ //conditionally update options based on length of dataPoints array
+
+ updateOptions(optionsUpdater(dataPoints[id]));
+ }, [dataPoints, currentResponse, chartURL, optionsUpdater]);
+
+ // useEffect(updateGraph(currentResponse), [currentResponse])
+
+ const chartClass = show ? 'chart' : 'chart-closed';
+
+ return (
+
+
+
+
+
+ {
+ props.clearGraph(currentResponse.id);
+ setChartURL('');
+ }}
+ >
+ Clear Response History
+ {host && (
+
+ {' '}
+ - {host}
+
+ )}
+
+
+
+ );
+};
+
+export default connect(mapStateToProps, mapDispatchToProps)(BarGraph);
diff --git a/src/client/components/display/ClearHistoryBtn.jsx b/src/client/components/display/ClearHistoryBtn.jsx
index a181e28c4..36d978f7b 100644
--- a/src/client/components/display/ClearHistoryBtn.jsx
+++ b/src/client/components/display/ClearHistoryBtn.jsx
@@ -1,31 +1,32 @@
-import React, { useEffect } from "react";
-import historyController from "../../controllers/historyController";
-
-// utilizing API we created in preload.js for node-free IPC communication
-const { api } = window;
-
-const ClearHistoryBtn = (props) => {
- // cleanup api.receive event listener on dismount
- useEffect(() => {
- api.receive("clear-history-response", (res) => {
- // a response of 0 from main means user has selected 'confirm'
- if (res.response === 0) {
- historyController.clearHistoryFromIndexedDb();
- props.clearHistory();
- }
- });
- }, []);
-
- const handleClick = () => {
- api.send("confirm-clear-history");
- };
- return (
-
- Clear History
- );
-};
-
-export default ClearHistoryBtn;
+import React, { useEffect } from 'react';
+import historyController from '../../controllers/historyController';
+
+// utilizing API we created in preload.js for node-free IPC communication
+const { api } = window;
+
+const ClearHistoryBtn = (props) => {
+ // cleanup api.receive event listener on dismount
+ useEffect(() => {
+ api.receive('clear-history-response', (res) => {
+ // a response of 0 from main means user has selected 'confirm'
+ if (res.response === 0) {
+ historyController.clearHistoryFromIndexedDb();
+ props.clearHistory();
+ }
+ });
+ }, []);
+
+ const handleClick = () => {
+ api.send('confirm-clear-history');
+ };
+ return (
+
+ Clear History
+
+ );
+};
+
+export default ClearHistoryBtn;
diff --git a/src/client/components/display/CloseBtn.jsx b/src/client/components/display/CloseBtn.jsx
deleted file mode 100644
index cf569c9e9..000000000
--- a/src/client/components/display/CloseBtn.jsx
+++ /dev/null
@@ -1,17 +0,0 @@
-import React from 'react';
-import ReqResCtrl from '../../controllers/reqResController';
-
-const CloseBtn = ({ stylesObj, content }) => {
- return (
-
ReqResCtrl.closeReqRes(content)}
- >
- Close
-
- )
-};
-
-export default CloseBtn;
diff --git a/src/client/components/display/Collection.jsx b/src/client/components/display/Collection.jsx
index 7a3065a4d..c118b0bbf 100644
--- a/src/client/components/display/Collection.jsx
+++ b/src/client/components/display/Collection.jsx
@@ -1,44 +1,54 @@
-import React from 'react';
-import { useDispatch } from 'react-redux';
-import * as actions from '../../actions/actions';
-import collectionsController from '../../controllers/collectionsController';
-
-const Collection = (props) => {
- const dispatch = useDispatch();
- const setWorkspaceTab = (tabName) => dispatch(actions.setWorkspaceActiveTab(tabName));
-
- const addCollectionToReqResContainer = () => {
- props.collectionToReqRes(props.content.reqResArray);
- setWorkspaceTab('workspace');
- }
-
- const deleteCollection = (e) => {
- props.deleteFromCollection(props.content); //a function we need to make in the container
- collectionsController.deleteCollectionFromIndexedDb(e.target.id);
- }
-
- return (
-
-
-
-
- {props.content.name}
-
-
-
collectionsController.exportCollection(props.content.id)}>
- Export
-
-
-
-
-
-
-
-
- );
-}
-
-export default Collection;
\ No newline at end of file
+/* eslint-disable jsx-a11y/no-static-element-interactions */
+/* eslint-disable jsx-a11y/click-events-have-key-events */
+import React from 'react';
+import { useDispatch } from 'react-redux';
+import * as actions from '../../actions/actions';
+import collectionsController from '../../controllers/collectionsController';
+
+const Collection = (props) => {
+ const dispatch = useDispatch();
+ const setWorkspaceTab = (tabName) =>
+ dispatch(actions.setWorkspaceActiveTab(tabName));
+
+ const addCollectionToReqResContainer = () => {
+ props.collectionToReqRes(props.content.reqResArray);
+ setWorkspaceTab('workspace');
+ };
+
+ const deleteCollection = (e) => {
+ props.deleteFromCollection(props.content);
+ collectionsController.deleteCollectionFromIndexedDb(e.target.id);
+ };
+
+ return (
+
+
+
+ {props.content.name}
+
+
+
+ collectionsController.exportCollection(props.content.id)
+ }
+ >
+ Export
+
+
+
+
+
+
+
+ );
+};
+
+export default Collection;
diff --git a/src/client/components/display/ContentReqRow.jsx b/src/client/components/display/ContentReqRow.jsx
index 446d67335..6deff7d88 100644
--- a/src/client/components/display/ContentReqRow.jsx
+++ b/src/client/components/display/ContentReqRow.jsx
@@ -1,10 +1,24 @@
-import React from 'react'
-
-export default function ContentReqRow({ data }) {
- return (
-
-
-
-
- )
-}
+import React from 'react';
+
+function ContentReqRow({ data }) {
+ return (
+
+
+
+
+ );
+}
+
+export default ContentReqRow;
diff --git a/src/client/components/display/CookieTable.tsx b/src/client/components/display/CookieTable.tsx
deleted file mode 100644
index 33a88c91b..000000000
--- a/src/client/components/display/CookieTable.tsx
+++ /dev/null
@@ -1,32 +0,0 @@
-import React from "react";
-import CookieTableRow from "./CookieTableRow";
-import { CookieProps } from "../../../types";
-
-const CookieTable = ({ cookies }: CookieProps) => {
- let cookieRowArray: Array
;
- if (Array.isArray(cookies)) {
- cookieRowArray = cookies.map((cookie, i) => {
- return (
-
- );
- });
- }
- return(
-
-
-
Name
-
ExpirationDate
-
Domain
-
HostOnly
-
Path
-
Secure
-
HttpOnly
-
Session
-
Value
-
- {cookieRowArray}
-
- )
-}
-
-export default CookieTable;
diff --git a/src/client/components/display/CookieTableCell.tsx b/src/client/components/display/CookieTableCell.tsx
deleted file mode 100644
index dc7935c64..000000000
--- a/src/client/components/display/CookieTableCell.tsx
+++ /dev/null
@@ -1,10 +0,0 @@
-import * as React from "react";
-import { CookieProps } from "../../../types";
-
-const CookieTableCell = ({ detail }: CookieProps) => {
- return (
- {detail.toString()}
- );
-}
-
-export default CookieTableCell;
diff --git a/src/client/components/display/CookieTableRow.jsx b/src/client/components/display/CookieTableRow.jsx
deleted file mode 100644
index 0f9c92b7b..000000000
--- a/src/client/components/display/CookieTableRow.jsx
+++ /dev/null
@@ -1,20 +0,0 @@
-import * as React from "react";
-import CookieTableCell from "./CookieTableCell";
-
-const CookieTableRow = ({ cookies }) => {
-
- const tableCellArray = [];
- for (const key in cookies) {
- tableCellArray.push(
-
- );
- }
- if (!cookies.expirationDate) {
- tableCellArray.push( );
- }
- return (
- {tableCellArray}
- )
-}
-
-export default CookieTableRow;
diff --git a/src/client/components/display/EmptyState.jsx b/src/client/components/display/EmptyState.jsx
index acf3a02c9..be6aca3b4 100644
--- a/src/client/components/display/EmptyState.jsx
+++ b/src/client/components/display/EmptyState.jsx
@@ -1,12 +1,12 @@
-import React from 'react'
-import logofaded from '../../../assets/img/swell-logo-faded.png'
-
-function EmptyState({ connection }) {
- return (
-
-
-
- )
-}
-
-export default EmptyState
+import React from 'react';
+import logofaded from '../../../assets/img/swell-logo-faded.png';
+
+function EmptyState({ connection }) {
+ return (
+
+
+
+ );
+}
+
+export default EmptyState;
diff --git a/src/client/components/display/EventPreview.jsx b/src/client/components/display/EventPreview.jsx
index f5d07bd01..3961aab38 100644
--- a/src/client/components/display/EventPreview.jsx
+++ b/src/client/components/display/EventPreview.jsx
@@ -1,63 +1,60 @@
-import React, { useState } from "react";
-import dropDownArrow from "../../../assets/icons/caret-down-tests.svg";
-import dropDownArrowUp from "../../../assets/icons/caret-up-tests.svg";
-
-const EventPreview = ({ contents }) => {
-
- const [showPreview, setShowPreview] = useState(false);
- const handleShowPreview = () => setShowPreview(!showPreview);
-
- return (
-
-
{
- handleShowPreview();
- }}
- >
- {showPreview === true &&
- <>
-
Hide Preview
-
-
-
- >
- }
- {showPreview === false &&
- <>
-
View Preview
-
-
-
- >
- }
-
- {showPreview === true &&
-
-
-
- }
-
- )
- }
-
- export default EventPreview;
\ No newline at end of file
+/* eslint-disable jsx-a11y/no-static-element-interactions */
+/* eslint-disable jsx-a11y/click-events-have-key-events */
+import React, { useState } from 'react';
+import dropDownArrow from '../../../assets/icons/caret-down-tests.svg';
+import dropDownArrowUp from '../../../assets/icons/caret-up-tests.svg';
+
+const EventPreview = ({ contents }) => {
+ const [showPreview, setShowPreview] = useState(false);
+ const handleShowPreview = () => setShowPreview(!showPreview);
+
+ return (
+
+
{
+ handleShowPreview();
+ }}
+ >
+ {showPreview === true && (
+ <>
+
Hide Preview
+
+
+
+ >
+ )}
+ {showPreview === false && (
+ <>
+
View Preview
+
+
+
+ >
+ )}
+
+ {showPreview === true && (
+
+
+
+ )}
+
+ );
+};
+
+export default EventPreview;
diff --git a/src/client/components/display/GRPCRequestContent.jsx b/src/client/components/display/GRPCRequestContent.jsx
index 5d38957ad..b3ab6ac0a 100644
--- a/src/client/components/display/GRPCRequestContent.jsx
+++ b/src/client/components/display/GRPCRequestContent.jsx
@@ -1,67 +1,79 @@
-import React from "react";
-import {UnControlled as CodeMirror} from 'react-codemirror2';
-
-import ContentReqRow from './ContentReqRow';
-
-export default function GRPCRequestContent({ request, rpc, service, servicesObj }) {
-
- const {
- headers, // refers to meta-data in a GRPC request
- body, // "body Content text"
- rawType,
- testContent
- } = request;
-
- // CREATE META-DATA COMPONENTS
- const metadataRows = headers.map((header, index) => );
-
- return (
-
- {/* REQUEST DETAILS */}
-
- {/* METADATA */}
- {metadataRows.length > 0 &&
-
Metadata
- }
- {metadataRows}
- {/* REQUEST / SERVICE */}
-
Service / Request
-
-
-
-
- {/* BODY */}
-
- {testContent.length > 0 &&
-
- }
-
-
- )
-}
\ No newline at end of file
+import React from 'react';
+import { UnControlled as CodeMirror } from 'react-codemirror2';
+import ContentReqRow from './ContentReqRow';
+
+function GRPCRequestContent({ request, rpc, service, servicesObj }) {
+ const {
+ headers, // refers to meta-data in a GRPC request
+ body, // "body Content text"
+ rawType,
+ testContent,
+ } = request;
+
+ // CREATE META-DATA COMPONENTS
+ const metadataRows = headers.map((header, index) => (
+
+ ));
+
+ return (
+
+ {/* REQUEST DETAILS */}
+
+ {/* METADATA */}
+ {metadataRows.length > 0 &&
Metadata
}
+ {metadataRows}
+ {/* REQUEST / SERVICE */}
+
Service / Request
+
+
+
+
+ {/* BODY */}
+
+ {testContent.length > 0 && (
+
+ )}
+
+
+ );
+}
+
+export default GRPCRequestContent;
diff --git a/src/client/components/display/GraphQLRequestContent.jsx b/src/client/components/display/GraphQLRequestContent.jsx
index 7854cfd6b..32f1efa98 100644
--- a/src/client/components/display/GraphQLRequestContent.jsx
+++ b/src/client/components/display/GraphQLRequestContent.jsx
@@ -1,97 +1,90 @@
-import React from "react";
-import {UnControlled as CodeMirror} from 'react-codemirror2';
-
-import ContentReqRow from './ContentReqRow';
-
-
-export default function GraphQLRequestContent({ request }) {
-
- // ORGANIZE PROPS
- const {
- headers,
- cookies,
- body,
- bodyVariables,
- testContent
- } = request;
-
- // CREATE HEADER COMPONENTS
- const headerRows = headers.map((header, index) => );
-
- // CREATE COOKIE COMPONENTS
- const cookieRows = cookies.map((cookie, index) => );
-
- // PRETTY-PRINT JSON IN BODY
- const bodyText = body;
- // const bodyText = ( JSON.stringify( JSON.parse(body), null, 4 ) );
- // PRETTY-PRINT JSON IN VARIABLES
- const bodyVarText = bodyVariables;
- // const bodyVarText = ( JSON.stringify( JSON.parse(bodyVariables), null, 4 ) );
-
- return (
-
- {/* REQUEST DETAILS */}
-
- {/* HEADERS */}
- {headerRows.length > 0 &&
-
Headers
- }
- {headerRows}
- {/* COOKIES */}
- {cookieRows.length > 0 &&
-
Cookies
- }
- {cookieRows}
- {/* BODY */}
-
- {/* VARIABLES */}
- {bodyVariables.length > 0 &&
-
- }
- {/* TEST DATA */}
- {testContent.length > 0 &&
-
- }
-
-
- )
-}
\ No newline at end of file
+import React from 'react';
+import { UnControlled as CodeMirror } from 'react-codemirror2';
+
+import ContentReqRow from './ContentReqRow';
+
+function GraphQLRequestContent({ request }) {
+ // ORGANIZE PROPS
+ const { headers, cookies, body, bodyVariables, testContent } = request;
+
+ // CREATE HEADER COMPONENTS
+ const headerRows = headers.map((header, index) => (
+
+ ));
+
+ // CREATE COOKIE COMPONENTS
+ const cookieRows = cookies.map((cookie, index) => (
+
+ ));
+
+ // PRETTY-PRINT JSON IN BODY
+ const bodyText = body;
+
+ // PRETTY-PRINT JSON IN VARIABLES
+ const bodyVarText = bodyVariables;
+
+ return (
+
+ {/* REQUEST DETAILS */}
+
+ {/* HEADERS */}
+ {headerRows.length > 0 &&
Headers
}
+ {headerRows}
+ {/* COOKIES */}
+ {cookieRows.length > 0 &&
Cookies
}
+ {cookieRows}
+ {/* BODY */}
+
+ {/* VARIABLES */}
+ {bodyVariables.length > 0 && (
+
+ )}
+ {/* TEST DATA */}
+ {testContent.length > 0 && (
+
+ )}
+
+
+ );
+}
+
+export default GraphQLRequestContent;
diff --git a/src/client/components/display/History.jsx b/src/client/components/display/History.jsx
index 8214d9a7b..bf4a6bd7c 100644
--- a/src/client/components/display/History.jsx
+++ b/src/client/components/display/History.jsx
@@ -1,166 +1,257 @@
-/* eslint-disable jsx-a11y/click-events-have-key-events */
-/* eslint-disable jsx-a11y/no-static-element-interactions */
-import React, { Component } from 'react';
-import { useDispatch } from 'react-redux';
-import * as actions from '../../actions/actions';
-import historyController from '../../controllers/historyController';
-
-const History = ({ newRequestFields, content, content: { request : { method, isSSE,
- headers, cookies, bodyType, body, bodyVariables, rawType, JSONFormatted, network,
- restUrl, wsUrl, gqlUrl, grpcUrl }, protocol, url, graphQL, gRPC, streamsArr,
- streamContent, queryArr, packageName, rpc, service, initialQuery, protoPath,
- servicesObj, protoContent }, setNewRequestFields, setNewRequestHeaders, setNewRequestCookies,
- setNewRequestBody, setNewRequestStreams, deleteFromHistory, focusOnForm }) => {
-
- const dispatch = useDispatch();
- const setSidebarTab = (tabName) => dispatch(actions.setSidebarActiveTab(tabName));
- const setNewRequestSSE = (bool) => dispatch(actions.setNewRequestSSE(bool));
-
- const addHistoryToNewRequest = () => {
- let requestFieldObj = {};
- if (network === 'rest') {
- requestFieldObj = {
- ...newRequestFields,
- isSSE: isSSE || false,
- method: method || 'GET',
- protocol: protocol || 'http://',
- url,
- restUrl,
- graphQL: graphQL || false,
- gRPC: gRPC || false,
- network,
- testContent: content.request.testContent,
- }
- };
- if (network === 'ws') {
- requestFieldObj = {
- ...newRequestFields,
- method: method || 'GET',
- protocol: protocol || 'http://',
- url,
- wsUrl,
- graphQL: graphQL || false,
- gRPC: gRPC || false,
- network,
- }
- };
- if (network === 'graphQL') {
- requestFieldObj = {
- ...newRequestFields,
- method: method || 'GET',
- protocol: protocol || 'http://',
- url,
- gqlUrl,
- graphQL: graphQL || false,
- gRPC: gRPC || false,
- network,
- }
- };
- if (network === 'grpc') {
- requestFieldObj = {
- ...newRequestFields,
- method: method || 'GET',
- protocol: protocol || 'http://',
- url,
- grpcUrl,
- graphQL: graphQL || false,
- gRPC: gRPC || false,
- network,
- }
- };
- let headerDeeperCopy;
- if (headers) {
- headerDeeperCopy = JSON.parse(JSON.stringify(headers));
- headerDeeperCopy.push({
- id: headers.length + 1,
- active: false,
- key: '',
- value: '',
- })
- }
- let cookieDeeperCopy;
- if (cookies && !/ws/.test(protocol)) {
- cookieDeeperCopy = JSON.parse(JSON.stringify(cookies));
- cookieDeeperCopy.push({
- id: cookies.length + 1,
- active: false,
- key: '',
- value: '',
- })
- }
- const requestHeadersObj = {
- headersArr: headerDeeperCopy || [],
- count: headerDeeperCopy ? headerDeeperCopy.length : 1,
- }
- const requestCookiesObj = {
- cookiesArr: cookieDeeperCopy || [],
- count: cookieDeeperCopy ? cookieDeeperCopy.length : 1,
- }
- const requestBodyObj = {
- bodyType: bodyType || 'raw',
- bodyContent: body || '',
- bodyVariables: bodyVariables || '',
- rawType: rawType || 'Text (text/plain)',
- JSONFormatted: JSONFormatted || true,
- bodyIsNew: false,
- }
- setNewRequestFields(requestFieldObj);
- setNewRequestHeaders(requestHeadersObj);
- setNewRequestCookies(requestCookiesObj);
- setNewRequestBody(requestBodyObj);
- setNewRequestSSE(isSSE);
- // for gRPC
- if (content && gRPC) {
- const streamsDeepCopy = JSON.parse(JSON.stringify(streamsArr));
- const contentsDeepCopy = JSON.parse(JSON.stringify(streamContent));
- // construct the streams obj from passed in history content & set state in store
- const requestStreamsObj = {
- streamsArr: streamsDeepCopy,
- count: queryArr.length,
- streamContent: contentsDeepCopy,
- selectedPackage: packageName,
- selectedRequest: rpc,
- selectedService: service,
- selectedStreamingType: method,
- initialQuery,
- queryArr,
- protoPath,
- services: servicesObj,
- protoContent,
- }
- setNewRequestStreams(requestStreamsObj)
- }
- setSidebarTab('composer');
- }
-
- let colorClass;
- switch (network) {
- case 'grpc': colorClass = 'is-grpc-color'; break;
- case 'rest': colorClass = 'is-rest-color'; break;
- case 'graphQL': colorClass = 'is-graphQL-color'; break;
- case 'ws': colorClass = 'is-ws-color'; break;
- }
-
- const deleteHistory = (e) => {
- deleteFromHistory(content);
- historyController.deleteHistoryFromIndexedDb(e.target.id);
- }
-
- const urlDisplay = url.length > 32 ? url.slice(0, 32) + '...' : url;
-
- return (
-
-
addHistoryToNewRequest()}>
-
{method}
-
{urlDisplay}
-
-
-
deleteHistory(e)} id={content.id} />
-
-
- );
-}
-
-
-
-export default History;
\ No newline at end of file
+/* eslint-disable default-case */
+/* eslint-disable jsx-a11y/click-events-have-key-events */
+/* eslint-disable jsx-a11y/no-static-element-interactions */
+import React from 'react';
+import { useDispatch } from 'react-redux';
+import * as actions from '../../actions/actions';
+import historyController from '../../controllers/historyController';
+
+const History = ({
+ newRequestFields,
+ newRequestsOpenAPI,
+ content,
+ content: {
+ request: {
+ method,
+ isSSE,
+ headers,
+ cookies,
+ bodyType,
+ body,
+ bodyVariables,
+ rawType,
+ JSONFormatted,
+ network,
+ restUrl,
+ wsUrl,
+ gqlUrl,
+ grpcUrl,
+ },
+ protocol,
+ url,
+ webrtcUrl,
+ graphQL,
+ gRPC,
+ webrtc,
+ streamsArr,
+ streamContent,
+ queryArr,
+ packageName,
+ rpc,
+ service,
+ initialQuery,
+ protoPath,
+ servicesObj,
+ protoContent,
+ },
+ setNewRequestFields,
+ setNewRequestHeaders,
+ setNewRequestCookies,
+ setNewRequestBody,
+ setNewRequestStreams,
+ deleteFromHistory,
+}) => {
+ const dispatch = useDispatch();
+ const setSidebarTab = (tabName) =>
+ dispatch(actions.setSidebarActiveTab(tabName));
+ const setNewRequestSSE = (bool) => dispatch(actions.setNewRequestSSE(bool));
+
+ const addHistoryToNewRequest = () => {
+ let requestFieldObj = {};
+ if (network === 'rest') {
+ requestFieldObj = {
+ ...newRequestFields,
+ isSSE: isSSE || false,
+ method: method || 'GET',
+ protocol: protocol || 'http://',
+ url,
+ restUrl,
+ graphQL: graphQL || false,
+ gRPC: gRPC || false,
+ webrtc: webrtc || false,
+ network,
+ testContent: content.request.testContent,
+ };
+ }
+ if (network === 'openapi') {
+ requestFieldObj = {
+ ...newRequestFields,
+ ...newRequestsOpenAPI,
+ isSSE: isSSE || false,
+ method: method || 'GET',
+ protocol: protocol || 'http://',
+ url,
+ restUrl,
+ graphQL: graphQL || false,
+ gRPC: gRPC || false,
+ webrtc: webrtc || false,
+ network,
+ };
+ }
+ if (network === 'ws') {
+ requestFieldObj = {
+ ...newRequestFields,
+ method: method || 'GET',
+ protocol: protocol || 'http://',
+ url,
+ wsUrl,
+ graphQL: graphQL || false,
+ gRPC: gRPC || false,
+ webrtc: webrtc || false,
+ network,
+ };
+ }
+ if (network === 'graphQL') {
+ requestFieldObj = {
+ ...newRequestFields,
+ method: method || 'GET',
+ protocol: protocol || 'http://',
+ url,
+ gqlUrl,
+ graphQL: graphQL || false,
+ gRPC: gRPC || false,
+ webrtc: webrtc || false,
+ network,
+ };
+ }
+ if (network === 'webrtc') {
+ requestFieldObj = {
+ ...newRequestFields,
+ method: method || 'GET',
+ protocol: protocol || 'http://',
+ url,
+ webrtcUrl,
+ grpcUrl,
+ graphQL: graphQL || false,
+ gRPC: gRPC || false,
+ webrtc: webrtc || false,
+ network,
+ };
+ }
+ if (network === 'grpc') {
+ requestFieldObj = {
+ ...newRequestFields,
+ method: method || 'GET',
+ protocol: protocol || 'http://',
+ url,
+ grpcUrl,
+ graphQL: graphQL || false,
+ gRPC: gRPC || false,
+ network,
+ };
+ }
+ let headerDeeperCopy;
+ if (headers) {
+ headerDeeperCopy = JSON.parse(JSON.stringify(headers));
+ headerDeeperCopy.push({
+ id: headers.length + 1,
+ active: false,
+ key: '',
+ value: '',
+ });
+ }
+ let cookieDeeperCopy;
+ if (cookies && !/ws/.test(protocol)) {
+ cookieDeeperCopy = JSON.parse(JSON.stringify(cookies));
+ cookieDeeperCopy.push({
+ id: cookies.length + 1,
+ active: false,
+ key: '',
+ value: '',
+ });
+ }
+ const requestHeadersObj = {
+ headersArr: headerDeeperCopy || [],
+ count: headerDeeperCopy ? headerDeeperCopy.length : 1,
+ };
+ const requestCookiesObj = {
+ cookiesArr: cookieDeeperCopy || [],
+ count: cookieDeeperCopy ? cookieDeeperCopy.length : 1,
+ };
+ const requestBodyObj = {
+ bodyType: bodyType || 'raw',
+ bodyContent: body || '',
+ bodyVariables: bodyVariables || '',
+ rawType: rawType || 'Text (text/plain)',
+ JSONFormatted: JSONFormatted || true,
+ bodyIsNew: false,
+ };
+ setNewRequestFields(requestFieldObj);
+ setNewRequestHeaders(requestHeadersObj);
+ setNewRequestCookies(requestCookiesObj);
+ setNewRequestBody(requestBodyObj);
+ setNewRequestSSE(isSSE);
+ // for gRPC
+ if (content && gRPC) {
+ const streamsDeepCopy = JSON.parse(JSON.stringify(streamsArr));
+ const contentsDeepCopy = JSON.parse(JSON.stringify(streamContent));
+ // construct the streams obj from passed in history content & set state in store
+ const requestStreamsObj = {
+ streamsArr: streamsDeepCopy,
+ count: queryArr.length,
+ streamContent: contentsDeepCopy,
+ selectedPackage: packageName,
+ selectedRequest: rpc,
+ selectedService: service,
+ selectedStreamingType: method,
+ initialQuery,
+ queryArr,
+ protoPath,
+ services: servicesObj,
+ protoContent,
+ };
+ setNewRequestStreams(requestStreamsObj);
+ }
+ setSidebarTab('composer');
+ };
+
+ let colorClass;
+ switch (network) {
+ case 'grpc':
+ colorClass = 'is-grpc-color';
+ break;
+ case 'rest':
+ colorClass = 'is-rest-color';
+ break;
+ case 'graphQL':
+ colorClass = 'is-graphQL-color';
+ break;
+ case 'ws':
+ colorClass = 'is-ws-color';
+ break;
+ case 'openapi':
+ colorClass = 'is-openapi-color';
+ break;
+ case 'webrtc':
+ colorClass = 'is-webrtc-color';
+ break;
+ }
+
+ const deleteHistory = (e) => {
+ deleteFromHistory(content);
+ historyController.deleteHistoryFromIndexedDb(e.target.id);
+ };
+
+ const urlDisplay = url && url.length > 32 ? url.slice(0, 32) + '...' : url;
+
+ return (
+
+
addHistoryToNewRequest()}
+ >
+
{method}
+
{urlDisplay || '-'}
+
+
+
deleteHistory(e)}
+ id={content.id}
+ />
+
+
+ );
+};
+
+export default History;
diff --git a/src/client/components/display/HistoryDate.jsx b/src/client/components/display/HistoryDate.jsx
index a1d7aace1..28b101f8f 100644
--- a/src/client/components/display/HistoryDate.jsx
+++ b/src/client/components/display/HistoryDate.jsx
@@ -1,62 +1,63 @@
-import React, { Component } from "react";
-import parse from "date-fns/parse";
-import isYesterday from "date-fns/is_yesterday";
-import isToday from "date-fns/is_today";
-import format from "date-fns/format";
-import History from "./History.jsx";
-
-class HistoryDate extends Component {
- constructor(props) {
- super(props);
- // this.state = {};
- this.focusOnForm = this.focusOnForm.bind(this);
- }
-
- focusOnForm(event) {
- const composerUrlField = document.querySelector(".composer_url_input");
- composerUrlField.focus();
- }
-
- render() {
- const current = this.props.history.find(
- (a) => a.date === this.props.content.date
- );
- let date = parse(current.date);
-
- if (isToday(date)) {
- date = "Today";
- } // If the date matches todays date render the word "Today"
- else if (isYesterday(date)) {
- date = "Yesterday";
- } else {
- date = format(date, "ddd, MMM D, YYYY");
- }
-
- const histArray = current.history.map((history, i) => {
- return (
-
- );
- });
-
- return (
-
-
{date}
- {histArray}
-
-
- );
- }
-}
-
-export default HistoryDate;
+import React, { Component } from 'react';
+import parse from 'date-fns/parse';
+import isYesterday from 'date-fns/is_yesterday';
+import isToday from 'date-fns/is_today';
+import format from 'date-fns/format';
+import History from './History.jsx';
+
+class HistoryDate extends Component {
+ constructor(props) {
+ super(props);
+ this.focusOnForm = this.focusOnForm.bind(this);
+ }
+
+ focusOnForm(event) {
+ const composerUrlField = document.querySelector('.composer_url_input');
+ composerUrlField.focus();
+ }
+
+ render() {
+ const current = this.props.history.find(
+ (a) => a.date === this.props.content.date
+ );
+ let date = parse(current.date);
+
+ if (isToday(date)) {
+ date = 'Today';
+ } // If the date matches todays date render the word "Today"
+ else if (isYesterday(date)) {
+ date = 'Yesterday';
+ } else {
+ date = format(date, 'ddd, MMM D, YYYY');
+ }
+
+ const histArray = current.history.map((history, i) => {
+ return (
+
+ );
+ });
+
+ return (
+
+
+ {date}
+
+ {histArray}
+
+
+ );
+ }
+}
+
+export default HistoryDate;
diff --git a/src/client/components/display/ImageDropzone.jsx b/src/client/components/display/ImageDropzone.jsx
index 283417ef5..49b701949 100644
--- a/src/client/components/display/ImageDropzone.jsx
+++ b/src/client/components/display/ImageDropzone.jsx
@@ -1,27 +1,28 @@
-import React from "react";
-import { useDropzone } from "react-dropzone";
-
-export default function ImageDropzone({ onFileChange }) {
- const { acceptedFiles, getRootProps, getInputProps } = useDropzone();
-
- const files = acceptedFiles.map((file) => (
-
image {file.name} uploaded
- ));
-
- return (
-
- );
-}
+/* eslint-disable react/jsx-props-no-spreading */
+import React from 'react';
+import { useDropzone } from 'react-dropzone';
+
+export default function ImageDropzone({ onFileChange }) {
+ const { acceptedFiles, getRootProps, getInputProps } = useDropzone();
+
+ const files = acceptedFiles.map((file) => (
+
image {file.name} uploaded
+ ));
+
+ return (
+
+ );
+}
diff --git a/src/client/components/display/OpenAPIRequestContent.jsx b/src/client/components/display/OpenAPIRequestContent.jsx
new file mode 100644
index 000000000..1b092b37b
--- /dev/null
+++ b/src/client/components/display/OpenAPIRequestContent.jsx
@@ -0,0 +1,6 @@
+import React from 'react';
+
+export default function OpenAPIRequestContent({ request, isHTTP2 }) {
+ console.log(request);
+ return
this is {request.network}
;
+}
diff --git a/src/client/components/display/OpenBtn.jsx b/src/client/components/display/OpenBtn.jsx
deleted file mode 100644
index 795af8678..000000000
--- a/src/client/components/display/OpenBtn.jsx
+++ /dev/null
@@ -1,18 +0,0 @@
-import React from "react";
-import ReqResCtrl from "../../controllers/reqResController";
-
-const OpenBtn = ({ stylesObj, content }) => {
- return (
-
ReqResCtrl.openReqRes(content.id)}
- >
- Send
-
- );
-}
-
-export default OpenBtn;
diff --git a/src/client/components/display/RequestTabs.jsx b/src/client/components/display/RequestTabs.jsx
deleted file mode 100644
index 24142e77d..000000000
--- a/src/client/components/display/RequestTabs.jsx
+++ /dev/null
@@ -1,145 +0,0 @@
-import React, { Component } from "react";
-import Tab from "./Tab.jsx";
-
-class RequestTabs extends Component {
- constructor(props) {
- super(props);
- this.state = {
- openTab: "",
- tabContentShown: []
- };
- this.handleTabSelect = this.handleTabSelect.bind(this);
- }
-
- handleTabSelect(val) {
- let headers = "Request Headers"
- if (this.props.requestContent.bodyType === 'GRPC') {
- headers = "Request Metadata"
- }
-
- switch (val) {
- case "Request Body":
- this.setState({
- openTab: val
- });
- break;
- case "Request Variables":
- this.setState({
- openTab: val
- });
- break;
- case "Request Cookies":
- this.setState({
- openTab: val
- });
- break;
- case headers:
- this.setState({
- openTab: val
- });
- break;
- default:
- }
- }
-
- componentDidMount() {
- this.handleTabSelect("Request Body");
- }
-
- render() {
- let body = "Request Body";
- let cookies = 'Request Cookies';
- let headers = "Request Headers";
- let variables = "Request Variables";
- let tabContentShown;
-
- if (this.props.requestContent.bodyType === 'GRPC') {
- headers = "Request Metadata"
- }
-
- // let displayQueries = this.props.requestContent.body;
- if (this.props.requestContent.bodyType === 'GRPC') {
- headers = "Request Metadata"
- }
- // displayQueries = '';
- // let length = this.props.requestContent.streams.length;
- // for (let i = 0; i < length; i += 1) {
- // if (i > 0) {
- // displayQueries += '\n\n'
- // }
- // let streamObj = this.props.requestContent.streams[i];
- // displayQueries += streamObj.query;
- // }
- // }
-
- if (this.state.openTab === "Request Body") {
- tabContentShown = !!this.props.requestContent.body
- ?
{this.props.requestContent.body}
- :
No Request Body
- }
-
- else if (this.state.openTab === "Request Variables") {
- tabContentShown = !!this.props.requestContent.bodyVariables
- ?
{this.props.requestContent.bodyVariables}
- :
No Request Variables
- }
-
- else if (this.state.openTab === headers) {
- tabContentShown = [];
- if (this.props.requestContent.headers && this.props.requestContent.headers.length > 0) {
- this.props.requestContent.headers.forEach((cur, idx) => {
- tabContentShown.push(
-
- {cur.key}
- {cur.value}
-
- );
- });
- }
- else {
- tabContentShown.push(
No {headers}
)
- }
- }
-
- else if (this.state.openTab === "Request Cookies") {
- tabContentShown = [];
- if (this.props.requestContent.cookies && this.props.requestContent.cookies.length > 0) {
- this.props.requestContent.cookies.forEach((cur, idx) => {
- tabContentShown.push(
-
- {cur.key}
- {cur.value}
-
- );
- });
- }
- else {
- tabContentShown.push(
No Request Cookies
)
- }
- }
-
- return (
-
-
-
-
- {
- this.props.requestContent.bodyType === "raw" &&
-
- }
- {
- this.props.requestContent.bodyType === "GQL" &&
-
- }
- {
- this.props.requestContent.bodyType === "GQL" &&
-
- }
-
-
{tabContentShown}
-
- );
- }
-}
-
-export default RequestTabs;
diff --git a/src/client/components/display/ResponseCookiesDisplay.jsx b/src/client/components/display/ResponseCookiesDisplay.jsx
deleted file mode 100644
index 27b2f1127..000000000
--- a/src/client/components/display/ResponseCookiesDisplay.jsx
+++ /dev/null
@@ -1,36 +0,0 @@
-import React, { Component } from 'react';
-import CookieTable from './CookieTable.tsx';
-
-class ResponseCookiesDisplay extends Component {
- constructor(props) {
- super(props);
- }
-
- render() {
- const displayContents = [];
-
- // Step 1 - Locate responses from store add them to cache array
- const responsesCache = [];
- responsesCache.push(this.props);
-
- // Step 2 - Increment across all responses in array
-
- responsesCache.forEach((cur, idx) => {
- if (!this.props.responseContent.cookies) {
- displayContents.push(
No Response Cookies
)
- return;
- }
- displayContents.push(
-
,
- );
- });
-
- return
{displayContents}
;
- }
-}
-
-export default ResponseCookiesDisplay;
diff --git a/src/client/components/display/ResponseEventsDisplay.jsx b/src/client/components/display/ResponseEventsDisplay.jsx
deleted file mode 100644
index e67704160..000000000
--- a/src/client/components/display/ResponseEventsDisplay.jsx
+++ /dev/null
@@ -1,93 +0,0 @@
-import React from "react";
-import JSONPretty from "react-json-pretty";
-import createDOMPurify from "dompurify";
-import SSERow from "./SSERow.jsx";
-
-const ResponseEventsDisplay = (props) => {
- const { events, headers } = props.content.response;
- const displayContents = [];
- const className =
- props.content.connection === "error"
- ? "__json-pretty__error"
- : "__json-pretty__";
-
- // If it's an SSE, render event rows
- if (
- headers &&
- headers["content-type"] &&
- headers["content-type"] === "text/event-stream"
- ) {
- events.forEach((cur, idx) => {
- displayContents.push(
);
- });
- }
- // if the response content-type, purify and render html
- else if (
- headers &&
- headers["content-type"] &&
- headers["content-type"].includes("text/html")
- ) {
- displayContents.push(
-
- );
- } else if (events && events.length > 1) {
- if (events) {
- let resEvents = "";
- let eventJSON;
- for (const event of events) {
- eventJSON = JSON.stringify(event, null, 4);
- resEvents = `${resEvents}
-${eventJSON}`;
- }
- displayContents.push(
-
- console.error(e)}
- space="4"
- className={className} //theme={{
- // main: 'line-height:1.3; color: midnightblue; background:#RRGGBB; overflow:auto;',
- // key: 'color:#0089D0;', // bluetwo
- // string: 'color:#15B78F;',// greenone
- // value: 'color:#fd971f;', // a nice orange
- // boolean: 'color:#E00198;', // gqlpink
- // }}
- />
-
- );
- }
- }
-
- // Otherwise, render a single display
- else if (events) {
- displayContents.push(
-
- console.error(e)}
- space="4"
- className={className}
- // theme={{
- // main:
- // "line-height:1.3; color: midnightblue; background:#RRGGBB; overflow:auto;",
- // key: "color:#0089D0;", // bluetwo
- // string: "color:#15B78F;", // greenone
- // value: "color:#fd971f;", // a nice orange
- // boolean: "color:#E00198;", // gqlpink
- // }}
- />
-
- );
- }
-
- return
{displayContents}
;
-};
-
-export default ResponseEventsDisplay;
diff --git a/src/client/components/display/ResponseSubscriptionDisplay.jsx b/src/client/components/display/ResponseSubscriptionDisplay.jsx
deleted file mode 100644
index 18ee989d7..000000000
--- a/src/client/components/display/ResponseSubscriptionDisplay.jsx
+++ /dev/null
@@ -1,34 +0,0 @@
-import React from 'react';
-import JSONPretty from 'react-json-pretty';
-import gql from 'graphql-tag';
-import { ApolloProvider, Subscription } from 'react-apollo';
-import { ApolloClient } from 'apollo-client';
-import { InMemoryCache } from 'apollo-cache-inmemory';
-import { WebSocketLink } from 'apollo-link-ws';
-
-
-const ResponseSubscriptionDisplay = ({ content, reqResUpdate }) => {
- let { bodyVariables } = content.request;
- if (bodyVariables === '') bodyVariables = null
- const uri = /wss?:\/\//.test(content.protocol) ? content.url : content.url.replace(content.protocol, 'ws://');
-
- const link = new WebSocketLink({
- uri,
- options: { reconnect: true }
- });
-
- const client = new ApolloClient({
- link,
- cache: new InMemoryCache(),
- });
-
- return (
-
-
- {content.response.events.reduce((array, subEvent, index) => array.concat([ ,
]), [])}
-
-
- );
-}
-
-export default ResponseSubscriptionDisplay;
diff --git a/src/client/components/display/ResponseTabs.jsx b/src/client/components/display/ResponseTabs.jsx
deleted file mode 100644
index 8a7c1b150..000000000
--- a/src/client/components/display/ResponseTabs.jsx
+++ /dev/null
@@ -1,34 +0,0 @@
-import React, { Component } from 'react';
-import Tab from './Tab.jsx';
-
-class ResponseTabs extends Component {
- constructor(props) {
- super(props);
- }
-
- render() {
-
- const events = 'Response Events';
- const cookies = 'Response Cookies';
- let headers = 'Response Headers';
-
- if (this.props.content.gRPC === true ) {
- headers = 'Response Metadata';
- }
-
- return (
-
-
-
- {
- !this.props.content.gRPC &&
-
- }
-
- );
- }
-}
-
-export default ResponseTabs;
-
-
diff --git a/src/client/components/display/ResponseTime.jsx b/src/client/components/display/ResponseTime.jsx
index 1405986ba..9ebb21fc2 100644
--- a/src/client/components/display/ResponseTime.jsx
+++ b/src/client/components/display/ResponseTime.jsx
@@ -1,48 +1,36 @@
-import React from "react";
-
-function ResponseTime({ currentResponse }) {
- // if (
- // !currentResponse ||
- // !currentResponse.timeReceived ||
- // !currentResponse.timeSent
- // ) {
- // return null;
- // }
- // const responseTime = currentResponse.timeReceived - currentResponse.timeSent;
-
- // return
{`${responseTime}ms`}
;
-
- //rest api
- if (
- currentResponse &&
- currentResponse.timeReceived &&
- currentResponse.timeSent
- ) {
- const responseTime =
- currentResponse.timeReceived - currentResponse.timeSent;
-
- return
{`${responseTime}ms`}
;
- }
-
- //websocket:
- if (
- currentResponse &&
- currentResponse.response &&
- currentResponse.response.messages &&
- currentResponse.response.messages.length > 0 &&
- currentResponse.response.messages.length ===
- currentResponse.request.messages.length
- ) {
- const leng = currentResponse.request.messages.length;
- const requestTime = currentResponse.request.messages[leng - 1].timeReceived;
-
- const responseTime =
- currentResponse.response.messages[leng - 1].timeReceived;
-
- const lagTime = responseTime - requestTime;
- return
{`${lagTime}ms`}
;
- }
-
- return null;
-}
-export default ResponseTime;
+import React from 'react';
+
+function ResponseTime({ currentResponse }) {
+ if (
+ currentResponse &&
+ currentResponse.timeReceived &&
+ currentResponse.timeSent
+ ) {
+ const responseTime =
+ currentResponse.timeReceived - currentResponse.timeSent;
+
+ return
{`${responseTime}ms`}
;
+ }
+
+ //websocket:
+ if (
+ currentResponse &&
+ currentResponse.response &&
+ currentResponse.response.messages &&
+ currentResponse.response.messages.length > 0 &&
+ currentResponse.response.messages.length ===
+ currentResponse.request.messages.length
+ ) {
+ const leng = currentResponse.request.messages.length;
+ const requestTime = currentResponse.request.messages[leng - 1].timeReceived;
+
+ const responseTime =
+ currentResponse.response.messages[leng - 1].timeReceived;
+
+ const lagTime = responseTime - requestTime;
+ return
{`${lagTime}ms`}
;
+ }
+
+ return null;
+}
+export default ResponseTime;
diff --git a/src/client/components/display/RestRequestContent.jsx b/src/client/components/display/RestRequestContent.jsx
index 19ebd4a2c..408193262 100644
--- a/src/client/components/display/RestRequestContent.jsx
+++ b/src/client/components/display/RestRequestContent.jsx
@@ -1,123 +1,124 @@
-import React from "react";
-import {UnControlled as CodeMirror} from 'react-codemirror2';
-import ContentReqRow from './ContentReqRow';
-import 'codemirror/theme/neat.css';
-
-
-export default function RestRequestContent({ request, isHTTP2 }) {
-
- // ORGANIZE PROPS
- const {
- headers, // [{id: 0, active: true, key: 'key', value: 'value'}]
- cookies, // [{id: 0, active: true, key: 'key', value: 'value'}]
- body, // "body Content text"
- bodyType, // "raw", x-www-form-urlencoded
- rawType, // "Text (text/plain)"
- isSSE,
- testContent // false/true
- } = request;
-
- // CREATE HEADER COMPONENTS
- const headerRows = headers.map((header, index) =>
);
-
-
- // CREATE COOKIE COMPONENTS
- const cookieRows = cookies.map((cookie, index) => );
-
- // CREATE FORM DATA BODY COMPONENTS
- // body = key1=value1&key2=value2
- const parseQueryString = (string) => {
- // input: key1=value1&key2=value2
- // output: [ {id: 1, key: key1, value: value1 ...etc } ]
- const query = [];
- let pairs = (string[0] === '?' ? string.substr(1) : string).split('&');
- for (let i = 0; i < pairs.length; i++) {
- const pair = pairs[i].split('=');
- const item = {
- id: i,
- key: decodeURIComponent(pair[0]),
- value: decodeURIComponent(pair[1] || '')
- };
- query.push(item);
- }
- return query;
- }
- let formRows = [];
- if (bodyType === "x-www-form-urlencoded") {
- const parsedFormBody = parseQueryString(body);
- formRows = parsedFormBody.map((item, index) => );
- }
-
- // PRETTY-PRINT BODY IF JSON
- // const bodyText = body;
- const bodyText = (rawType === 'application/json') ? ( JSON.stringify( JSON.parse(body), null, 4 ) ) : ( body );
-
- return (
-
- {/* REQUEST DETAILS */}
-
- {/* SSE CONFIRMATION */}
- { isSSE &&
-
SSE
- }
- {/* HTTP2 CONFIRMATION */}
- { isHTTP2 &&
-
HTTP2 Connection Established
- }
- {/* HEADERS */}
- {headerRows.length > 0 &&
-
Headers
- }
- {headerRows}
- {/* COOKIES */}
- {cookieRows.length > 0 &&
-
Cookies
- }
- {cookieRows}
- {/* BODY */}
- {/* RAW DATA */}
- {body.length > 0 && bodyType === "raw" &&
-
- }
- {/* FORM DATA */}
- {bodyType === "x-www-form-urlencoded" &&
-
- }
- {/* TEST DATA */}
- {testContent.length > 0 &&
-
- }
-
-
-
- )
-}
\ No newline at end of file
+import React from 'react';
+import { UnControlled as CodeMirror } from 'react-codemirror2';
+import ContentReqRow from './ContentReqRow';
+import 'codemirror/theme/neat.css';
+
+export default function RestRequestContent({ request, isHTTP2 }) {
+ // ORGANIZE PROPS
+ const {
+ headers, // [{id: 0, active: true, key: 'key', value: 'value'}]
+ cookies, // [{id: 0, active: true, key: 'key', value: 'value'}]
+ body, // "body Content text"
+ bodyType, // "raw", x-www-form-urlencoded
+ rawType, // "Text (text/plain)"
+ isSSE,
+ testContent, // false/true
+ } = request;
+
+ // CREATE HEADER COMPONENTS
+ const headerRows = headers.map((header, index) => (
+
+ ));
+
+ // CREATE COOKIE COMPONENTS
+ const cookieRows = cookies.map((cookie, index) => (
+
+ ));
+
+ // CREATE FORM DATA BODY COMPONENTS
+ // body = key1=value1&key2=value2
+ const parseQueryString = (string) => {
+ // input: key1=value1&key2=value2
+ // output: [ {id: 1, key: key1, value: value1 ...etc } ]
+ const query = [];
+ let pairs = (string[0] === '?' ? string.substr(1) : string).split('&');
+ for (let i = 0; i < pairs.length; i++) {
+ const pair = pairs[i].split('=');
+ const item = {
+ id: i,
+ key: decodeURIComponent(pair[0]),
+ value: decodeURIComponent(pair[1] || ''),
+ };
+ query.push(item);
+ }
+ return query;
+ };
+ let formRows = [];
+ if (bodyType === 'x-www-form-urlencoded') {
+ const parsedFormBody = parseQueryString(body);
+ formRows = parsedFormBody.map((item, index) => (
+
+ ));
+ }
+
+ // PRETTY-PRINT BODY IF JSON
+ // const bodyText = body;
+ const bodyText =
+ rawType === 'application/json'
+ ? JSON.stringify(JSON.parse(body), null, 4)
+ : body;
+
+ return (
+
+ {/* REQUEST DETAILS */}
+
+ {/* SSE CONFIRMATION */}
+ {isSSE &&
SSE
}
+ {/* HTTP2 CONFIRMATION */}
+ {isHTTP2 && (
+
+ HTTP2 Connection Established
+
+ )}
+ {/* HEADERS */}
+ {headerRows.length > 0 &&
Headers
}
+ {headerRows}
+ {/* COOKIES */}
+ {cookieRows.length > 0 &&
Cookies
}
+ {cookieRows}
+ {/* BODY */}
+ {/* RAW DATA */}
+ {body.length > 0 && bodyType === 'raw' && (
+
+ )}
+ {/* FORM DATA */}
+ {bodyType === 'x-www-form-urlencoded' && (
+
+ )}
+ {/* TEST DATA */}
+ {testContent.length > 0 && (
+
+ )}
+
+
+ );
+}
diff --git a/src/client/components/display/SSERow.jsx b/src/client/components/display/SSERow.jsx
deleted file mode 100644
index ddf207b06..000000000
--- a/src/client/components/display/SSERow.jsx
+++ /dev/null
@@ -1,62 +0,0 @@
- import React, { Component } from "react";
-
-class SSERow extends Component {
- constructor(props) {
- super(props);
- // this.state = {};
- this.handleClick = this.handleClick.bind(this);
- }
-
- handleClick(e) {
- const expandable = e.target
- .closest(".response_sse")
- .getElementsByClassName("data-inner")
- .item(0);
- const expandBtn = e.target;
- expandBtn.classList.toggle("expand-active");
- expandable.classList.toggle("expanded");
- }
-
- render() {
- return (
-
-
-
-
- ID {this.props.content.lastEventId}
-
-
-
-
-
- Event {this.props.content.type}
-
-
-
-
-
- Time Received {this.props.content.timeReceived}
-
-
-
-
- this.handleClick(e)}
- />
-
-
-
-
-
-
- Data: {this.props.content.data}
-
-
-
-
- );
- }
-}
-
-export default SSERow;
diff --git a/src/client/components/display/SaveModalSavedWorkspaces.jsx b/src/client/components/display/SaveModalSavedWorkspaces.jsx
index f3eb2a005..6da4c762e 100644
--- a/src/client/components/display/SaveModalSavedWorkspaces.jsx
+++ b/src/client/components/display/SaveModalSavedWorkspaces.jsx
@@ -1,16 +1,22 @@
-import React from 'react'
-
-export default function SaveModalSavedWorkspaces({ name, inputID, updateCollection }) {
- return (
-
-
-
{updateCollection(name, inputID); }}
- >
- {name}
-
-
-
- )
-}
\ No newline at end of file
+/* eslint-disable jsx-a11y/no-static-element-interactions */
+/* eslint-disable jsx-a11y/click-events-have-key-events */
+import React from 'react';
+
+function SaveModalSavedWorkspaces({ name, inputID, updateCollection }) {
+ return (
+
+
+
{
+ updateCollection(name, inputID);
+ }}
+ >
+ {name}
+
+
+
+ );
+}
+
+export default SaveModalSavedWorkspaces;
diff --git a/src/client/components/display/StatusButtons.jsx b/src/client/components/display/StatusButtons.jsx
index 36c9e2f56..d4b5f1ec7 100644
--- a/src/client/components/display/StatusButtons.jsx
+++ b/src/client/components/display/StatusButtons.jsx
@@ -1,49 +1,32 @@
-import React from 'react'
-
-function StatusButtons({ currentResponse }) {
-
- if (currentResponse.error || !currentResponse) {
- return (
-
- Error
-
- );
- }
-
- // STATUS FOR GRAPHQL
- if (currentResponse.graphQL === true && currentResponse.response) {
- return (
-
- Success
-
- )
- }
-
- if (!currentResponse.response ||
- !currentResponse.response.headers ||
- (Object.keys(currentResponse.response.headers).length === 0) ||
- !currentResponse.response.headers[":status"]) {
- return (null)
- }
-
-
- // RECEIVING STATUS CODE AND CONVERTING INTO STRING
- const statusCode = currentResponse.response.headers[":status"].toString()
-
- if (statusCode.startsWith('2')) {
- return (
-
- {statusCode}
-
- )
- }
-
- return (
-
- {statusCode}
-
- )
-
-}
-
-export default StatusButtons
+import React from 'react';
+
+function StatusButtons({ currentResponse }) {
+ if (currentResponse.error || !currentResponse) {
+ return Error
;
+ }
+
+ // STATUS FOR GRAPHQL
+ if (currentResponse.graphQL === true && currentResponse.response) {
+ return Success
;
+ }
+
+ if (
+ !currentResponse.response ||
+ !currentResponse.response.headers ||
+ Object.keys(currentResponse.response.headers).length === 0 ||
+ !currentResponse.response.headers[':status']
+ ) {
+ return null;
+ }
+
+ // RECEIVING STATUS CODE AND CONVERTING INTO STRING
+ const statusCode = currentResponse.response.headers[':status'].toString();
+
+ if (statusCode.startsWith('2')) {
+ return {statusCode}
;
+ }
+
+ return {statusCode}
;
+}
+
+export default StatusButtons;
diff --git a/src/client/components/display/Tab.jsx b/src/client/components/display/Tab.jsx
deleted file mode 100644
index 16a4560a8..000000000
--- a/src/client/components/display/Tab.jsx
+++ /dev/null
@@ -1,31 +0,0 @@
-import React, { Component } from "react";
-
-class Tab extends Component {
- constructor(props) {
- super(props);
- this.tabClickHandler = this.tabClickHandler.bind(this);
- }
-
- tabClickHandler() {
- const selectedTab = this.props.tabName;
- this.props.onTabSelected(selectedTab);
- }
-
- render() {
- return (
-
- {this.props.tabName}
-
- );
- }
-}
-
-export default Tab;
diff --git a/src/client/components/display/WebRTCRequestContent.jsx b/src/client/components/display/WebRTCRequestContent.jsx
new file mode 100644
index 000000000..af34ad054
--- /dev/null
+++ b/src/client/components/display/WebRTCRequestContent.jsx
@@ -0,0 +1,93 @@
+import React, { useState, useEffect } from 'react';
+import { UnControlled as CodeMirror } from 'react-codemirror2';
+import { useSelector, useDispatch } from 'react-redux';
+import Peer from '../../controllers/webrtcPeerController';
+import * as actions from '../../actions/actions.js';
+
+const jBeautify = require('js-beautify').js;
+
+export default function WebRTCRequestContent({ content }) {
+ const { body } = content.request;
+ const { iceConfiguration } = content.request.body;
+ const [localSdp, setLocalSdp] = useState('');
+ const [pcInitiator, setPcInitiator] = useState(null);
+ const dispatch = useDispatch();
+
+ const currentResponse = useSelector(
+ (store) => store.business.currentResponse
+ );
+
+ useEffect(() => {
+ if (pcInitiator?.connection?.localDescription) {
+ dispatch(
+ actions.saveCurrentResponseData({
+ ...content,
+ webrtcData: {
+ localSdp: pcInitiator.connection.localDescription.sdp,
+ },
+ })
+ );
+ }
+ }, [pcInitiator, localSdp, dispatch, content]);
+
+ useEffect(() => {
+ setLocalSdp(currentResponse?.webrtcData?.localSdp);
+ }, [currentResponse]);
+
+ function createLocalSDP() {
+ const pc = new Peer(iceConfiguration);
+ pc.role = 'INITIATOR';
+ pc.initDataChannelAndEvents();
+ pc.createLocalSdp();
+ setPcInitiator(pc);
+ }
+
+ return (
+
+
+
Servers
+
{
+ editor.setSize('100%', '100%');
+ }}
+ />
+
+
+
Local SDP
+
+
+ createLocalSDP()}>
+ Create Local SDP
+
+
+
+
+ );
+}
diff --git a/src/client/components/display/WebSocketMessage.tsx b/src/client/components/display/WebSocketMessage.tsx
index 18488ca0d..ee000c630 100644
--- a/src/client/components/display/WebSocketMessage.tsx
+++ b/src/client/components/display/WebSocketMessage.tsx
@@ -1,59 +1,62 @@
+/* eslint-disable @typescript-eslint/no-unsafe-assignment */
+/* eslint-disable @typescript-eslint/restrict-template-expressions */
/* eslint-disable camelcase */
import React from 'react';
import PropTypes from 'prop-types';
-import { WebSocketMessageProps } from "../../../types"
+import { WebSocketMessageProps } from '../../../types';
-const WebSocketMessage:React.SFC = ({
+const WebSocketMessage: React.SFC = ({
source,
timeReceived,
data,
- index
+ index,
}) => {
-
+ // conditional classNames and id for messages for styling depending on source
+ const webSocketMessageClassNames =
+ source === 'server'
+ ? 'websocket_message websocket_message-server'
+ : 'websocket_message websocket_message-client';
+ const webSocketMessageIDNames =
+ source === 'server'
+ ? 'id_websocket_message-server'
+ : 'id_websocket_message-client';
- //conditional classNames and id for messages for styling depending on source
- const webSocketMessageClassNames = source === 'server' ? 'websocket_message websocket_message-server' : 'websocket_message websocket_message-client'
- const webSocketMessageIDNames = source === 'server' ? 'id_websocket_message-server' : 'id_websocket_message-client'
+ const message_background =
+ source === 'server' ? 'server-background' : 'client-background';
+ const message_sender = source === 'server' ? 'server' : 'client';
- const message_background = source === 'server' ? 'server-background' : 'client-background'
- const message_sender = source === 'server' ? 'server' : 'client'
-
- //timestamp for messages
- const buildTime = (time:number):string => {
+ // timestamp for messages
+ const buildTime = (time: number): string => {
const hours = new Date(time).getHours();
- const h = hours >= 10 ? `${hours}` : `0${JSON.stringify(hours)}`
- const minutes= new Date(time).getMinutes();
- const m = minutes >= 10 ? `${minutes}` : `0${JSON.stringify(minutes)}`
+ const h = hours >= 10 ? `${hours}` : `0${JSON.stringify(hours)}`;
+ const minutes = new Date(time).getMinutes();
+ const m = minutes >= 10 ? `${minutes}` : `0${JSON.stringify(minutes)}`;
const seconds = new Date(time).getSeconds();
- const s = seconds >= 10 ? `${seconds}` : `0${JSON.stringify(seconds)}`
- return `${h}:${m}:${s}`
- }
-
+ const s = seconds >= 10 ? `${seconds}` : `0${JSON.stringify(seconds)}`;
+ return `${h}:${m}:${s}`;
+ };
return (
-
- {
-
- typeof data==='object'?
-
- //decode buffer to dataURI
-
:
-
-
{data}
- }
-
+ {typeof data === 'object' ? (
+ // decode buffer to dataURI
+
+ ) : (
+
{data}
+ )}
+
+
+ {buildTime(timeReceived)}
-
{buildTime(timeReceived)}
{message_sender}
);
-}
+};
WebSocketMessage.propTypes = {
source: PropTypes.string.isRequired,
diff --git a/src/client/components/display/WebSocketWindow.tsx b/src/client/components/display/WebSocketWindow.tsx
index 550cd7378..314ae6338 100644
--- a/src/client/components/display/WebSocketWindow.tsx
+++ b/src/client/components/display/WebSocketWindow.tsx
@@ -1,12 +1,14 @@
+/* eslint-disable @typescript-eslint/no-explicit-any */
+/* eslint-disable @typescript-eslint/no-unsafe-call */
+/* eslint-disable @typescript-eslint/no-unsafe-member-access */
+/* eslint-disable @typescript-eslint/no-unsafe-assignment */
/* eslint-disable no-param-reassign */
-import React, { useState } from "react";
-import PropTypes, { string } from "prop-types";
-import { useSelector, useDispatch } from "react-redux";
-import { DropzoneArea } from "material-ui-dropzone";
-import WebSocketMessage from "./WebSocketMessage";
-import { WebSocketWindowProps } from "../../../types";
-import ImageDropzone from "../../components/display/ImageDropzone";
-// import * as actions from "../../../../src/client/actions/actions.js";
+import React, { useState } from 'react';
+import PropTypes from 'prop-types';
+import WebSocketMessage from './WebSocketMessage';
+import { WebSocketWindowProps } from '../../../types';
+import ImageDropzone from './ImageDropzone';
+
const { api } = window;
const WebSocketWindow: React.SFC = ({
@@ -15,33 +17,32 @@ const WebSocketWindow: React.SFC = ({
incomingMessages,
connection,
}) => {
-
const [inputMsgImg, setinputMsgImg] = useState({
- inputMsg: "",
- inputImg: "",
+ inputMsg: '',
+ inputImg: '',
});
- //updates the outgoing message when it changes
- const updateOutgoingMessage = async (value: any) => {
- console.log("updating msg");
- if (value.includes("data:image/")) {
- await setinputMsgImg({ ...inputMsgImg, inputImg: value });
+ // updates the outgoing message when it changes
+ const updateOutgoingMessage = (value: any) => {
+ console.log('updating msg');
+ if (value.includes('data:image/')) {
+ setinputMsgImg({ ...inputMsgImg, inputImg: value });
} else {
setinputMsgImg({ ...inputMsgImg, inputMsg: value });
}
};
- //sends to WScontroller in main.js to send the message to server
+ // sends to WScontroller in main.js to send the message to server
const sendToWSController = () => {
if (inputMsgImg.inputMsg) {
- api.send("send-ws", content, inputMsgImg.inputMsg);
- setinputMsgImg({ inputMsg: "", inputImg: "" });
+ api.send('send-ws', content, inputMsgImg.inputMsg);
+ setinputMsgImg({ inputMsg: '', inputImg: '' });
} else if (inputMsgImg.inputImg) {
- console.log("rerendering");
- api.send("send-ws", content, inputMsgImg.inputImg);
- setinputMsgImg({ inputMsg: "", inputImg: "" });
+ console.log('rerendering');
+ api.send('send-ws', content, inputMsgImg.inputImg);
+ setinputMsgImg({ inputMsg: '', inputImg: '' });
}
- //reset inputbox
+ // reset inputbox
};
const handleFileChange = async (file: any) => {
@@ -61,30 +62,30 @@ const WebSocketWindow: React.SFC = ({
}
};
- //when you press enter send the message, send message to socket
+ // when you press enter send the message, send message to socket
const handleKeyPress = (event: { key: string }) => {
- if (event.key === "Enter") {
+ if (event.key === 'Enter') {
sendToWSController();
}
};
- //maps the messages to view in chronological order and by whom - self/server
+ // maps the messages to view in chronological order and by whom - self/server
const combinedMessagesReactArr = outgoingMessages
.map((message) => {
- message.source = "client";
+ message.source = 'client';
return message;
})
.concat(
incomingMessages.map((message) => {
- message.source = "server";
+ message.source = 'server';
return message;
})
)
- //sorts by time
+ // sorts by time
.sort((a, b) => a.timeReceived - b.timeReceived)
- //then maps the combined array to a WebSocket Component
- //conditionally rendering messages or images
+ // then maps the combined array to a WebSocket Component
+ // conditionally rendering messages or images
.map((message, index) => (
= ({
/>
));
- //sets the message style depending on if the connection is open
- //hides when connection is not open
+ // sets the message style depending on if the connection is open
+ // hides when connection is not open
const messageInputStyles = {
- display: connection === "open" ? "block" : "none",
+ display: connection === 'open' ? 'block' : 'none',
};
- //exports the chatLog- sends it to the backend
+ // exports the chatLog- sends it to the backend
const exportChatLog = (event: any) => {
- api.send("exportChatLog", outgoingMessages, incomingMessages);
+ api.send('exportChatLog', outgoingMessages, incomingMessages);
};
return (
-
+
= ({
{/* only show the ws messages when connection is open */}
- {connection === "open" && (
+ {connection === 'open' && (
<>
-
-
- {combinedMessagesReactArr}
-
+
+ {combinedMessagesReactArr}
+
{
- let {total, pass} = props;
- let passed = Math.floor((pass / (total)) * 100);
- let passedFract = (pass / (total)).toFixed(2);
-
- let useMeasure = () => {
- let mounted = true;
- const ref = useRef()
- const [bounds, set] = useState({ left: 0, top: 0, width: 0, height: 0 })
- const [ro] = useState(() => new ResizeObserver(([entry]) => set(entry.contentRect)) )
- useEffect(() => {
- (ro.observe(ref.current), ro.disconnect)
- }, [])
- return [{ ref }, bounds];
- }
-
-
- const [bind, { width }] = useMeasure()
- const adjWidth = (width * passedFract);
- const springProps = useSpring({ width: adjWidth, config: { duration: 1000 }})
-
-
- return (
-
-
-
-
- {props => {props.number.toFixed(0)}% Passed
}
-
- {/* {springProps.width.interpolate(x => x.toFixed(2))} */}
-
-
-
- );
-};
-
-export default VerticalProgress;
+import React, { useRef, useState, useEffect, Component } from 'react';
+import { useSpring, animated } from 'react-spring';
+import ResizeObserver from 'resize-observer-polyfill';
+import { Spring, config } from 'react-spring/renderprops';
+
+const VerticalProgress = (props) => {
+ const { total, pass } = props;
+ const passed = Math.floor((pass / total) * 100);
+ const passedFract = (pass / total).toFixed(2);
+
+ const useMeasure = () => {
+ const mounted = true;
+ const ref = useRef();
+ const [bounds, set] = useState({ left: 0, top: 0, width: 0, height: 0 });
+ const [ro] = useState(
+ () => new ResizeObserver(([entry]) => set(entry.contentRect))
+ );
+ useEffect(() => {
+ ro.observe(ref.current), ro.disconnect;
+ }, [ro]);
+ return [{ ref }, bounds];
+ };
+
+ const [bind, { width }] = useMeasure();
+ const adjWidth = width * passedFract;
+ const springProps = useSpring({
+ width: adjWidth,
+ config: { duration: 1000 },
+ });
+
+ return (
+ // eslint-disable-next-line react/jsx-props-no-spreading
+
+
+
+
+ {(props) => {props.number.toFixed(0)}% Passed
}
+
+ {/* {springProps.width.interpolate(x => x.toFixed(2))} */}
+
+
+ );
+};
+
+export default VerticalProgress;
diff --git a/src/client/controllers/collectionsController.js b/src/client/controllers/collectionsController.js
index c1f0e17ed..8d6b57690 100644
--- a/src/client/controllers/collectionsController.js
+++ b/src/client/controllers/collectionsController.js
@@ -1,91 +1,90 @@
-// import { ipcRenderer } from "electron";
-import uuid from "uuid/v4";
-import db from "../db";
-import * as store from "../store";
-import * as actions from "../actions/actions";
-
-const { api } = window;
-
-api.receive("add-collection", (collectionData) => {
- // Add parsed text file to db
- collectionsController.addCollectionToIndexedDb(JSON.parse(collectionData));
- collectionsController.getCollections();
-});
-
-const collectionsController = {
- addCollectionToIndexedDb(collection) {
- db.collections
- .put(collection)
- .catch((err) => console.log("Error in addToCollection", err));
- },
-
- deleteCollectionFromIndexedDb(id) {
- db.collections
- .delete(id)
- .catch((err) => console.log("Error in deleteFromCollection", err));
- },
-
- updateCollectionInIndexedDb(collection) {
- collectionsController.deleteCollectionFromIndexedDb(collection.id);
- collectionsController.addCollectionToIndexedDb(collection);
- },
-
- getCollections() {
- db.table("collections")
- .toArray()
- .then((collections) => {
- const collectionsArr = collections.sort(
- (a, b) => b.created_at - a.created_at
- );
- store.default.dispatch(actions.getCollections(collectionsArr));
- })
- .catch((err) => console.log("Error in getCollection s", err));
- },
-
- collectionNameExists(obj) {
- const { name } = obj;
- return new Promise((resolve, reject) => {
- // resolve and reject are functions!
- db.collections
- .where("name")
- .equalsIgnoreCase(name)
- .first((foundCollection) => !!foundCollection)
- .then((found) => resolve(found))
- .catch((error) => {
- console.error(error.stack || error);
- reject(error);
- });
- });
- },
-
- exportCollection(id) {
- db.collections
- .where("id")
- .equals(id)
- .first((foundCollection) => {
- // change name and id of collection to satisfy uniqueness requirements of db
- foundCollection.name += " import";
- foundCollection.id = uuid();
-
- api.send("export-collection", { collection: foundCollection });
- })
- .catch((error) => {
- console.error(error.stack || error);
- reject(error);
- });
- },
-
- importCollection(collection) {
- return new Promise((resolve) => {
- api.send("import-collection", collection);
- api.receive("add-collection", (...args) => {
- console.log("received data: ", JSON.parse(args.data));
- collectionsController.addCollectionToIndexedDb(JSON.parse(args.data));
- collectionsController.getCollections();
- resolve();
- });
- })
- },
-};
-
-export default collectionsController;
+import uuid from 'uuid/v4';
+import db from '../db';
+import * as store from '../store';
+import * as actions from '../actions/actions';
+
+const { api } = window;
+
+api.receive('add-collection', (collectionData) => {
+ // Add parsed text file to db
+ collectionsController.addCollectionToIndexedDb(JSON.parse(collectionData));
+ collectionsController.getCollections();
+});
+
+const collectionsController = {
+ addCollectionToIndexedDb(collection) {
+ db.collections
+ .put(collection)
+ .catch((err) => console.log('Error in addToCollection', err));
+ },
+
+ deleteCollectionFromIndexedDb(id) {
+ db.collections
+ .delete(id)
+ .catch((err) => console.log('Error in deleteFromCollection', err));
+ },
+
+ updateCollectionInIndexedDb(collection) {
+ collectionsController.deleteCollectionFromIndexedDb(collection.id);
+ collectionsController.addCollectionToIndexedDb(collection);
+ },
+
+ getCollections() {
+ db.table('collections')
+ .toArray()
+ .then((collections) => {
+ const collectionsArr = collections.sort(
+ (a, b) => b.created_at - a.created_at
+ );
+ store.default.dispatch(actions.getCollections(collectionsArr));
+ })
+ .catch((err) => console.log('Error in getCollection s', err));
+ },
+
+ collectionNameExists(obj) {
+ const { name } = obj;
+ return new Promise((resolve, reject) => {
+ // resolve and reject are functions!
+ db.collections
+ .where('name')
+ .equalsIgnoreCase(name)
+ .first((foundCollection) => !!foundCollection)
+ .then((found) => resolve(found))
+ .catch((error) => {
+ console.error(error.stack || error);
+ reject(error);
+ });
+ });
+ },
+
+ exportCollection(id) {
+ db.collections
+ .where('id')
+ .equals(id)
+ .first((foundCollection) => {
+ // change name and id of collection to satisfy uniqueness requirements of db
+ foundCollection.name += ' import';
+ foundCollection.id = uuid();
+
+ api.send('export-collection', { collection: foundCollection });
+ })
+ .catch((error) => {
+ console.error(error.stack || error);
+ reject(error);
+ });
+ },
+
+ importCollection(collection) {
+ return new Promise((resolve) => {
+ api.send('import-collection', collection);
+ api.receive('add-collection', (...args) => {
+ console.log('received data: ', JSON.parse(args.data));
+ collectionsController.addCollectionToIndexedDb(JSON.parse(args.data));
+ collectionsController.getCollections();
+ resolve();
+ });
+ });
+ },
+};
+
+export default collectionsController;
diff --git a/src/client/controllers/graphQLController.js b/src/client/controllers/graphQLController.js
index fcd5d1b4e..3aa2f1bb8 100644
--- a/src/client/controllers/graphQLController.js
+++ b/src/client/controllers/graphQLController.js
@@ -1,222 +1,221 @@
-import ApolloClient from "apollo-client";
-import gql from "graphql-tag";
-import { InMemoryCache } from "apollo-cache-inmemory";
-import { WebSocketLink } from "apollo-link-ws";
-import { SubscriptionClient } from "subscriptions-transport-ws";
-import { buildClientSchema, printSchema } from "graphql";
-import * as store from "../store";
-import * as actions from "../actions/actions";
-
-const { api } = window;
-
-const graphQLController = {
-
- openGraphQLConnection(reqResObj) {
- // initialize response data
- reqResObj.response.headers = {};
- reqResObj.response.events = [];
- reqResObj.response.cookies = [];
- reqResObj.connection = "open";
- reqResObj.timeSent = Date.now();
- // store.default.dispatch(actions.reqResUpdate(reqResObj));
- //send reqRes object to main process through context bridge
- this.sendGqlToMain({ reqResObj })
- .then((response) => {
- if (response.error)
- this.handleError(response.reqResObj.error, response.reqResObj);
- else this.handleResponse(response.data, response.reqResObj);
- })
- .catch((err) => console.log("error in sendGqlToMain", err));
- },
-
- openGraphQLConnectionAndRunCollection(reqResArray) {
- // initialize response data
- let index = 0;
- const reqResObj = reqResArray[index]
- api.removeAllListeners("reply-gql");
- api.receive("reply-gql", (result) => {
- // needs formatting because component reads them in a particular order
- result.reqResObj.response.cookies = this.cookieFormatter(
- result.reqResObj.response.cookies
- );
-
- if (result.error) {
- this.handleError(result.error, result.reqResObj);
- }
- else {
- result.reqResObj.response.events.push(result.data);
-
- result.reqResObj.connection = "closed";
- result.reqResObj.connectionType = "plain";
- result.reqResObj.timeReceived = Date.now();
-
- store.default.dispatch(actions.reqResUpdate(result.reqResObj));
- store.default.dispatch(actions.saveCurrentResponseData(result.reqResObj));
- store.default.dispatch(actions.updateGraph(result.reqResObj));
- index += 1;
- if (reqResArray.length > index) runSingleGraphQLRequest(reqResArray[index])
- }
- });
-
- const runSingleGraphQLRequest = (reqResObj) => {
- reqResObj.response.headers = {};
- reqResObj.response.events = [];
- reqResObj.response.cookies = [];
- reqResObj.connection = "open";
- reqResObj.timeSent = Date.now();
- api.send("open-gql", {reqResObj});
- }
- runSingleGraphQLRequest(reqResArray[index]);
- },
-
- // handles graphQL queries and mutations
- sendGqlToMain(args) {
- return new Promise((resolve) => {
- //send object to the context bridge
- api.removeAllListeners("reply-gql");
- api.send("open-gql", args);
- api.receive("reply-gql", (result) => {
- // needs formatting because component reads them in a particular order
- result.reqResObj.response.cookies = this.cookieFormatter(
- result.reqResObj.response.cookies
- );
- resolve(result);
- });
- });
- },
-
- openSubscription(reqResObj) {
- reqResObj.response.headers = {};
- reqResObj.response.events = [];
- reqResObj.connection = "open";
- store.default.dispatch(actions.reqResUpdate(reqResObj));
-
- const currentID = store.default.getState().business.currentResponse.id;
- if(currentID === reqResObj.id) store.default.dispatch(actions.saveCurrentResponseData(reqResObj));
-
- // have to replace http with ws to connect to the websocket
- const wsUri = reqResObj.url.replace(/http/gi, 'ws');
-
- // Map all headers to headers object
- const headers = {};
- reqResObj.request.headers.forEach(({ active, key, value }) => {
- if(active) headers[key] = value;
- })
-
- // Reformat cookies
- let cookies = '';
- if (reqResObj.request.cookies.length) {
- cookies = reqResObj.request.cookies.reduce((acc, userCookie) => {
- if(userCookie.active) return acc + `${userCookie.key}=${userCookie.value}; `;
- return acc;
- }, '');
- }
- headers.Cookie = cookies;
-
- const wsLink = new WebSocketLink(
- new SubscriptionClient(
- wsUri,
- {
- reconnect: true,
- timeout: 30000,
- connectionParams: {
- headers
- }
- })
- );
-
- const apolloClient = new ApolloClient({
- link: wsLink,
- cache: new InMemoryCache(),
- });
-
- const query = gql`
- ${reqResObj.request.body}
- `;
- const variables = reqResObj.request.bodyVariables
- ? JSON.parse(reqResObj.request.bodyVariables)
- : {};
-
- apolloClient
- .subscribe({
- query,
- variables,
- })
- .subscribe({
- next(subsEvent) {
- // Notify your application with the new arrived data
- reqResObj.response.events.push(subsEvent.data);
- const newReqRes = JSON.parse(JSON.stringify(reqResObj));
- store.default.dispatch(actions.saveCurrentResponseData(newReqRes));
- store.default.dispatch(actions.reqResUpdate(newReqRes));
- },
- error(err) {
- console.error(err)
- }
- });
- },
-
- handleResponse(response, reqResObj) {
- reqResObj.connection = "closed";
- reqResObj.connectionType = "plain";
- reqResObj.timeReceived = Date.now();
-
- // reqResObj.response.events.push(JSON.stringify(response.data));
- reqResObj.response.events.push(response.data);
-
- store.default.dispatch(actions.reqResUpdate(reqResObj));
- store.default.dispatch(actions.saveCurrentResponseData(reqResObj));
- store.default.dispatch(actions.updateGraph(reqResObj));
- },
-
- handleError(errorsObj, reqResObj) {
- reqResObj.connection = "error";
- reqResObj.timeReceived = Date.now();
-
- // reqResObj.response.events.push(errorsObj);
- reqResObj.response.events.push(JSON.parse(errorsObj));
- store.default.dispatch(actions.saveCurrentResponseData(reqResObj));
- store.default.dispatch(actions.reqResUpdate(reqResObj));
- },
-
- // objects that travel over IPC API have their properties alphabetized...
- cookieFormatter(cookieArray) {
- return cookieArray.map((eachCookie) => {
- const cookieFormat = {
- name: eachCookie.name,
- value: eachCookie.value,
- domain: eachCookie.domain,
- hostOnly: eachCookie.hostonly ? eachCookie.hostonly : false,
- path: eachCookie.path,
- secure: eachCookie.secure ? eachCookie.secure : false,
- httpOnly: eachCookie.httponly ? eachCookie.httponly : false,
- session: eachCookie.session ? eachCookie.session : false,
- expirationDate: eachCookie.expires ? eachCookie.expires : "",
- };
- return cookieFormat;
- });
- },
-
- introspect(url, headers, cookies) {
- const introspectionObject = {
- url,
- headers,
- cookies
- }
- api.send("introspect", JSON.stringify(introspectionObject));
- api.receive("introspect-reply", (data) => {
- if (data !== "Error: Please enter a valid GraphQL API URI") {
- //formatted for Codemirror hint and lint
- const clientSchema = buildClientSchema(data);
- // formatted for pretty schema display
- const schemaSDL = printSchema(clientSchema);
- const modifiedData = { schemaSDL, clientSchema };
- store.default.dispatch(actions.setIntrospectionData(modifiedData));
- } else {
- store.default.dispatch(actions.setIntrospectionData(data));
- }
- });
- },
-};
-
-export default graphQLController;
+import ApolloClient from 'apollo-client';
+import gql from 'graphql-tag';
+import { InMemoryCache } from 'apollo-cache-inmemory';
+import { WebSocketLink } from 'apollo-link-ws';
+import { SubscriptionClient } from 'subscriptions-transport-ws';
+import { buildClientSchema, printSchema } from 'graphql';
+import * as store from '../store';
+import * as actions from '../actions/actions';
+
+const { api } = window;
+
+const graphQLController = {
+ openGraphQLConnection(reqResObj) {
+ // initialize response data
+ reqResObj.response.headers = {};
+ reqResObj.response.events = [];
+ reqResObj.response.cookies = [];
+ reqResObj.connection = 'open';
+ reqResObj.timeSent = Date.now();
+ // store.default.dispatch(actions.reqResUpdate(reqResObj));
+ // send reqRes object to main process through context bridge
+ this.sendGqlToMain({ reqResObj })
+ .then((response) => {
+ if (response.error)
+ this.handleError(response.reqResObj.error, response.reqResObj);
+ else this.handleResponse(response.data, response.reqResObj);
+ })
+ .catch((err) => console.log('error in sendGqlToMain', err));
+ },
+
+ openGraphQLConnectionAndRunCollection(reqResArray) {
+ // initialize response data
+ let index = 0;
+ const reqResObj = reqResArray[index];
+ api.removeAllListeners('reply-gql');
+ api.receive('reply-gql', (result) => {
+ // needs formatting because component reads them in a particular order
+ result.reqResObj.response.cookies = this.cookieFormatter(
+ result.reqResObj.response.cookies
+ );
+
+ if (result.error) {
+ this.handleError(result.error, result.reqResObj);
+ } else {
+ result.reqResObj.response.events.push(result.data);
+
+ result.reqResObj.connection = 'closed';
+ result.reqResObj.connectionType = 'plain';
+ result.reqResObj.timeReceived = Date.now();
+
+ store.default.dispatch(actions.reqResUpdate(result.reqResObj));
+ store.default.dispatch(
+ actions.saveCurrentResponseData(result.reqResObj)
+ );
+ store.default.dispatch(actions.updateGraph(result.reqResObj));
+ index += 1;
+ if (reqResArray.length > index)
+ runSingleGraphQLRequest(reqResArray[index]);
+ }
+ });
+
+ const runSingleGraphQLRequest = (reqResObj) => {
+ reqResObj.response.headers = {};
+ reqResObj.response.events = [];
+ reqResObj.response.cookies = [];
+ reqResObj.connection = 'open';
+ reqResObj.timeSent = Date.now();
+ api.send('open-gql', { reqResObj });
+ };
+ runSingleGraphQLRequest(reqResArray[index]);
+ },
+
+ // handles graphQL queries and mutations
+ sendGqlToMain(args) {
+ return new Promise((resolve) => {
+ // send object to the context bridge
+ api.removeAllListeners('reply-gql');
+ api.send('open-gql', args);
+ api.receive('reply-gql', (result) => {
+ // needs formatting because component reads them in a particular order
+ result.reqResObj.response.cookies = this.cookieFormatter(
+ result.reqResObj.response.cookies
+ );
+ resolve(result);
+ });
+ });
+ },
+
+ openSubscription(reqResObj) {
+ reqResObj.response.headers = {};
+ reqResObj.response.events = [];
+ reqResObj.connection = 'open';
+ store.default.dispatch(actions.reqResUpdate(reqResObj));
+
+ const currentID = store.default.getState().business.currentResponse.id;
+ if (currentID === reqResObj.id)
+ store.default.dispatch(actions.saveCurrentResponseData(reqResObj));
+
+ // have to replace http with ws to connect to the websocket
+ const wsUri = reqResObj.url.replace(/http/gi, 'ws');
+
+ // Map all headers to headers object
+ const headers = {};
+ reqResObj.request.headers.forEach(({ active, key, value }) => {
+ if (active) headers[key] = value;
+ });
+
+ // Reformat cookies
+ let cookies = '';
+ if (reqResObj.request.cookies.length) {
+ cookies = reqResObj.request.cookies.reduce((acc, userCookie) => {
+ if (userCookie.active)
+ return `${acc}${userCookie.key}=${userCookie.value}; `;
+ return acc;
+ }, '');
+ }
+ headers.Cookie = cookies;
+
+ const wsLink = new WebSocketLink(
+ new SubscriptionClient(wsUri, {
+ reconnect: true,
+ timeout: 30000,
+ connectionParams: {
+ headers,
+ },
+ })
+ );
+
+ const apolloClient = new ApolloClient({
+ link: wsLink,
+ cache: new InMemoryCache(),
+ });
+
+ const query = gql`
+ ${reqResObj.request.body}
+ `;
+ const variables = reqResObj.request.bodyVariables
+ ? JSON.parse(reqResObj.request.bodyVariables)
+ : {};
+
+ apolloClient
+ .subscribe({
+ query,
+ variables,
+ })
+ .subscribe({
+ next(subsEvent) {
+ // Notify your application with the new arrived data
+ reqResObj.response.events.push(subsEvent.data);
+ const newReqRes = JSON.parse(JSON.stringify(reqResObj));
+ store.default.dispatch(actions.saveCurrentResponseData(newReqRes));
+ store.default.dispatch(actions.reqResUpdate(newReqRes));
+ },
+ error(err) {
+ console.error(err);
+ },
+ });
+ },
+
+ handleResponse(response, reqResObj) {
+ reqResObj.connection = 'closed';
+ reqResObj.connectionType = 'plain';
+ reqResObj.timeReceived = Date.now();
+
+ reqResObj.response.events.push(response.data);
+
+ store.default.dispatch(actions.reqResUpdate(reqResObj));
+ store.default.dispatch(actions.saveCurrentResponseData(reqResObj));
+ store.default.dispatch(actions.updateGraph(reqResObj));
+ },
+
+ handleError(errorsObj, reqResObj) {
+ reqResObj.connection = 'error';
+ reqResObj.timeReceived = Date.now();
+
+ reqResObj.response.events.push(JSON.parse(errorsObj));
+ store.default.dispatch(actions.saveCurrentResponseData(reqResObj));
+ store.default.dispatch(actions.reqResUpdate(reqResObj));
+ },
+
+ // objects that travel over IPC API have their properties alphabetized...
+ cookieFormatter(cookieArray) {
+ return cookieArray.map((eachCookie) => {
+ const cookieFormat = {
+ name: eachCookie.name,
+ value: eachCookie.value,
+ domain: eachCookie.domain,
+ hostOnly: eachCookie.hostonly ? eachCookie.hostonly : false,
+ path: eachCookie.path,
+ secure: eachCookie.secure ? eachCookie.secure : false,
+ httpOnly: eachCookie.httponly ? eachCookie.httponly : false,
+ session: eachCookie.session ? eachCookie.session : false,
+ expirationDate: eachCookie.expires ? eachCookie.expires : '',
+ };
+ return cookieFormat;
+ });
+ },
+
+ introspect(url, headers, cookies) {
+ const introspectionObject = {
+ url,
+ headers,
+ cookies,
+ };
+ api.send('introspect', JSON.stringify(introspectionObject));
+ api.receive('introspect-reply', (data) => {
+ if (data !== 'Error: Please enter a valid GraphQL API URI') {
+ // formatted for Codemirror hint and lint
+ const clientSchema = buildClientSchema(data);
+ // formatted for pretty schema display
+ const schemaSDL = printSchema(clientSchema);
+ const modifiedData = { schemaSDL, clientSchema };
+ store.default.dispatch(actions.setIntrospectionData(modifiedData));
+ } else {
+ store.default.dispatch(actions.setIntrospectionData(data));
+ }
+ });
+ },
+};
+
+export default graphQLController;
diff --git a/src/client/controllers/historyController.js b/src/client/controllers/historyController.js
index cd5e6399f..e503e51f3 100644
--- a/src/client/controllers/historyController.js
+++ b/src/client/controllers/historyController.js
@@ -1,53 +1,51 @@
-import format from "date-fns/format";
-import parse from "date-fns/parse";
-import * as store from "../store";
-import * as actions from "../actions/actions";
-import db from "../db";
-
-const historyController = {
- addHistoryToIndexedDb(reqRes) {
- db.history
- .put(reqRes)
- .catch((err) => console.log("Error in addToHistory", err));
- },
-
- deleteHistoryFromIndexedDb(id) {
- db.history
- .delete(id)
- .catch((err) => console.log("Error in deleteFromHistory", err));
- },
-
- clearHistoryFromIndexedDb() {
- db.history.clear().catch((err) => console.log(err));
- },
-
- getHistory() {
- db.table("history")
- .toArray()
- .then((history) => {
- const historyGroupsObj = history.reduce((groups, hist) => {
- const date = format(hist.created_at, "MM/DD/YYYY");
- if (!groups[date]) {
- groups[date] = [];
- }
- groups[date].push(hist);
- return groups;
- }, {});
- console.log("historyGroupsObj==>", historyGroupsObj);
- const historyGroupsArr = Object.keys(historyGroupsObj)
- .sort((a, b) => parse(b) - parse(a))
- .map((date) => {
- return {
- date,
- history: historyGroupsObj[date].sort(
- (a, b) => b.created_at - a.created_at
- ),
- };
- });
- store.default.dispatch(actions.getHistory(historyGroupsArr));
- })
- .catch((err) => console.log("Error in getHistory", err));
- },
-};
-
-export default historyController;
+import format from 'date-fns/format';
+import parse from 'date-fns/parse';
+import * as store from '../store';
+import * as actions from '../actions/actions';
+import db from '../db';
+
+const historyController = {
+ addHistoryToIndexedDb(reqRes) {
+ db.history
+ .put(reqRes)
+ .catch((err) => console.log('Error in addToHistory', err));
+ },
+
+ deleteHistoryFromIndexedDb(id) {
+ db.history
+ .delete(id)
+ .catch((err) => console.log('Error in deleteFromHistory', err));
+ },
+
+ clearHistoryFromIndexedDb() {
+ db.history.clear().catch((err) => console.log(err));
+ },
+
+ getHistory() {
+ db.table('history')
+ .toArray()
+ .then((history) => {
+ const historyGroupsObj = history.reduce((groups, hist) => {
+ const date = format(hist.created_at, 'MM/DD/YYYY');
+ if (!groups[date]) {
+ groups[date] = [];
+ }
+ groups[date].push(hist);
+ return groups;
+ }, {});
+ console.log('historyGroupsObj==>', historyGroupsObj);
+ const historyGroupsArr = Object.keys(historyGroupsObj)
+ .sort((a, b) => parse(b) - parse(a))
+ .map((date) => ({
+ date,
+ history: historyGroupsObj[date].sort(
+ (a, b) => b.created_at - a.created_at
+ ),
+ }));
+ store.default.dispatch(actions.getHistory(historyGroupsArr));
+ })
+ .catch((err) => console.log('Error in getHistory', err));
+ },
+};
+
+export default historyController;
diff --git a/src/client/controllers/reqResController.js b/src/client/controllers/reqResController.js
index 0f44c7054..f5cc443cf 100644
--- a/src/client/controllers/reqResController.js
+++ b/src/client/controllers/reqResController.js
@@ -1,243 +1,245 @@
-import * as store from "../store";
-import * as actions from "../actions/actions";
-import graphQLController from "./graphQLController.js";
-
-const { api } = window;
-const connectionController = {
- openConnectionArray: [],
-
- //toggles checked in state for entire reqResArray
- toggleSelectAll() {
- const { reqResArray } = store.default.getState().business;
-
- if (reqResArray.every((obj) => obj.checked === true)) {
- reqResArray.forEach((obj) => (obj.checked = false));
- } else {
- reqResArray.forEach((obj) => (obj.checked = true));
- }
- store.default.dispatch(actions.setChecksAndMinis(reqResArray));
- },
-
- openReqRes(id) {
- // listens for reqResUpdate event from main process telling it to update reqResobj
- // REST EVENTS
- //remove all previou listeners for 'reqresupdate' before starting to listen for 'reqresupdate' again
- api.removeAllListeners("reqResUpdate");
- api.receive("reqResUpdate", (reqResObj) => {
- if (
- (reqResObj.connection === "closed" ||
- reqResObj.connection === "error") &&
- reqResObj.timeSent &&
- reqResObj.timeReceived &&
- reqResObj.response.events.length > 0
- ) {
- store.default.dispatch(actions.updateGraph(reqResObj));
- }
- store.default.dispatch(actions.reqResUpdate(reqResObj));
- // If current selected response equals reqResObj received, update current response
- const currentID = store.default.getState().business.currentResponse.id;
- if (currentID === reqResObj.id) {
- store.default.dispatch(
- actions.saveCurrentResponseData(reqResObj, "currentID===reqresObj.id")
- );
- }
- });
- //Since only obj ID is passed in, next two lines get the current array of reqest objects and finds the one with matching ID
- const reqResArr = store.default.getState().business.reqResArray;
- const reqResObj = reqResArr.find((el) => el.id === id);
- if (reqResObj.request.method === "SUBSCRIPTION")
- graphQLController.openSubscription(reqResObj);
- else if (reqResObj.graphQL) {
- graphQLController.openGraphQLConnection(reqResObj);
- } else if (/wss?:\/\//.test(reqResObj.protocol)) {
- //create context bridge to wsController in node process to open connection, send the reqResObj and connection array
- api.send("open-ws", reqResObj, this.openConnectionArray);
-
- //update the connectionArray when connection is open from ws
- api.receive("update-connectionArray", (connectionArray) => {
- this.openConnectionArray.push(...connectionArray);
- });
- }
- //gRPC connection
- else if (reqResObj.gRPC) {
- api.send("open-grpc", reqResObj);
- //Standard HTTP?
- } else {
- api.send("open-http", reqResObj, this.openConnectionArray);
- }
- },
-
- openScheduledReqRes(id) {
- // listens for reqResUpdate event from main process telling it to update reqResobj
- // REST EVENTS
- api.removeAllListeners("reqResUpdate");
- api.receive("reqResUpdate", (reqResObj) => {
- if (
- (reqResObj.connection === "closed" ||
- reqResObj.connection === "error") &&
- reqResObj.timeSent &&
- reqResObj.timeReceived &&
- reqResObj.response.events.length > 0
- ) {
- store.default.dispatch(actions.updateGraph(reqResObj));
- }
- store.default.dispatch(actions.scheduledReqResUpdate(reqResObj));
- });
- //Since only obj ID is passed in, next two lines get the current array of reqest objects and finds the one with matching ID
- const reqResArr = store.default.getState().business.reqResArray;
- const reqResObj = reqResArr.find((el) => el.id === id);
- if (reqResObj.request.method === "SUBSCRIPTION")
- graphQLController.openSubscription(reqResObj);
- else if (reqResObj.graphQL) {
- graphQLController.openGraphQLConnection(reqResObj);
- } else if (/wss?:\/\//.test(reqResObj.protocol)) {
- //create context bridge to wsController in node process to open connection, send the reqResObj and connection array
- api.send("open-ws", reqResObj, this.openConnectionArray);
-
- //update the connectionArray when connection is open from ws
- api.receive("update-connectionArray", (connectionArray) => {
- this.openConnectionArray.push(...connectionArray);
- });
- }
- //gRPC connection
- else if (reqResObj.gRPC) {
- api.send("open-grpc", reqResObj);
- //Standard HTTP?
- } else {
- api.send("open-http", reqResObj, this.openConnectionArray);
- }
- },
-
- runCollectionTest(reqResArray) {
- api.removeAllListeners("reqResUpdate");
- let index = 0;
- api.receive("reqResUpdate", (reqResObj) => {
- if (
- (reqResObj.connection === "closed" ||
- reqResObj.connection === "error") &&
- reqResObj.timeSent &&
- reqResObj.timeReceived &&
- reqResObj.response.events.length > 0
- ) {
- store.default.dispatch(actions.updateGraph(reqResObj));
- }
- store.default.dispatch(actions.reqResUpdate(reqResObj));
-
- store.default.dispatch(
- actions.saveCurrentResponseData(reqResObj, "api.receive reqresupdate")
- );
- if (index < reqResArray.length) {
- runSingletest(reqResArray[index]);
- index += 1;
- }
- });
- const reqResObj = reqResArray[index];
-
- function runSingletest(reqResObj) {
- if (reqResObj.request.method === "SUBSCRIPTION")
- graphQLController.openSubscription(reqResObj);
- else if (reqResObj.graphQL) {
- graphQLController.openGraphQLConnectionAndRunCollection(reqResArray);
- } else if (/wss?:\/\//.test(reqResObj.protocol)) {
- //create context bridge to wsController in node process to open connection, send the reqResObj and connection array
- api.send("open-ws", reqResObj);
-
- //update the connectionArray when connection is open from ws
- api.receive("update-connectionArray", (connectionArray) => {
- this.openConnectionArray.push(...connectionArray);
- });
- }
- //gRPC connection
- else if (reqResObj.gRPC) {
- api.send("open-grpc", reqResObj);
- //Standard HTTP?
- } else {
- api.send("open-http", reqResObj);
- }
- }
- runSingletest(reqResObj);
- index += 1;
- },
-
- openAllSelectedReqRes() {
- connectionController.closeAllReqRes();
-
- const { reqResArray } = store.default.getState().business;
-
- reqResArray.forEach((reqRes) => connectionController.openReqRes(reqRes.id));
- },
-
- getConnectionObject(id) {
- return this.openConnectionArray.find((obj) => (obj.id = id));
- },
-
- setReqResConnectionToClosed(id) {
- const reqResArr = store.default.getState().business.reqResArray;
-
- const foundReqRes = JSON.parse(
- JSON.stringify(reqResArr.find((reqRes) => reqRes.id === id))
- );
-
- foundReqRes.connection = "closed";
- store.default.dispatch(actions.reqResUpdate(foundReqRes));
- store.default.dispatch(
- actions.saveCurrentResponseData(
- foundReqRes,
- "foundreqres.connection closed"
- )
- );
- },
-
- closeReqRes(reqResObj) {
- if (reqResObj.protocol.includes("http")) {
- api.send("close-http", reqResObj);
- }
-
- const { id } = reqResObj;
- this.setReqResConnectionToClosed(id);
-
- // WS is the only protocol using openConnectionArray
- const foundAbortController = this.openConnectionArray.find(
- (obj) => obj.id === id
- );
- if (foundAbortController && foundAbortController.protocol === "WS") {
- api.send("close-ws");
- }
- this.openConnectionArray = this.openConnectionArray.filter(
- (obj) => obj.id !== id
- );
- },
-
- /* Closes all open endpoint */
- closeAllReqRes() {
- const { reqResArray } = store.default.getState().business;
- reqResArray.forEach((reqRes) => connectionController.closeReqRes(reqRes));
- },
-
- clearAllReqRes() {
- connectionController.closeAllReqRes();
- store.default.dispatch(actions.reqResClear());
- },
-
- //toggles minimized in ReqRes array in state
- toggleMinimizeAll() {
- const { reqResArray } = store.default.getState().business;
-
- if (reqResArray.every((obj) => obj.minimized === true)) {
- reqResArray.forEach((obj) => (obj.minimized = false));
- } else {
- reqResArray.forEach((obj) => (obj.minimized = true));
- }
- store.default.dispatch(actions.setChecksAndMinis(reqResArray));
- },
- //clears a dataPoint from state
- clearGraph() {
- store.default.dispatch(actions.clearGraph());
- },
- // clears ALL data points from state
- clearAllGraph() {
- store.default.dispatch(actions.clearAllGraph());
- },
-};
-
-export default connectionController;
+import * as store from '../store';
+import * as actions from '../actions/actions';
+import graphQLController from './graphQLController.js';
+
+const { api } = window;
+const connectionController = {
+ openConnectionArray: [],
+
+ // toggles checked in state for entire reqResArray
+ toggleSelectAll() {
+ const { reqResArray } = store.default.getState().business;
+
+ if (reqResArray.every((obj) => obj.checked === true)) {
+ reqResArray.forEach((obj) => (obj.checked = false));
+ } else {
+ reqResArray.forEach((obj) => (obj.checked = true));
+ }
+ store.default.dispatch(actions.setChecksAndMinis(reqResArray));
+ },
+
+ // listens for reqResUpdate event from main process telling it to update reqResObj REST EVENTS
+ openReqRes(id) {
+ // remove all previous listeners for 'reqResUpdate' before starting to listen for 'reqResUpdate' again
+ api.removeAllListeners('reqResUpdate');
+ api.receive('reqResUpdate', (reqResObj) => {
+ if (
+ (reqResObj.connection === 'closed' ||
+ reqResObj.connection === 'error') &&
+ reqResObj.timeSent &&
+ reqResObj.timeReceived &&
+ reqResObj.response.events.length > 0
+ ) {
+ store.default.dispatch(actions.updateGraph(reqResObj));
+ }
+ store.default.dispatch(actions.reqResUpdate(reqResObj));
+ // If current selected response equals reqResObj received, update current response
+ const currentID = store.default.getState().business.currentResponse.id;
+ if (currentID === reqResObj.id) {
+ store.default.dispatch(
+ actions.saveCurrentResponseData(reqResObj, 'currentID===reqresObj.id')
+ );
+ }
+ });
+ // Since only obj ID is passed in, next two lines get the current array of request objects and finds the one with matching ID
+ const reqResArr = store.default.getState().business.reqResArray;
+ const reqResObj = reqResArr.find((el) => el.id === id);
+ if (reqResObj.request.method === 'SUBSCRIPTION')
+ graphQLController.openSubscription(reqResObj);
+ else if (reqResObj.graphQL) {
+ graphQLController.openGraphQLConnection(reqResObj);
+ } else if (/wss?:\/\//.test(reqResObj.protocol) && !reqResObj.webrtc) {
+ // create context bridge to wsController in node process to open connection, send the reqResObj and connection array
+ api.send('open-ws', reqResObj, this.openConnectionArray);
+
+ // update the connectionArray when connection is open from ws
+ api.receive('update-connectionArray', (connectionArray) => {
+ this.openConnectionArray.push(...connectionArray);
+ });
+ }
+ // gRPC connection
+ else if (reqResObj.gRPC) {
+ api.send('open-grpc', reqResObj);
+ // Standard HTTP?
+ } else if (reqResObj.openapi) {
+ console.log('got an open api request to fill');
+ console.log(reqResObj);
+ } else {
+ api.send('open-http', reqResObj, this.openConnectionArray);
+ }
+ },
+
+ openScheduledReqRes(id) {
+ // listens for reqResUpdate event from main process telling it to update reqResObj
+ // REST EVENTS
+ api.removeAllListeners('reqResUpdate');
+ api.receive('reqResUpdate', (reqResObj) => {
+ if (
+ (reqResObj.connection === 'closed' ||
+ reqResObj.connection === 'error') &&
+ reqResObj.timeSent &&
+ reqResObj.timeReceived &&
+ reqResObj.response.events.length > 0
+ ) {
+ store.default.dispatch(actions.updateGraph(reqResObj));
+ }
+ store.default.dispatch(actions.scheduledReqResUpdate(reqResObj));
+ });
+ // Since only obj ID is passed in, next two lines get the current array of request objects and finds the one with matching ID
+ const reqResArr = store.default.getState().business.reqResArray;
+ const reqResObj = reqResArr.find((el) => el.id === id);
+ if (reqResObj.request.method === 'SUBSCRIPTION')
+ graphQLController.openSubscription(reqResObj);
+ else if (reqResObj.graphQL) {
+ graphQLController.openGraphQLConnection(reqResObj);
+ } else if (/wss?:\/\//.test(reqResObj.protocol)) {
+ // create context bridge to wsController in node process to open connection, send the reqResObj and connection array
+ api.send('open-ws', reqResObj, this.openConnectionArray);
+
+ // update the connectionArray when connection is open from ws
+ api.receive('update-connectionArray', (connectionArray) => {
+ this.openConnectionArray.push(...connectionArray);
+ });
+ }
+ // gRPC connection
+ else if (reqResObj.gRPC) {
+ api.send('open-grpc', reqResObj);
+ // Standard HTTP?
+ } else {
+ api.send('open-http', reqResObj, this.openConnectionArray);
+ }
+ },
+
+ runCollectionTest(reqResArray) {
+ api.removeAllListeners('reqResUpdate');
+ let index = 0;
+ api.receive('reqResUpdate', (reqResObj) => {
+ if (
+ (reqResObj.connection === 'closed' ||
+ reqResObj.connection === 'error') &&
+ reqResObj.timeSent &&
+ reqResObj.timeReceived &&
+ reqResObj.response.events.length > 0
+ ) {
+ store.default.dispatch(actions.updateGraph(reqResObj));
+ }
+ store.default.dispatch(actions.reqResUpdate(reqResObj));
+
+ store.default.dispatch(
+ actions.saveCurrentResponseData(reqResObj, 'api.receive reqresupdate')
+ );
+ if (index < reqResArray.length) {
+ runSingletest(reqResArray[index]);
+ index += 1;
+ }
+ });
+ const reqResObj = reqResArray[index];
+
+ function runSingletest(reqResObj) {
+ if (reqResObj.request.method === 'SUBSCRIPTION')
+ graphQLController.openSubscription(reqResObj);
+ else if (reqResObj.graphQL) {
+ graphQLController.openGraphQLConnectionAndRunCollection(reqResArray);
+ } else if (/wss?:\/\//.test(reqResObj.protocol)) {
+ // create context bridge to wsController in node process to open connection, send the reqResObj and connection array
+ api.send('open-ws', reqResObj);
+
+ // update the connectionArray when connection is open from ws
+ api.receive('update-connectionArray', (connectionArray) => {
+ this.openConnectionArray.push(...connectionArray);
+ });
+ }
+ // gRPC connection
+ else if (reqResObj.gRPC) {
+ api.send('open-grpc', reqResObj);
+ // Standard HTTP?
+ } else {
+ api.send('open-http', reqResObj);
+ }
+ }
+ runSingletest(reqResObj);
+ index += 1;
+ },
+
+ openAllSelectedReqRes() {
+ connectionController.closeAllReqRes();
+
+ const { reqResArray } = store.default.getState().business;
+
+ reqResArray.forEach((reqRes) => connectionController.openReqRes(reqRes.id));
+ },
+
+ getConnectionObject(id) {
+ return this.openConnectionArray.find((obj) => (obj.id = id));
+ },
+
+ setReqResConnectionToClosed(id) {
+ const reqResArr = store.default.getState().business.reqResArray;
+
+ const foundReqRes = JSON.parse(
+ JSON.stringify(reqResArr.find((reqRes) => reqRes.id === id))
+ );
+
+ foundReqRes.connection = 'closed';
+ store.default.dispatch(actions.reqResUpdate(foundReqRes));
+ store.default.dispatch(
+ actions.saveCurrentResponseData(
+ foundReqRes,
+ 'foundreqres.connection closed'
+ )
+ );
+ },
+
+ closeReqRes(reqResObj) {
+ if (reqResObj.protocol.includes('http')) {
+ api.send('close-http', reqResObj);
+ }
+
+ const { id } = reqResObj;
+ this.setReqResConnectionToClosed(id);
+
+ // WS is the only protocol using openConnectionArray
+ const foundAbortController = this.openConnectionArray.find(
+ (obj) => obj.id === id
+ );
+ if (foundAbortController && foundAbortController.protocol === 'WS') {
+ api.send('close-ws');
+ }
+ this.openConnectionArray = this.openConnectionArray.filter(
+ (obj) => obj.id !== id
+ );
+ },
+
+ /* Closes all open endpoint */
+ closeAllReqRes() {
+ const { reqResArray } = store.default.getState().business;
+ reqResArray.forEach((reqRes) => connectionController.closeReqRes(reqRes));
+ },
+
+ clearAllReqRes() {
+ connectionController.closeAllReqRes();
+ store.default.dispatch(actions.reqResClear());
+ },
+
+ // toggles minimized in ReqRes array in state
+ toggleMinimizeAll() {
+ const { reqResArray } = store.default.getState().business;
+
+ if (reqResArray.every((obj) => obj.minimized === true)) {
+ reqResArray.forEach((obj) => (obj.minimized = false));
+ } else {
+ reqResArray.forEach((obj) => (obj.minimized = true));
+ }
+ store.default.dispatch(actions.setChecksAndMinis(reqResArray));
+ },
+ // clears a dataPoint from state
+ clearGraph() {
+ store.default.dispatch(actions.clearGraph());
+ },
+ // clears ALL data points from state
+ clearAllGraph() {
+ store.default.dispatch(actions.clearAllGraph());
+ },
+};
+
+export default connectionController;
diff --git a/src/client/controllers/webrtcPeerController.js b/src/client/controllers/webrtcPeerController.js
new file mode 100644
index 000000000..e2c969f9e
--- /dev/null
+++ b/src/client/controllers/webrtcPeerController.js
@@ -0,0 +1,153 @@
+/**
+ * exports Peer class for use in WebRTC implementations
+ *
+ * @file webrtcPeerController.js
+ * @author Ted Craig
+ * @since 1.0.0
+ */
+
+export default class Peer {
+ // ┌──────────────────────────────┐
+ // │ CONSTRUCTOR │
+ // └──────────────────────────────┘
+ constructor(initConfig) {
+ this.initConfig = initConfig;
+ this._roles = {
+ INITIATOR: 'INITIATOR',
+ PENDING: 'PENDING',
+ RECEIVER: 'RECEIVER',
+ };
+ this.role = this._roles.PENDING;
+ this._createPeer(initConfig);
+ this._initICECandidateEvents();
+ }
+
+ // ┌──────────────────────────────┐
+ // │ GET BROWSER RTC │
+ // └──────────────────────────────┘
+
+ getBrowserRTC() {
+ if (typeof globalThis === 'undefined') return null;
+ const wrtc = {
+ RTCPeerConnection:
+ globalThis.RTCPeerConnection ||
+ globalThis.mozRTCPeerConnection ||
+ globalThis.webkitRTCPeerConnection,
+ RTCSessionDescription:
+ globalThis.RTCSessionDescription ||
+ globalThis.mozRTCSessionDescription ||
+ globalThis.webkitRTCSessionDescription,
+ RTCIceCandidate:
+ globalThis.RTCIceCandidate ||
+ globalThis.mozRTCIceCandidate ||
+ globalThis.webkitRTCIceCandidate,
+ };
+ if (!wrtc.RTCPeerConnection) return null;
+ return wrtc;
+ }
+
+ // ┌──────────────────────────────┐
+ // │ _ CREATE PEER │
+ // └──────────────────────────────┘
+ _createPeer(config) {
+ // grab RTCPeerConnection from globalThis
+ // console.log('[webrtcPeerController][Peer][_createPeer] getBrowserRTC():');
+ // console.log(this.getBrowserRTC());
+ const Wrtc = this.getBrowserRTC();
+
+ // instantiate a new peer connection with config and return
+ this.connection = new Wrtc.RTCPeerConnection(config);
+ }
+
+ // ┌──────────────────────────────┐
+ // │ _ INIT ICE CANDIDATE EVENTS │
+ // └──────────────────────────────┘
+ _initICECandidateEvents() {
+ // setup ice candidate event handler
+ // listen for ICE candidates. Each time a candidate is added to the list, re-log the whole SDP
+ // this.connection.onicecandidate = (event) => {
+ // if (
+ // event &&
+ // event.target &&
+ // event.target.iceGatheringState === 'complete'
+ // ) {
+ // console.log(
+ // 'done gathering candidates - got iceGatheringState complete'
+ // );
+ // } else if (event && event.candidate == null) {
+ // console.log('done gathering candidates - got null candidate');
+ // } else {
+ // console.log(
+ // event.target.iceGatheringState,
+ // event,
+ // this.connection.localDescription
+ // );
+ // // console.log('corresponding SDP for above ICE candidate in JSON:');
+ // // console.log(JSON.stringify(this.connection.localDescription));
+ // }
+ // };
+ }
+
+ // ┌──────────────────────────────┐
+ // │ INIT DATA CHANNEL AND EVENTS │
+ // └──────────────────────────────┘
+ initDataChannelAndEvents() {
+ // check for role before continuing
+ if (this.role === this._roles.PENDING) {
+ console.log(`peer role is ${this.role}. Skipping channel init.`);
+ } else if (this.role === this._roles.INITIATOR) {
+ // on our local connection, create a data channel and pass it the name "chatRoom1"
+ const dataChannel = this.connection.createDataChannel('chatRoom1');
+
+ // when the channel is opened ...
+ dataChannel.onopen = (event) => console.log('Connection opened!');
+
+ // when the channel is closed ...
+ dataChannel.onclose = (event) =>
+ console.log('Connection closed! Goodbye (^-^)');
+
+ // when message received...
+ dataChannel.onmessage = (event) =>
+ console.log('Received Msg: ' + event.data);
+ } else if (this.role === this._roles.RECEIVER) {
+ this.connection.ondatachannel = (event) => {
+ // create new property on rc object and assign it to be the incoming data channel (*** is this the name that was passed in by the local client? ***)
+ const incomingChannel = event.channel;
+ this.connection.dataChannel = incomingChannel;
+ // when the channel is opened ...
+ //remoteConnection.dataChannel.onopen = event => console.log("Connection opened!");
+ this.connection.dataChannel.onopen = (event) =>
+ console.log('Connection opened!');
+ // when the channel is closed ...
+ //remoteConnection.dataChannel.onclose = event => console.log("Connection closed! Goodbye (^-^)");
+ this.connection.dataChannel.onclose = (event) =>
+ console.log('Connection closed! Goodbye (^-^)');
+ // when message received...
+ //remoteConnection.dataChannel.onmessage = event => console.log("PeerA: " + event.data);
+ this.connection.dataChannel.onmessage = (event) =>
+ console.log('Received Msg' + event.data);
+ };
+ }
+ }
+
+ // ┌──────────────────────────────┐
+ // │ CREATE LOCAL SDP │
+ // └──────────────────────────────┘
+ createLocalSdp() {
+ if (this.role === this._roles.PENDING) this.role = this._roles.INITIATOR;
+
+ if (this.role === this._roles.INITIATOR) {
+ this.connection
+ .createOffer()
+ .then((offer) => this.connection.setLocalDescription(offer))
+ .then((a) => {
+ console.log('offer set successfully!');
+ // return the offer/localDescription
+ return this.connection.localDescription;
+ })
+ .catch(
+ `[webrtcPeerController][createLocalSdp] ERROR while attempting to set offer`
+ );
+ }
+ }
+}
diff --git a/src/client/db.js b/src/client/db.js
index e5e5f39cb..70d414e13 100644
--- a/src/client/db.js
+++ b/src/client/db.js
@@ -1,30 +1,30 @@
-import Dexie from "dexie";
-
-const db = new Dexie("Swell");
-
-db.on("versionchange", function (event) {
- if (
- confirm(
- `Another page tries to upgrade the database to version ${event.newVersion}. Accept?`
- )
- ) {
- // Refresh current webapp so that it starts working with newer DB schema.
- return window.location.reload();
- }
- // Will let user finish its work in this window and
- // block the other window from upgrading.
- return false;
-});
-
-db.version(2).stores({
- history: "id, created_at",
- collections: "id, created_at, &name",
-});
-
-db.version(1).stores({
- history: "id, created_at",
-});
-
-// db.open()
-
-export default db;
+import Dexie from 'dexie';
+
+const db = new Dexie('Swell');
+
+db.on('versionchange', (event) => {
+ if (
+ confirm(
+ `Another page tries to upgrade the database to version ${event.newVersion}. Accept?`
+ )
+ ) {
+ // Refresh current webapp so that it starts working with newer DB schema.
+ return window.location.reload();
+ }
+ // Will let user finish its work in this window and
+ // block the other window from upgrading.
+ return false;
+});
+
+db.version(2).stores({
+ history: 'id, created_at',
+ collections: 'id, created_at, &name',
+});
+
+db.version(1).stores({
+ history: 'id, created_at',
+});
+
+// db.open()
+
+export default db;
diff --git a/src/client/docs/twitter-example.json b/src/client/docs/twitter-example.json
new file mode 100644
index 000000000..24b66aa28
--- /dev/null
+++ b/src/client/docs/twitter-example.json
@@ -0,0 +1,4463 @@
+{
+ "openapi" : "3.0.0",
+ "info" : {
+ "description" : "Twitter API v2 available endpoints",
+ "version" : "2.20",
+ "title" : "Twitter API v2",
+ "termsOfService" : "https://developer.twitter.com/en/developer-terms/agreement-and-policy.html",
+ "contact" : {
+ "name" : "Twitter Developers",
+ "url" : "https://developer.twitter.com/"
+ },
+ "license" : {
+ "name" : "Twitter Developer Agreement and Policy",
+ "url" : "https://developer.twitter.com/en/developer-terms/agreement-and-policy.html"
+ }
+ },
+ "servers" : [ {
+ "description" : "Twitter API",
+ "url" : "https://api.twitter.com"
+ } ],
+ "tags" : [ {
+ "name" : "General",
+ "description" : "Miscellaneous endpoints for general API functionality",
+ "externalDocs" : {
+ "description" : "Find out more",
+ "url" : "https://developer.twitter.com/en/docs/twitter-api"
+ }
+ }, {
+ "name" : "Tweets",
+ "description" : "Endpoints related to retrieving, searching, and modifying Tweets",
+ "externalDocs" : {
+ "description" : "Find out more",
+ "url" : "https://developer.twitter.com/en/docs/twitter-api/tweets/lookup"
+ }
+ }, {
+ "name" : "Users",
+ "description" : "Endpoints related to retrieving, managing relationships of Users",
+ "externalDocs" : {
+ "description" : "Find out more",
+ "url" : "https://developer.twitter.com/en/docs/twitter-api/users/lookup"
+ }
+ } ],
+ "paths" : {
+ "/2/users" : {
+ "get" : {
+ "security" : [ {
+ "BearerToken" : [ ],
+ "UserToken" : [ ]
+ } ],
+ "tags" : [ "Users" ],
+ "summary" : "User lookup by IDs",
+ "description" : "This endpoint returns information about users. Specify users by their ID.",
+ "operationId" : "findUsersById",
+ "parameters" : [ {
+ "name" : "ids",
+ "in" : "query",
+ "description" : "Required. A list of User IDs, comma-separated. You can specify up to 100 IDs.",
+ "required" : true,
+ "example" : "2244994945,6253282,12",
+ "schema" : {
+ "type" : "array",
+ "items" : {
+ "$ref" : "#/components/schemas/UserID"
+ },
+ "minItems" : 1,
+ "maxItems" : 100
+ },
+ "style" : "form",
+ "explode" : false
+ }, {
+ "$ref" : "#/components/parameters/UserExpansionsParameter"
+ }, {
+ "$ref" : "#/components/parameters/TweetFieldsParameter"
+ }, {
+ "$ref" : "#/components/parameters/UserFieldsParameter"
+ } ],
+ "responses" : {
+ "200" : {
+ "description" : "The request was successful",
+ "content" : {
+ "application/json" : {
+ "schema" : {
+ "$ref" : "#/components/schemas/MultiUserLookupResponse"
+ }
+ }
+ }
+ },
+ "default" : {
+ "$ref" : "#/components/responses/HttpErrorResponse"
+ }
+ }
+ }
+ },
+ "/2/users/{id}" : {
+ "get" : {
+ "security" : [ {
+ "BearerToken" : [ ],
+ "UserToken" : [ ]
+ } ],
+ "tags" : [ "Users" ],
+ "summary" : "User lookup by ID",
+ "description" : "This endpoint returns information about a user. Specify user by ID.",
+ "operationId" : "findUserById",
+ "parameters" : [ {
+ "name" : "id",
+ "in" : "path",
+ "description" : "Required. A User ID.",
+ "required" : true,
+ "example" : "2244994945",
+ "schema" : {
+ "$ref" : "#/components/schemas/UserID"
+ }
+ }, {
+ "$ref" : "#/components/parameters/UserExpansionsParameter"
+ }, {
+ "$ref" : "#/components/parameters/TweetFieldsParameter"
+ }, {
+ "$ref" : "#/components/parameters/UserFieldsParameter"
+ } ],
+ "responses" : {
+ "200" : {
+ "description" : "The request was successful",
+ "content" : {
+ "application/json" : {
+ "schema" : {
+ "$ref" : "#/components/schemas/SingleUserLookupResponse"
+ }
+ }
+ }
+ },
+ "default" : {
+ "$ref" : "#/components/responses/HttpErrorResponse"
+ }
+ }
+ }
+ },
+ "/2/users/by" : {
+ "get" : {
+ "security" : [ {
+ "BearerToken" : [ ],
+ "UserToken" : [ ]
+ } ],
+ "tags" : [ "Users" ],
+ "summary" : "User lookup by usernames",
+ "description" : "This endpoint returns information about users. Specify users by their username.",
+ "operationId" : "findUsersByUsername",
+ "parameters" : [ {
+ "name" : "usernames",
+ "in" : "query",
+ "description" : "Required . A list of usernames, comma-separated. You can specify up to 100 usernames.",
+ "required" : true,
+ "example" : "TwitterDev,TwitterAPI,jack",
+ "schema" : {
+ "type" : "array",
+ "items" : {
+ "$ref" : "#/components/schemas/UserName"
+ },
+ "minItems" : 1,
+ "maxItems" : 100
+ },
+ "style" : "form",
+ "explode" : false
+ }, {
+ "$ref" : "#/components/parameters/UserExpansionsParameter"
+ }, {
+ "$ref" : "#/components/parameters/TweetFieldsParameter"
+ }, {
+ "$ref" : "#/components/parameters/UserFieldsParameter"
+ } ],
+ "responses" : {
+ "200" : {
+ "description" : "The request was successful",
+ "content" : {
+ "application/json" : {
+ "schema" : {
+ "$ref" : "#/components/schemas/MultiUserLookupResponse"
+ }
+ }
+ }
+ },
+ "default" : {
+ "$ref" : "#/components/responses/HttpErrorResponse"
+ }
+ }
+ }
+ },
+ "/2/users/by/username/{username}" : {
+ "get" : {
+ "security" : [ {
+ "BearerToken" : [ ],
+ "UserToken" : [ ]
+ } ],
+ "tags" : [ "Users" ],
+ "summary" : "User lookup by username",
+ "description" : "This endpoint returns information about a user. Specify user by username.",
+ "operationId" : "findUserByUsername",
+ "parameters" : [ {
+ "name" : "username",
+ "in" : "path",
+ "required" : true,
+ "description" : "Required. A username.",
+ "example" : "TwitterDev",
+ "schema" : {
+ "$ref" : "#/components/schemas/UserName"
+ }
+ }, {
+ "$ref" : "#/components/parameters/UserExpansionsParameter"
+ }, {
+ "$ref" : "#/components/parameters/TweetFieldsParameter"
+ }, {
+ "$ref" : "#/components/parameters/UserFieldsParameter"
+ } ],
+ "responses" : {
+ "200" : {
+ "description" : "The request was successful",
+ "content" : {
+ "application/json" : {
+ "schema" : {
+ "$ref" : "#/components/schemas/SingleUserLookupResponse"
+ }
+ }
+ }
+ },
+ "default" : {
+ "$ref" : "#/components/responses/HttpErrorResponse"
+ }
+ }
+ }
+ },
+ "/2/users/{id}/blocking" : {
+ "post" : {
+ "tags" : [ "Users" ],
+ "summary" : "Block User by User ID",
+ "description" : "Causes the user (in the path) to block the target user. The user (in the path) must match the user context authorizing the request",
+ "operationId" : "usersIdBlock",
+ "security" : [ {
+ "UserToken" : [ ]
+ } ],
+ "parameters" : [ {
+ "name" : "id",
+ "in" : "path",
+ "description" : "The ID of the user that is requesting to block the target user",
+ "required" : true,
+ "schema" : {
+ "$ref" : "#/components/schemas/UserID"
+ }
+ } ],
+ "requestBody" : {
+ "content" : {
+ "application/json" : {
+ "schema" : {
+ "type" : "object",
+ "required" : [ "target_user_id" ],
+ "properties" : {
+ "target_user_id" : {
+ "type" : "string",
+ "pattern" : "^[0-9]{1,19}$"
+ }
+ }
+ }
+ }
+ }
+ },
+ "responses" : {
+ "200" : {
+ "description" : "The request was successful",
+ "content" : {
+ "application/json" : {
+ "schema" : {
+ "$ref" : "#/components/schemas/UsersBlockingMutationResponse"
+ }
+ }
+ }
+ },
+ "default" : {
+ "$ref" : "#/components/responses/HttpErrorResponse"
+ }
+ }
+ },
+ "get" : {
+ "tags" : [ "Users" ],
+ "summary" : "Returns user objects that are blocked by provided user ID",
+ "description" : "Returns a list of users that are blocked by the provided user ID",
+ "operationId" : "usersIdBlocking",
+ "security" : [ {
+ "UserToken" : [ ]
+ } ],
+ "parameters" : [ {
+ "name" : "id",
+ "in" : "path",
+ "description" : "The ID of the user for whom to return results",
+ "required" : true,
+ "schema" : {
+ "$ref" : "#/components/schemas/UserID"
+ }
+ }, {
+ "name" : "max_results",
+ "in" : "query",
+ "description" : "The maximum number of results",
+ "required" : false,
+ "schema" : {
+ "type" : "integer",
+ "format" : "int32",
+ "minimum" : 1,
+ "maximum" : 1000
+ }
+ }, {
+ "name" : "pagination_token",
+ "in" : "query",
+ "description" : "This value is populated by passing the 'next_token' or 'previous_token' returned in a request to paginate through results.",
+ "required" : false,
+ "schema" : {
+ "type" : "string",
+ "minLength" : 16,
+ "maxLength" : 16
+ }
+ } ],
+ "responses" : {
+ "200" : {
+ "description" : "The request was successful",
+ "content" : {
+ "application/json" : {
+ "schema" : {
+ "$ref" : "#/components/schemas/GenericMultipleUsersLookupResponse"
+ }
+ }
+ }
+ },
+ "default" : {
+ "$ref" : "#/components/responses/HttpErrorResponse"
+ }
+ }
+ }
+ },
+ "/2/users/{source_user_id}/blocking/{target_user_id}" : {
+ "delete" : {
+ "tags" : [ "Users" ],
+ "summary" : "Unblock User by User ID",
+ "description" : "Causes the source user to unblock the target user. The source user must match the user context authorizing the request",
+ "operationId" : "usersIdUnblock",
+ "security" : [ {
+ "UserToken" : [ ]
+ } ],
+ "parameters" : [ {
+ "name" : "source_user_id",
+ "in" : "path",
+ "description" : "The ID of the user that is requesting to unblock the target user",
+ "required" : true,
+ "schema" : {
+ "$ref" : "#/components/schemas/UserID"
+ }
+ }, {
+ "name" : "target_user_id",
+ "in" : "path",
+ "description" : "The ID of the user that the source user is requesting to unblock",
+ "required" : true,
+ "schema" : {
+ "$ref" : "#/components/schemas/UserID"
+ }
+ } ],
+ "responses" : {
+ "200" : {
+ "description" : "The request was successful",
+ "content" : {
+ "application/json" : {
+ "schema" : {
+ "$ref" : "#/components/schemas/UsersBlockingMutationResponse"
+ }
+ }
+ }
+ },
+ "default" : {
+ "$ref" : "#/components/responses/HttpErrorResponse"
+ }
+ }
+ }
+ },
+ "/2/users/{source_user_id}/muting/{target_user_id}" : {
+ "delete" : {
+ "tags" : [ "Users" ],
+ "summary" : "Unmute User by User ID",
+ "description" : "Causes the source user to unmute the target user. The source user must match the user context authorizing the request",
+ "operationId" : "usersIdUnmute",
+ "security" : [ {
+ "UserToken" : [ ]
+ } ],
+ "parameters" : [ {
+ "name" : "source_user_id",
+ "in" : "path",
+ "description" : "The ID of the user that is requesting to unmute the target user",
+ "required" : true,
+ "schema" : {
+ "$ref" : "#/components/schemas/UserID"
+ }
+ }, {
+ "name" : "target_user_id",
+ "in" : "path",
+ "description" : "The ID of the user that the source user is requesting to unmute",
+ "required" : true,
+ "schema" : {
+ "$ref" : "#/components/schemas/UserID"
+ }
+ } ],
+ "responses" : {
+ "200" : {
+ "description" : "The request was successful",
+ "content" : {
+ "application/json" : {
+ "schema" : {
+ "$ref" : "#/components/schemas/UsersMutingMutationResponse"
+ }
+ }
+ }
+ },
+ "default" : {
+ "$ref" : "#/components/responses/HttpErrorResponse"
+ }
+ }
+ }
+ },
+ "/2/users/{id}/muting" : {
+ "post" : {
+ "tags" : [ "Users" ],
+ "summary" : "Mute User by User ID",
+ "description" : "Causes the user (in the path) to mute the target user. The user (in the path) must match the user context authorizing the request",
+ "operationId" : "usersIdMute",
+ "security" : [ {
+ "UserToken" : [ ]
+ } ],
+ "parameters" : [ {
+ "name" : "id",
+ "in" : "path",
+ "description" : "The ID of the user that is requesting to mute the target user",
+ "required" : true,
+ "schema" : {
+ "$ref" : "#/components/schemas/UserID"
+ }
+ } ],
+ "requestBody" : {
+ "content" : {
+ "application/json" : {
+ "schema" : {
+ "type" : "object",
+ "required" : [ "target_user_id" ],
+ "properties" : {
+ "target_user_id" : {
+ "type" : "string",
+ "pattern" : "^[0-9]{1,19}$"
+ }
+ }
+ }
+ }
+ }
+ },
+ "responses" : {
+ "200" : {
+ "description" : "The request was successful",
+ "content" : {
+ "application/json" : {
+ "schema" : {
+ "$ref" : "#/components/schemas/UsersMutingMutationResponse"
+ }
+ }
+ }
+ },
+ "default" : {
+ "$ref" : "#/components/responses/HttpErrorResponse"
+ }
+ }
+ }
+ },
+ "/2/users/{id}/followers" : {
+ "get" : {
+ "tags" : [ "Users" ],
+ "summary" : "Returns user objects that follow the provided user ID",
+ "description" : "Returns a list of users that follow the provided user ID",
+ "operationId" : "usersIdFollowers",
+ "security" : [ {
+ "BearerToken" : [ ],
+ "UserToken" : [ ]
+ } ],
+ "parameters" : [ {
+ "name" : "id",
+ "in" : "path",
+ "description" : "The ID of the user for whom to return results",
+ "required" : true,
+ "schema" : {
+ "$ref" : "#/components/schemas/UserID"
+ }
+ }, {
+ "name" : "max_results",
+ "in" : "query",
+ "description" : "The maximum number of results",
+ "required" : false,
+ "schema" : {
+ "type" : "integer",
+ "format" : "int32",
+ "minimum" : 1,
+ "maximum" : 1000
+ }
+ }, {
+ "name" : "pagination_token",
+ "in" : "query",
+ "description" : "This value is populated by passing the 'next_token' or 'previous_token' returned in a request to paginate through results.",
+ "required" : false,
+ "schema" : {
+ "type" : "string",
+ "minLength" : 16,
+ "maxLength" : 16
+ }
+ } ],
+ "responses" : {
+ "200" : {
+ "description" : "The request was successful",
+ "content" : {
+ "application/json" : {
+ "schema" : {
+ "$ref" : "#/components/schemas/GenericMultipleUsersLookupResponse"
+ }
+ }
+ }
+ },
+ "default" : {
+ "$ref" : "#/components/responses/HttpErrorResponse"
+ }
+ }
+ }
+ },
+ "/2/users/{id}/following" : {
+ "get" : {
+ "security" : [ {
+ "BearerToken" : [ ],
+ "UserToken" : [ ]
+ } ],
+ "tags" : [ "Users" ],
+ "summary" : "Following by User ID",
+ "description" : "Returns a list of users that are being followed by the provided user ID",
+ "operationId" : "usersIdFollowing",
+ "parameters" : [ {
+ "name" : "id",
+ "in" : "path",
+ "description" : "The ID of the user for whom to return results",
+ "required" : true,
+ "schema" : {
+ "$ref" : "#/components/schemas/UserID"
+ }
+ }, {
+ "name" : "max_results",
+ "in" : "query",
+ "description" : "The maximum number of results",
+ "required" : false,
+ "schema" : {
+ "type" : "integer",
+ "format" : "int32",
+ "minimum" : 1,
+ "maximum" : 1000
+ }
+ }, {
+ "name" : "pagination_token",
+ "in" : "query",
+ "description" : "This value is populated by passing the 'next_token' or 'previous_token' returned in a request to paginate through results.",
+ "required" : false,
+ "schema" : {
+ "type" : "string",
+ "minLength" : 16,
+ "maxLength" : 16
+ }
+ } ],
+ "responses" : {
+ "200" : {
+ "description" : "The request was successful",
+ "content" : {
+ "application/json" : {
+ "schema" : {
+ "$ref" : "#/components/schemas/UsersFollowingLookupResponse"
+ }
+ }
+ }
+ },
+ "default" : {
+ "$ref" : "#/components/responses/HttpErrorResponse"
+ }
+ }
+ },
+ "post" : {
+ "tags" : [ "Users" ],
+ "summary" : "Follow User",
+ "description" : "Causes the user(in the path) to follow, or “request to follow” for protected users, the target user. The user(in the path) must match the user context authorizing the request",
+ "operationId" : "usersIdFollow",
+ "security" : [ {
+ "UserToken" : [ ]
+ } ],
+ "parameters" : [ {
+ "name" : "id",
+ "in" : "path",
+ "description" : "The ID of the user that is requesting to follow the target user",
+ "required" : true,
+ "schema" : {
+ "$ref" : "#/components/schemas/UserID"
+ }
+ } ],
+ "requestBody" : {
+ "content" : {
+ "application/json" : {
+ "schema" : {
+ "type" : "object",
+ "required" : [ "target_user_id" ],
+ "properties" : {
+ "target_user_id" : {
+ "type" : "string",
+ "pattern" : "^[0-9]{1,19}$"
+ }
+ }
+ }
+ }
+ }
+ },
+ "responses" : {
+ "200" : {
+ "description" : "The request was successful",
+ "content" : {
+ "application/json" : {
+ "schema" : {
+ "$ref" : "#/components/schemas/UsersFollowingCreateResponse"
+ }
+ }
+ }
+ },
+ "default" : {
+ "$ref" : "#/components/responses/HttpErrorResponse"
+ }
+ }
+ }
+ },
+ "/2/users/{source_user_id}/following/{target_user_id}" : {
+ "delete" : {
+ "tags" : [ "Users" ],
+ "summary" : "Unfollow User",
+ "description" : "Causes the source user to unfollow the target user. The source user must match the user context authorizing the request",
+ "operationId" : "usersIdUnfollow",
+ "security" : [ {
+ "UserToken" : [ ]
+ } ],
+ "parameters" : [ {
+ "name" : "source_user_id",
+ "in" : "path",
+ "description" : "The ID of the user that is requesting to unfollow the target user",
+ "required" : true,
+ "schema" : {
+ "$ref" : "#/components/schemas/UserID"
+ }
+ }, {
+ "name" : "target_user_id",
+ "in" : "path",
+ "description" : "The ID of the user that the source user is requesting to unfollow",
+ "required" : true,
+ "schema" : {
+ "$ref" : "#/components/schemas/UserID"
+ }
+ } ],
+ "responses" : {
+ "200" : {
+ "description" : "The request was successful",
+ "content" : {
+ "application/json" : {
+ "schema" : {
+ "$ref" : "#/components/schemas/UsersFollowingDeleteResponse"
+ }
+ }
+ }
+ },
+ "default" : {
+ "$ref" : "#/components/responses/HttpErrorResponse"
+ }
+ }
+ }
+ },
+ "/2/tweets" : {
+ "get" : {
+ "security" : [ {
+ "BearerToken" : [ ],
+ "UserToken" : [ ]
+ } ],
+ "tags" : [ "Tweets" ],
+ "summary" : "Tweet lookup by Tweet IDs",
+ "description" : "Returns a variety of information about the Tweet specified by the requested ID",
+ "operationId" : "findTweetsById",
+ "parameters" : [ {
+ "name" : "ids",
+ "in" : "query",
+ "description" : "A comma separated list of Tweet IDs. Up to 100 are allowed in a single request.",
+ "required" : true,
+ "style" : "form",
+ "explode" : false,
+ "schema" : {
+ "type" : "array",
+ "items" : {
+ "$ref" : "#/components/schemas/TweetID"
+ },
+ "minItems" : 1,
+ "maxItems" : 100
+ }
+ }, {
+ "$ref" : "#/components/parameters/TweetExpansionsParameter"
+ }, {
+ "$ref" : "#/components/parameters/TweetFieldsParameter"
+ }, {
+ "$ref" : "#/components/parameters/UserFieldsParameter"
+ }, {
+ "$ref" : "#/components/parameters/MediaFieldsParameter"
+ }, {
+ "$ref" : "#/components/parameters/PlaceFieldsParameter"
+ }, {
+ "$ref" : "#/components/parameters/PollFieldsParameter"
+ } ],
+ "responses" : {
+ "200" : {
+ "description" : "The request was successful",
+ "content" : {
+ "application/json" : {
+ "schema" : {
+ "$ref" : "#/components/schemas/MultiTweetLookupResponse"
+ }
+ }
+ }
+ },
+ "default" : {
+ "$ref" : "#/components/responses/HttpErrorResponse"
+ }
+ }
+ }
+ },
+ "/2/tweets/{id}" : {
+ "get" : {
+ "security" : [ {
+ "BearerToken" : [ ],
+ "UserToken" : [ ]
+ } ],
+ "tags" : [ "Tweets" ],
+ "summary" : "Tweet lookup by Tweet ID",
+ "description" : "Returns a variety of information about the Tweet specified by the requested ID",
+ "operationId" : "findTweetById",
+ "parameters" : [ {
+ "name" : "id",
+ "in" : "path",
+ "description" : "A single Tweet ID.",
+ "required" : true,
+ "schema" : {
+ "$ref" : "#/components/schemas/TweetID"
+ }
+ }, {
+ "$ref" : "#/components/parameters/TweetExpansionsParameter"
+ }, {
+ "$ref" : "#/components/parameters/TweetFieldsParameter"
+ }, {
+ "$ref" : "#/components/parameters/UserFieldsParameter"
+ }, {
+ "$ref" : "#/components/parameters/MediaFieldsParameter"
+ }, {
+ "$ref" : "#/components/parameters/PlaceFieldsParameter"
+ }, {
+ "$ref" : "#/components/parameters/PollFieldsParameter"
+ } ],
+ "responses" : {
+ "200" : {
+ "description" : "The request was successful",
+ "content" : {
+ "application/json" : {
+ "schema" : {
+ "$ref" : "#/components/schemas/SingleTweetLookupResponse"
+ }
+ }
+ }
+ },
+ "default" : {
+ "$ref" : "#/components/responses/HttpErrorResponse"
+ }
+ }
+ }
+ },
+ "/2/tweets/{id}/hidden" : {
+ "put" : {
+ "tags" : [ "Tweets" ],
+ "summary" : "Hide replies",
+ "description" : "Hides or unhides a reply to an owned conversation.",
+ "operationId" : "hideReplyById",
+ "security" : [ {
+ "UserToken" : [ ]
+ } ],
+ "parameters" : [ {
+ "name" : "id",
+ "in" : "path",
+ "description" : "The ID of the reply that you want to hide or unhide.",
+ "required" : true,
+ "schema" : {
+ "$ref" : "#/components/schemas/TweetID"
+ }
+ } ],
+ "requestBody" : {
+ "content" : {
+ "application/json" : {
+ "schema" : {
+ "type" : "object",
+ "properties" : {
+ "hidden" : {
+ "type" : "boolean"
+ }
+ }
+ }
+ }
+ }
+ },
+ "responses" : {
+ "200" : {
+ "description" : "A successful response. The reply has been hidden or unhidden.",
+ "content" : {
+ "application/json" : {
+ "schema" : {
+ "type" : "object",
+ "properties" : {
+ "data" : {
+ "type" : "object",
+ "properties" : {
+ "hidden" : {
+ "type" : "boolean"
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "default" : {
+ "$ref" : "#/components/responses/HttpErrorResponse"
+ }
+ }
+ }
+ },
+ "/2/tweets/search/recent" : {
+ "get" : {
+ "security" : [ {
+ "BearerToken" : [ ],
+ "UserToken" : [ ]
+ } ],
+ "tags" : [ "Tweets" ],
+ "summary" : "Recent search",
+ "description" : "Returns Tweets from the last 7 days that match a search query.",
+ "operationId" : "tweetsRecentSearch",
+ "parameters" : [ {
+ "in" : "query",
+ "name" : "query",
+ "description" : "One query/rule/filter for matching Tweets. Up to 512 characters.",
+ "required" : true,
+ "schema" : {
+ "type" : "string",
+ "minLength" : 1,
+ "maxLength" : 512,
+ "example" : "(from:TwitterDev OR from:TwitterAPI) has:media -is:retweet"
+ }
+ }, {
+ "in" : "query",
+ "name" : "start_time",
+ "description" : "YYYY-MM-DDTHH:mm:ssZ. The oldest UTC timestamp (from most recent 7 days) from which the Tweets will be provided. Timestamp is in second granularity and is inclusive (i.e. 12:00:01 includes the first second of the minute).",
+ "required" : false,
+ "schema" : {
+ "type" : "string",
+ "format" : "date-time"
+ }
+ }, {
+ "in" : "query",
+ "name" : "end_time",
+ "description" : "YYYY-MM-DDTHH:mm:ssZ. The newest, most recent UTC timestamp to which the Tweets will be provided. Timestamp is in second granularity and is exclusive (i.e. 12:00:01 excludes the first second of the minute).",
+ "required" : false,
+ "schema" : {
+ "type" : "string",
+ "format" : "date-time"
+ }
+ }, {
+ "in" : "query",
+ "name" : "since_id",
+ "description" : "Returns results with a Tweet ID greater than (that is, more recent than) the specified ID.",
+ "required" : false,
+ "schema" : {
+ "$ref" : "#/components/schemas/TweetID"
+ }
+ }, {
+ "in" : "query",
+ "name" : "until_id",
+ "description" : "Returns results with a Tweet ID less than (that is, older than) the specified ID.",
+ "required" : false,
+ "schema" : {
+ "$ref" : "#/components/schemas/TweetID"
+ }
+ }, {
+ "in" : "query",
+ "name" : "max_results",
+ "description" : "The maximum number of search results to be returned by a request.",
+ "required" : false,
+ "schema" : {
+ "type" : "integer",
+ "format" : "int32",
+ "minimum" : 10,
+ "maximum" : 100,
+ "default" : 10
+ }
+ }, {
+ "in" : "query",
+ "name" : "next_token",
+ "description" : "This parameter is used to get the next 'page' of results. The value used with the parameter is pulled directly from the response provided by the API, and should not be modified.",
+ "required" : false,
+ "schema" : {
+ "type" : "string",
+ "minLength" : 1
+ }
+ }, {
+ "$ref" : "#/components/parameters/TweetExpansionsParameter"
+ }, {
+ "$ref" : "#/components/parameters/TweetFieldsParameter"
+ }, {
+ "$ref" : "#/components/parameters/UserFieldsParameter"
+ }, {
+ "$ref" : "#/components/parameters/MediaFieldsParameter"
+ }, {
+ "$ref" : "#/components/parameters/PlaceFieldsParameter"
+ }, {
+ "$ref" : "#/components/parameters/PollFieldsParameter"
+ } ],
+ "responses" : {
+ "200" : {
+ "description" : "Tweets recent search response",
+ "content" : {
+ "application/json" : {
+ "schema" : {
+ "$ref" : "#/components/schemas/TweetSearchResponse"
+ }
+ }
+ }
+ },
+ "default" : {
+ "$ref" : "#/components/responses/HttpErrorResponse"
+ }
+ }
+ }
+ },
+ "/2/tweets/search/all" : {
+ "get" : {
+ "security" : [ {
+ "BearerToken" : [ ]
+ } ],
+ "tags" : [ "Tweets" ],
+ "summary" : "Full-archive search",
+ "description" : "Returns Tweets that match a search query.",
+ "operationId" : "tweetsFullarchiveSearch",
+ "parameters" : [ {
+ "in" : "query",
+ "name" : "query",
+ "description" : "One query/rule/filter for matching Tweets. Up to 1024 characters.",
+ "required" : true,
+ "schema" : {
+ "type" : "string",
+ "minLength" : 1,
+ "maxLength" : 1024,
+ "example" : "(from:TwitterDev OR from:TwitterAPI) has:media -is:retweet"
+ }
+ }, {
+ "in" : "query",
+ "name" : "start_time",
+ "description" : "YYYY-MM-DDTHH:mm:ssZ. The oldest UTC timestamp from which the Tweets will be provided. Timestamp is in second granularity and is inclusive (i.e. 12:00:01 includes the first second of the minute).",
+ "required" : false,
+ "schema" : {
+ "type" : "string",
+ "format" : "date-time"
+ }
+ }, {
+ "in" : "query",
+ "name" : "end_time",
+ "description" : "YYYY-MM-DDTHH:mm:ssZ. The newest, most recent UTC timestamp to which the Tweets will be provided. Timestamp is in second granularity and is exclusive (i.e. 12:00:01 excludes the first second of the minute).",
+ "required" : false,
+ "schema" : {
+ "type" : "string",
+ "format" : "date-time"
+ }
+ }, {
+ "in" : "query",
+ "name" : "since_id",
+ "description" : "Returns results with a Tweet ID greater than (that is, more recent than) the specified ID.",
+ "required" : false,
+ "schema" : {
+ "$ref" : "#/components/schemas/TweetID"
+ }
+ }, {
+ "in" : "query",
+ "name" : "until_id",
+ "description" : "Returns results with a Tweet ID less than (that is, older than) the specified ID.",
+ "required" : false,
+ "schema" : {
+ "$ref" : "#/components/schemas/TweetID"
+ }
+ }, {
+ "in" : "query",
+ "name" : "max_results",
+ "description" : "The maximum number of search results to be returned by a request.",
+ "required" : false,
+ "schema" : {
+ "type" : "integer",
+ "format" : "int32",
+ "minimum" : 10,
+ "maximum" : 500,
+ "default" : 10
+ }
+ }, {
+ "in" : "query",
+ "name" : "next_token",
+ "description" : "This parameter is used to get the next 'page' of results. The value used with the parameter is pulled directly from the response provided by the API, and should not be modified.",
+ "required" : false,
+ "schema" : {
+ "type" : "string",
+ "minLength" : 1
+ }
+ }, {
+ "$ref" : "#/components/parameters/TweetExpansionsParameter"
+ }, {
+ "$ref" : "#/components/parameters/TweetFieldsParameter"
+ }, {
+ "$ref" : "#/components/parameters/UserFieldsParameter"
+ }, {
+ "$ref" : "#/components/parameters/MediaFieldsParameter"
+ }, {
+ "$ref" : "#/components/parameters/PlaceFieldsParameter"
+ }, {
+ "$ref" : "#/components/parameters/PollFieldsParameter"
+ } ],
+ "responses" : {
+ "200" : {
+ "description" : "Tweets full archive search response",
+ "content" : {
+ "application/json" : {
+ "schema" : {
+ "$ref" : "#/components/schemas/TweetSearchResponse"
+ }
+ }
+ }
+ },
+ "default" : {
+ "$ref" : "#/components/responses/HttpErrorResponse"
+ }
+ }
+ }
+ },
+ "/2/tweets/search/stream" : {
+ "get" : {
+ "security" : [ {
+ "BearerToken" : [ ]
+ } ],
+ "tags" : [ "Tweets" ],
+ "x-twitter-streaming" : true,
+ "summary" : "Filtered stream",
+ "description" : "Streams Tweets matching the stream's active rule set.",
+ "operationId" : "searchStream",
+ "parameters" : [ {
+ "$ref" : "#/components/parameters/TweetExpansionsParameter"
+ }, {
+ "$ref" : "#/components/parameters/TweetFieldsParameter"
+ }, {
+ "$ref" : "#/components/parameters/UserFieldsParameter"
+ }, {
+ "$ref" : "#/components/parameters/MediaFieldsParameter"
+ }, {
+ "$ref" : "#/components/parameters/PlaceFieldsParameter"
+ }, {
+ "$ref" : "#/components/parameters/PollFieldsParameter"
+ }, {
+ "$ref" : "#/components/parameters/BackfillMinutesRequestParameter"
+ } ],
+ "responses" : {
+ "200" : {
+ "description" : "The request was successful. Successful responses will return a stream of individual JSON Tweet payloads.",
+ "content" : {
+ "application/json" : {
+ "schema" : {
+ "$ref" : "#/components/schemas/FilteredStreamingTweet"
+ }
+ }
+ }
+ },
+ "default" : {
+ "$ref" : "#/components/responses/HttpErrorResponse"
+ }
+ }
+ }
+ },
+ "/2/tweets/search/stream/rules" : {
+ "get" : {
+ "security" : [ {
+ "BearerToken" : [ ]
+ } ],
+ "tags" : [ "Tweets" ],
+ "summary" : "Rules lookup",
+ "description" : "Returns rules from a user's active rule set. Users can fetch all of their rules or a subset, specified by the provided rule ids.",
+ "operationId" : "getRules",
+ "parameters" : [ {
+ "name" : "ids",
+ "in" : "query",
+ "description" : "A comma-separated list of Rule IDs.",
+ "required" : false,
+ "schema" : {
+ "type" : "array",
+ "items" : {
+ "$ref" : "#/components/schemas/RuleId"
+ }
+ }
+ } ],
+ "responses" : {
+ "200" : {
+ "description" : "The request was successful",
+ "content" : {
+ "application/json" : {
+ "schema" : {
+ "type" : "object",
+ "required" : [ "data", "meta" ],
+ "properties" : {
+ "data" : {
+ "type" : "array",
+ "items" : {
+ "$ref" : "#/components/schemas/Rule"
+ }
+ },
+ "meta" : {
+ "$ref" : "#/components/schemas/RulesResponseMetadata"
+ }
+ }
+ }
+ }
+ }
+ },
+ "default" : {
+ "$ref" : "#/components/responses/HttpErrorResponse"
+ }
+ }
+ },
+ "post" : {
+ "security" : [ {
+ "BearerToken" : [ ]
+ } ],
+ "tags" : [ "Tweets" ],
+ "summary" : "Add/Delete rules",
+ "description" : "Add or delete rules from a user's active rule set. Users can provide unique, optionally tagged rules to add. Users can delete their entire rule set or a subset specified by rule ids or values.",
+ "operationId" : "addOrDeleteRules",
+ "parameters" : [ {
+ "name" : "dry_run",
+ "in" : "query",
+ "description" : "Dry Run can be used with both the add and delete action, with the expected result given, but without actually taking any action in the system (meaning the end state will always be as it was when the request was submitted). This is particularly useful to validate rule changes.",
+ "required" : false,
+ "schema" : {
+ "type" : "boolean"
+ }
+ } ],
+ "requestBody" : {
+ "description" : "",
+ "required" : true,
+ "content" : {
+ "application/json" : {
+ "schema" : {
+ "$ref" : "#/components/schemas/AddOrDeleteRulesRequest"
+ }
+ }
+ }
+ },
+ "responses" : {
+ "200" : {
+ "description" : "The request was successful",
+ "content" : {
+ "application/json" : {
+ "schema" : {
+ "$ref" : "#/components/schemas/AddOrDeleteRulesResponse"
+ }
+ }
+ }
+ },
+ "default" : {
+ "$ref" : "#/components/responses/HttpErrorResponse"
+ }
+ }
+ }
+ },
+ "/2/tweets/sample/stream" : {
+ "get" : {
+ "security" : [ {
+ "BearerToken" : [ ]
+ } ],
+ "tags" : [ "Tweets" ],
+ "x-twitter-streaming" : true,
+ "summary" : "Sample stream",
+ "description" : "Streams a deterministic 1% of public Tweets.",
+ "operationId" : "sampleStream",
+ "parameters" : [ {
+ "$ref" : "#/components/parameters/TweetExpansionsParameter"
+ }, {
+ "$ref" : "#/components/parameters/TweetFieldsParameter"
+ }, {
+ "$ref" : "#/components/parameters/UserFieldsParameter"
+ }, {
+ "$ref" : "#/components/parameters/MediaFieldsParameter"
+ }, {
+ "$ref" : "#/components/parameters/PlaceFieldsParameter"
+ }, {
+ "$ref" : "#/components/parameters/PollFieldsParameter"
+ }, {
+ "$ref" : "#/components/parameters/BackfillMinutesRequestParameter"
+ } ],
+ "responses" : {
+ "200" : {
+ "description" : "The request was successful. Successful responses will return a stream of individual JSON Tweet payloads.",
+ "content" : {
+ "application/json" : {
+ "schema" : {
+ "$ref" : "#/components/schemas/StreamingTweet"
+ }
+ }
+ }
+ },
+ "default" : {
+ "$ref" : "#/components/responses/HttpErrorResponse"
+ }
+ }
+ }
+ },
+ "/2/openapi.json" : {
+ "get" : {
+ "tags" : [ "General" ],
+ "summary" : "Returns the open api spec document.",
+ "description" : "Full open api spec in JSON format. (See https://github.com/OAI/OpenAPI-Specification/blob/master/README.md)",
+ "operationId" : "getOpenApiSpec",
+ "parameters" : [ ],
+ "responses" : {
+ "200" : {
+ "description" : "The request was successful",
+ "content" : {
+ "application/json" : {
+ "schema" : {
+ "type" : "object"
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "/2/users/{id}/tweets" : {
+ "get" : {
+ "tags" : [ "Tweets" ],
+ "security" : [ {
+ "BearerToken" : [ ],
+ "UserToken" : [ ]
+ } ],
+ "summary" : "User Tweets timeline by User ID",
+ "description" : "Returns a list of Tweets authored by the provided User ID",
+ "operationId" : "usersIdTweets",
+ "parameters" : [ {
+ "name" : "id",
+ "in" : "path",
+ "description" : "The ID of the User to list Tweets of",
+ "required" : true,
+ "schema" : {
+ "$ref" : "#/components/schemas/UserID"
+ }
+ }, {
+ "$ref" : "#/components/parameters/SinceIdRequestParameter"
+ }, {
+ "$ref" : "#/components/parameters/UntilIdRequestParameter"
+ }, {
+ "$ref" : "#/components/parameters/MaxResultsRequestParameter"
+ }, {
+ "$ref" : "#/components/parameters/TweetTypeExcludesRequestParameter"
+ }, {
+ "$ref" : "#/components/parameters/PaginationTokenRequestParameter"
+ }, {
+ "$ref" : "#/components/parameters/StartTimeRequestParameter"
+ }, {
+ "$ref" : "#/components/parameters/EndTimeRequestParameter"
+ }, {
+ "$ref" : "#/components/parameters/TweetExpansionsParameter"
+ }, {
+ "$ref" : "#/components/parameters/TweetFieldsParameter"
+ }, {
+ "$ref" : "#/components/parameters/UserFieldsParameter"
+ }, {
+ "$ref" : "#/components/parameters/MediaFieldsParameter"
+ }, {
+ "$ref" : "#/components/parameters/PlaceFieldsParameter"
+ }, {
+ "$ref" : "#/components/parameters/PollFieldsParameter"
+ } ],
+ "responses" : {
+ "200" : {
+ "description" : "The request was successful",
+ "content" : {
+ "application/json" : {
+ "schema" : {
+ "$ref" : "#/components/schemas/GenericTweetsTimelineResponse"
+ }
+ }
+ }
+ },
+ "default" : {
+ "$ref" : "#/components/responses/HttpErrorResponse"
+ }
+ }
+ }
+ },
+ "/2/users/{id}/mentions" : {
+ "get" : {
+ "tags" : [ "Tweets" ],
+ "security" : [ {
+ "BearerToken" : [ ],
+ "UserToken" : [ ]
+ } ],
+ "summary" : "User mention timeline by User ID",
+ "description" : "Returns Tweet objects that mention username associated to the provided User ID",
+ "operationId" : "usersIdMentions",
+ "parameters" : [ {
+ "name" : "id",
+ "in" : "path",
+ "description" : "The ID of the User to list mentions of",
+ "required" : true,
+ "schema" : {
+ "$ref" : "#/components/schemas/UserID"
+ }
+ }, {
+ "$ref" : "#/components/parameters/SinceIdRequestParameter"
+ }, {
+ "$ref" : "#/components/parameters/UntilIdRequestParameter"
+ }, {
+ "$ref" : "#/components/parameters/MaxResultsRequestParameter"
+ }, {
+ "$ref" : "#/components/parameters/PaginationTokenRequestParameter"
+ }, {
+ "$ref" : "#/components/parameters/StartTimeRequestParameter"
+ }, {
+ "$ref" : "#/components/parameters/EndTimeRequestParameter"
+ }, {
+ "$ref" : "#/components/parameters/TweetExpansionsParameter"
+ }, {
+ "$ref" : "#/components/parameters/TweetFieldsParameter"
+ }, {
+ "$ref" : "#/components/parameters/UserFieldsParameter"
+ }, {
+ "$ref" : "#/components/parameters/MediaFieldsParameter"
+ }, {
+ "$ref" : "#/components/parameters/PlaceFieldsParameter"
+ }, {
+ "$ref" : "#/components/parameters/PollFieldsParameter"
+ } ],
+ "responses" : {
+ "200" : {
+ "description" : "The request was successful",
+ "content" : {
+ "application/json" : {
+ "schema" : {
+ "$ref" : "#/components/schemas/GenericTweetsTimelineResponse"
+ }
+ }
+ }
+ },
+ "default" : {
+ "$ref" : "#/components/responses/HttpErrorResponse"
+ }
+ }
+ }
+ },
+ "/2/users/{id}/likes" : {
+ "post" : {
+ "tags" : [ "Tweets" ],
+ "summary" : "Causes the user (in the path) to like the specified tweet",
+ "description" : "Causes the user (in the path) to like the specified tweet. The user in the path must match the user context authorizing the request.",
+ "operationId" : "usersIdLike",
+ "security" : [ {
+ "UserToken" : [ ]
+ } ],
+ "parameters" : [ {
+ "name" : "id",
+ "in" : "path",
+ "description" : "The ID of the user that is requesting to like the tweet",
+ "required" : true,
+ "schema" : {
+ "$ref" : "#/components/schemas/UserID"
+ }
+ } ],
+ "requestBody" : {
+ "content" : {
+ "application/json" : {
+ "schema" : {
+ "$ref" : "#/components/schemas/UsersLikesCreateRequest"
+ }
+ }
+ }
+ },
+ "responses" : {
+ "200" : {
+ "description" : "The request was successful",
+ "content" : {
+ "application/json" : {
+ "schema" : {
+ "$ref" : "#/components/schemas/UsersLikesCreateResponse"
+ }
+ }
+ }
+ },
+ "default" : {
+ "$ref" : "#/components/responses/HttpErrorResponse"
+ }
+ }
+ }
+ },
+ "/2/users/{id}/likes/{tweet_id}" : {
+ "delete" : {
+ "tags" : [ "Tweets" ],
+ "summary" : "Causes the user (in the path) to unlike the specified tweet",
+ "description" : "Causes the user (in the path) to unlike the specified tweet. The user must match the user context authorizing the request",
+ "operationId" : "usersIdUnlike",
+ "security" : [ {
+ "UserToken" : [ ]
+ } ],
+ "parameters" : [ {
+ "name" : "id",
+ "in" : "path",
+ "description" : "The ID of the user that is requesting to unlike the tweet",
+ "required" : true,
+ "schema" : {
+ "$ref" : "#/components/schemas/UserID"
+ }
+ }, {
+ "name" : "tweet_id",
+ "in" : "path",
+ "description" : "The ID of the tweet that the user is requesting to unlike",
+ "required" : true,
+ "schema" : {
+ "$ref" : "#/components/schemas/TweetID"
+ }
+ } ],
+ "responses" : {
+ "200" : {
+ "description" : "The request was successful",
+ "content" : {
+ "application/json" : {
+ "schema" : {
+ "$ref" : "#/components/schemas/UsersLikesDeleteResponse"
+ }
+ }
+ }
+ },
+ "default" : {
+ "$ref" : "#/components/responses/HttpErrorResponse"
+ }
+ }
+ }
+ },
+ "/2/users/{id}/liked_tweets" : {
+ "get" : {
+ "tags" : [ "Tweets" ],
+ "summary" : "Returns Tweet objects liked by the provided User ID",
+ "description" : "Returns a list of Tweets liked by the provided User ID",
+ "operationId" : "usersIdLikedTweets",
+ "security" : [ {
+ "BearerToken" : [ ],
+ "UserToken" : [ ]
+ } ],
+ "parameters" : [ {
+ "name" : "id",
+ "in" : "path",
+ "description" : "The ID of the User to list the liked Tweets of",
+ "required" : true,
+ "schema" : {
+ "$ref" : "#/components/schemas/UserID"
+ }
+ }, {
+ "$ref" : "#/components/parameters/MaxResultsRequestParameter"
+ }, {
+ "$ref" : "#/components/parameters/PaginationTokenRequestParameter"
+ }, {
+ "$ref" : "#/components/parameters/TweetExpansionsParameter"
+ }, {
+ "$ref" : "#/components/parameters/TweetFieldsParameter"
+ }, {
+ "$ref" : "#/components/parameters/UserFieldsParameter"
+ }, {
+ "$ref" : "#/components/parameters/MediaFieldsParameter"
+ }, {
+ "$ref" : "#/components/parameters/PlaceFieldsParameter"
+ }, {
+ "$ref" : "#/components/parameters/PollFieldsParameter"
+ } ],
+ "responses" : {
+ "200" : {
+ "description" : "The request was successful",
+ "content" : {
+ "application/json" : {
+ "schema" : {
+ "type" : "object",
+ "properties" : {
+ "data" : {
+ "type" : "array",
+ "items" : {
+ "$ref" : "#/components/schemas/Tweet"
+ },
+ "minItems" : 1
+ },
+ "includes" : {
+ "$ref" : "#/components/schemas/Expansions"
+ },
+ "errors" : {
+ "type" : "array",
+ "items" : {
+ "$ref" : "#/components/schemas/Problem"
+ },
+ "minItems" : 1
+ },
+ "meta" : {
+ "type" : "object",
+ "properties" : {
+ "previous_token" : {
+ "description" : "The previous token",
+ "type" : "string",
+ "pattern" : "^[a-zA-Z0-9]+$"
+ },
+ "next_token" : {
+ "description" : "The next token",
+ "type" : "string",
+ "pattern" : "^[a-zA-Z0-9]+$"
+ },
+ "result_count" : {
+ "description" : "Number of Tweets in result set",
+ "type" : "integer",
+ "format" : "int32"
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "default" : {
+ "$ref" : "#/components/responses/HttpErrorResponse"
+ }
+ }
+ }
+ },
+ "/2/tweets/{id}/liking_users" : {
+ "get" : {
+ "tags" : [ "Users" ],
+ "summary" : "Returns user objects that have liked the provided Tweet ID",
+ "description" : "Returns a list of users that have liked the provided Tweet ID",
+ "operationId" : "tweetsIdLikingUsers",
+ "security" : [ {
+ "BearerToken" : [ ],
+ "UserToken" : [ ]
+ } ],
+ "parameters" : [ {
+ "name" : "id",
+ "in" : "path",
+ "description" : "The ID of the Tweet for which to return results",
+ "required" : true,
+ "schema" : {
+ "$ref" : "#/components/schemas/TweetID"
+ }
+ } ],
+ "responses" : {
+ "200" : {
+ "description" : "The request was successful",
+ "content" : {
+ "application/json" : {
+ "schema" : {
+ "$ref" : "#/components/schemas/GenericMultipleUsersLookupResponse"
+ }
+ }
+ }
+ },
+ "default" : {
+ "$ref" : "#/components/responses/HttpErrorResponse"
+ }
+ }
+ }
+ },
+ "/2/tweets/{id}/retweeted_by" : {
+ "get" : {
+ "tags" : [ "Users" ],
+ "summary" : "Returns user objects that have retweeted the provided Tweet ID",
+ "description" : "Returns a list of users that have retweeted the provided Tweet ID",
+ "operationId" : "tweetsIdRetweetingUsers",
+ "security" : [ {
+ "BearerToken" : [ ],
+ "UserToken" : [ ]
+ } ],
+ "parameters" : [ {
+ "name" : "id",
+ "in" : "path",
+ "description" : "The ID of the Tweet for which to return results",
+ "required" : true,
+ "schema" : {
+ "$ref" : "#/components/schemas/TweetID"
+ }
+ } ],
+ "responses" : {
+ "200" : {
+ "description" : "The request was successful",
+ "content" : {
+ "application/json" : {
+ "schema" : {
+ "$ref" : "#/components/schemas/GenericMultipleUsersLookupResponse"
+ }
+ }
+ }
+ },
+ "default" : {
+ "$ref" : "#/components/responses/HttpErrorResponse"
+ }
+ }
+ }
+ },
+ "/2/users/{id}/retweets" : {
+ "post" : {
+ "tags" : [ "Tweets" ],
+ "summary" : "Causes the user (in the path) to retweet the specified tweet",
+ "description" : "Causes the user (in the path) to retweet the specified tweet. The user in the path must match the user context authorizing the request.",
+ "operationId" : "usersIdRetweets",
+ "security" : [ {
+ "UserToken" : [ ]
+ } ],
+ "parameters" : [ {
+ "name" : "id",
+ "in" : "path",
+ "description" : "The ID of the user that is requesting to retweet the tweet",
+ "required" : true,
+ "schema" : {
+ "$ref" : "#/components/schemas/UserID"
+ }
+ } ],
+ "requestBody" : {
+ "content" : {
+ "application/json" : {
+ "schema" : {
+ "$ref" : "#/components/schemas/UsersRetweetsCreateRequest"
+ }
+ }
+ }
+ },
+ "responses" : {
+ "200" : {
+ "description" : "The request was successful",
+ "content" : {
+ "application/json" : {
+ "schema" : {
+ "$ref" : "#/components/schemas/UsersRetweetsCreateResponse"
+ }
+ }
+ }
+ },
+ "default" : {
+ "$ref" : "#/components/responses/HttpErrorResponse"
+ }
+ }
+ }
+ },
+ "/2/users/{id}/retweets/{source_tweet_id}" : {
+ "delete" : {
+ "tags" : [ "Tweets" ],
+ "summary" : "Causes the user (in the path) to unretweet the specified tweet",
+ "description" : "Causes the user (in the path) to unretweet the specified tweet. The user must match the user context authorizing the request",
+ "operationId" : "usersIdUnretweets",
+ "security" : [ {
+ "UserToken" : [ ]
+ } ],
+ "parameters" : [ {
+ "name" : "id",
+ "in" : "path",
+ "description" : "The ID of the user that is requesting to unretweet the tweet",
+ "required" : true,
+ "schema" : {
+ "$ref" : "#/components/schemas/UserID"
+ }
+ }, {
+ "name" : "source_tweet_id",
+ "in" : "path",
+ "description" : "The ID of the tweet that the user is requesting to unretweet",
+ "required" : true,
+ "schema" : {
+ "$ref" : "#/components/schemas/TweetID"
+ }
+ } ],
+ "responses" : {
+ "200" : {
+ "description" : "The request was successful",
+ "content" : {
+ "application/json" : {
+ "schema" : {
+ "$ref" : "#/components/schemas/UsersRetweetsDeleteResponse"
+ }
+ }
+ }
+ },
+ "default" : {
+ "$ref" : "#/components/responses/HttpErrorResponse"
+ }
+ }
+ }
+ },
+ "/2/tweets/counts/recent" : {
+ "get" : {
+ "security" : [ {
+ "BearerToken" : [ ],
+ "UserToken" : [ ]
+ } ],
+ "tags" : [ "Tweets" ],
+ "summary" : "Recent search counts",
+ "description" : "Returns Tweet Counts from the last 7 days that match a search query.",
+ "operationId" : "tweetCountsRecentSearch",
+ "parameters" : [ {
+ "in" : "query",
+ "name" : "query",
+ "description" : "One query/rule/filter for matching Tweets. Up to 2048 characters.",
+ "required" : true,
+ "schema" : {
+ "type" : "string",
+ "minLength" : 1,
+ "maxLength" : 2048,
+ "example" : "(from:TwitterDev OR from:TwitterAPI) has:media -is:retweet"
+ }
+ }, {
+ "in" : "query",
+ "name" : "start_time",
+ "description" : "YYYY-MM-DDTHH:mm:ssZ. The oldest UTC timestamp (from most recent 7 days) from which the Tweets will be provided. Timestamp is in second granularity and is inclusive (i.e. 12:00:01 includes the first second of the minute).",
+ "required" : false,
+ "schema" : {
+ "type" : "string",
+ "format" : "date-time"
+ }
+ }, {
+ "in" : "query",
+ "name" : "end_time",
+ "description" : "YYYY-MM-DDTHH:mm:ssZ. The newest, most recent UTC timestamp to which the Tweets will be provided. Timestamp is in second granularity and is exclusive (i.e. 12:00:01 excludes the first second of the minute).",
+ "required" : false,
+ "schema" : {
+ "type" : "string",
+ "format" : "date-time"
+ }
+ }, {
+ "in" : "query",
+ "name" : "since_id",
+ "description" : "Returns results with a Tweet ID greater than (that is, more recent than) the specified ID.",
+ "required" : false,
+ "schema" : {
+ "$ref" : "#/components/schemas/TweetID"
+ }
+ }, {
+ "in" : "query",
+ "name" : "until_id",
+ "description" : "Returns results with a Tweet ID less than (that is, older than) the specified ID.",
+ "required" : false,
+ "schema" : {
+ "$ref" : "#/components/schemas/TweetID"
+ }
+ }, {
+ "in" : "query",
+ "name" : "next_token",
+ "description" : "This parameter is used to get the next 'page' of results. The value used with the parameter is pulled directly from the response provided by the API, and should not be modified.",
+ "required" : false,
+ "schema" : {
+ "type" : "string",
+ "minLength" : 1
+ }
+ }, {
+ "in" : "query",
+ "name" : "granularity",
+ "description" : "The granularity for the search counts results.",
+ "required" : false,
+ "schema" : {
+ "$ref" : "#/components/schemas/Granularity"
+ }
+ } ],
+ "responses" : {
+ "200" : {
+ "description" : "Recent tweet counts response",
+ "content" : {
+ "application/json" : {
+ "schema" : {
+ "$ref" : "#/components/schemas/TweetCountsResponse"
+ }
+ }
+ }
+ },
+ "default" : {
+ "$ref" : "#/components/responses/HttpErrorResponse"
+ }
+ }
+ }
+ },
+ "/2/tweets/counts/all" : {
+ "get" : {
+ "security" : [ {
+ "BearerToken" : [ ],
+ "UserToken" : [ ]
+ } ],
+ "tags" : [ "Tweets" ],
+ "summary" : "Full archive search counts",
+ "description" : "Returns Tweet Counts that match a search query.",
+ "operationId" : "tweetCountsFullArchiveSearch",
+ "parameters" : [ {
+ "in" : "query",
+ "name" : "query",
+ "description" : "One query/rule/filter for matching Tweets. Up to 2048 characters.",
+ "required" : true,
+ "schema" : {
+ "type" : "string",
+ "minLength" : 1,
+ "maxLength" : 2048,
+ "example" : "(from:TwitterDev OR from:TwitterAPI) has:media -is:retweet"
+ }
+ }, {
+ "in" : "query",
+ "name" : "start_time",
+ "description" : "YYYY-MM-DDTHH:mm:ssZ. The oldest UTC timestamp (from most recent 7 days) from which the Tweets will be provided. Timestamp is in second granularity and is inclusive (i.e. 12:00:01 includes the first second of the minute).",
+ "required" : false,
+ "schema" : {
+ "type" : "string",
+ "format" : "date-time"
+ }
+ }, {
+ "in" : "query",
+ "name" : "end_time",
+ "description" : "YYYY-MM-DDTHH:mm:ssZ. The newest, most recent UTC timestamp to which the Tweets will be provided. Timestamp is in second granularity and is exclusive (i.e. 12:00:01 excludes the first second of the minute).",
+ "required" : false,
+ "schema" : {
+ "type" : "string",
+ "format" : "date-time"
+ }
+ }, {
+ "in" : "query",
+ "name" : "since_id",
+ "description" : "Returns results with a Tweet ID greater than (that is, more recent than) the specified ID.",
+ "required" : false,
+ "schema" : {
+ "$ref" : "#/components/schemas/TweetID"
+ }
+ }, {
+ "in" : "query",
+ "name" : "until_id",
+ "description" : "Returns results with a Tweet ID less than (that is, older than) the specified ID.",
+ "required" : false,
+ "schema" : {
+ "$ref" : "#/components/schemas/TweetID"
+ }
+ }, {
+ "in" : "query",
+ "name" : "next_token",
+ "description" : "This parameter is used to get the next 'page' of results. The value used with the parameter is pulled directly from the response provided by the API, and should not be modified.",
+ "required" : false,
+ "schema" : {
+ "type" : "string",
+ "minLength" : 1
+ }
+ }, {
+ "in" : "query",
+ "name" : "granularity",
+ "description" : "The granularity for the search counts results.",
+ "required" : false,
+ "schema" : {
+ "$ref" : "#/components/schemas/Granularity"
+ }
+ } ],
+ "responses" : {
+ "200" : {
+ "description" : "Tweet counts response",
+ "content" : {
+ "application/json" : {
+ "schema" : {
+ "$ref" : "#/components/schemas/TweetCountsResponse"
+ }
+ }
+ }
+ },
+ "default" : {
+ "$ref" : "#/components/responses/HttpErrorResponse"
+ }
+ }
+ }
+ }
+ },
+ "components" : {
+ "securitySchemes" : {
+ "BearerToken" : {
+ "type" : "http",
+ "scheme" : "bearer"
+ },
+ "UserToken" : {
+ "type" : "http",
+ "scheme" : "OAuth"
+ }
+ },
+ "schemas" : {
+ "MediaHeight" : {
+ "type" : "integer",
+ "minimum" : 0,
+ "description" : "The height of the media in pixels"
+ },
+ "MediaWidth" : {
+ "type" : "integer",
+ "minimum" : 0,
+ "description" : "The width of the media in pixels"
+ },
+ "HTTPStatusCode" : {
+ "type" : "integer",
+ "minimum" : 100,
+ "maximum" : 599,
+ "description" : "HTTP Status Code."
+ },
+ "ContextAnnotation" : {
+ "type" : "object",
+ "description" : "Annotation inferred from the tweet text.",
+ "required" : [ "domain", "entity" ],
+ "properties" : {
+ "domain" : {
+ "$ref" : "#/components/schemas/ContextAnnotationDomainFields"
+ },
+ "entity" : {
+ "$ref" : "#/components/schemas/ContextAnnotationEntityFields"
+ }
+ }
+ },
+ "ContextAnnotationDomainFields" : {
+ "type" : "object",
+ "description" : "Represents the data for the context annotation domain.",
+ "required" : [ "id" ],
+ "properties" : {
+ "id" : {
+ "type" : "string",
+ "description" : "The unique id for a context annotation domain.",
+ "pattern" : "^[0-9]{1,19}$"
+ },
+ "name" : {
+ "type" : "string",
+ "description" : "Name of the context annotation domain."
+ },
+ "description" : {
+ "type" : "string",
+ "description" : "Description of the context annotation domain."
+ }
+ }
+ },
+ "ContextAnnotationEntityFields" : {
+ "type" : "object",
+ "description" : "Represents the data for the context annotation entity.",
+ "required" : [ "id" ],
+ "properties" : {
+ "id" : {
+ "type" : "string",
+ "description" : "The unique id for a context annotation entity.",
+ "pattern" : "^[0-9]{1,19}$"
+ },
+ "name" : {
+ "type" : "string",
+ "description" : "Name of the context annotation entity."
+ },
+ "description" : {
+ "type" : "string",
+ "description" : "Description of the context annotation entity."
+ }
+ }
+ },
+ "URL" : {
+ "type" : "string",
+ "description" : "A validly formatted URL.",
+ "format" : "uri",
+ "example" : "https://developer.twitter.com/en/docs/twitter-api"
+ },
+ "EntityIndicesInclusiveExclusive" : {
+ "type" : "object",
+ "description" : "Represent a boundary range (start and end index) for a recognized entity (for example a hashtag or a mention). `start` must be smaller than `end`. The start index is inclusive, the end index is exclusive.",
+ "required" : [ "start", "end" ],
+ "properties" : {
+ "start" : {
+ "type" : "integer",
+ "minimum" : 0,
+ "description" : "Index (zero-based) at which position this entity starts. The index is inclusive.",
+ "example" : 50
+ },
+ "end" : {
+ "type" : "integer",
+ "minimum" : 0,
+ "description" : "Index (zero-based) at which position this entity ends. The index is exclusive.",
+ "example" : 61
+ }
+ }
+ },
+ "EntityIndicesInclusiveInclusive" : {
+ "type" : "object",
+ "description" : "Represent a boundary range (start and end index) for a recognized entity (for example a hashtag or a mention). `start` must be smaller than `end`. The start index is inclusive, the end index is inclusive.",
+ "required" : [ "start", "end" ],
+ "properties" : {
+ "start" : {
+ "type" : "integer",
+ "minimum" : 0,
+ "description" : "Index (zero-based) at which position this entity starts. The index is inclusive.",
+ "example" : 50
+ },
+ "end" : {
+ "type" : "integer",
+ "minimum" : 0,
+ "description" : "Index (zero-based) at which position this entity ends. The index is inclusive.",
+ "example" : 61
+ }
+ }
+ },
+ "URLFields" : {
+ "type" : "object",
+ "description" : "Represent the portion of text recognized as a URL.",
+ "required" : [ "url" ],
+ "properties" : {
+ "url" : {
+ "$ref" : "#/components/schemas/URL"
+ },
+ "expanded_url" : {
+ "$ref" : "#/components/schemas/URL"
+ },
+ "display_url" : {
+ "type" : "string",
+ "description" : "The URL as displayed in the Twitter client.",
+ "example" : "twittercommunity.com/t/introducing-…"
+ },
+ "unwound_url" : {
+ "type" : "string",
+ "description" : "Fully resolved url",
+ "format" : "uri",
+ "example" : "https://twittercommunity.com/t/introducing-the-v2-follow-lookup-endpoints/147118"
+ },
+ "status" : {
+ "$ref" : "#/components/schemas/HTTPStatusCode"
+ },
+ "title" : {
+ "type" : "string",
+ "description" : "Title of the page the URL points to.",
+ "example" : "Introducing the v2 follow lookup endpoints"
+ },
+ "description" : {
+ "type" : "string",
+ "description" : "Description of the URL landing page.",
+ "example" : "This is a description of the website."
+ },
+ "images" : {
+ "type" : "array",
+ "items" : {
+ "$ref" : "#/components/schemas/URLImage"
+ },
+ "minItems" : 1
+ }
+ }
+ },
+ "UrlEntity" : {
+ "description" : "Represent the portion of text recognized as a URL, and its start and end position within the text.",
+ "allOf" : [ {
+ "$ref" : "#/components/schemas/EntityIndicesInclusiveExclusive"
+ }, {
+ "$ref" : "#/components/schemas/URLFields"
+ } ]
+ },
+ "URLImage" : {
+ "type" : "object",
+ "description" : "Represent the information for the URL image",
+ "properties" : {
+ "url" : {
+ "$ref" : "#/components/schemas/URL"
+ },
+ "height" : {
+ "$ref" : "#/components/schemas/MediaHeight"
+ },
+ "width" : {
+ "$ref" : "#/components/schemas/MediaWidth"
+ }
+ }
+ },
+ "HashtagFields" : {
+ "type" : "object",
+ "description" : "Represent the portion of text recognized as a Hashtag, and its start and end position within the text.",
+ "required" : [ "tag" ],
+ "properties" : {
+ "tag" : {
+ "type" : "string",
+ "description" : "The text of the Hashtag",
+ "example" : "MondayMotivation"
+ }
+ }
+ },
+ "HashtagEntity" : {
+ "allOf" : [ {
+ "$ref" : "#/components/schemas/EntityIndicesInclusiveExclusive"
+ }, {
+ "$ref" : "#/components/schemas/HashtagFields"
+ } ]
+ },
+ "CashtagFields" : {
+ "type" : "object",
+ "description" : "Represent the portion of text recognized as a Cashtag, and its start and end position within the text.",
+ "required" : [ "tag" ],
+ "properties" : {
+ "tag" : {
+ "type" : "string",
+ "example" : "TWTR"
+ }
+ }
+ },
+ "CashtagEntity" : {
+ "allOf" : [ {
+ "$ref" : "#/components/schemas/EntityIndicesInclusiveExclusive"
+ }, {
+ "$ref" : "#/components/schemas/CashtagFields"
+ } ]
+ },
+ "MentionFields" : {
+ "type" : "object",
+ "description" : "Represent the portion of text recognized as a User mention, and its start and end position within the text.",
+ "required" : [ "username", "id" ],
+ "properties" : {
+ "username" : {
+ "$ref" : "#/components/schemas/UserName"
+ },
+ "id" : {
+ "$ref" : "#/components/schemas/UserID"
+ }
+ }
+ },
+ "MentionEntity" : {
+ "allOf" : [ {
+ "$ref" : "#/components/schemas/EntityIndicesInclusiveExclusive"
+ }, {
+ "$ref" : "#/components/schemas/MentionFields"
+ } ]
+ },
+ "FullTextEntities" : {
+ "type" : "object",
+ "properties" : {
+ "urls" : {
+ "type" : "array",
+ "items" : {
+ "$ref" : "#/components/schemas/UrlEntity"
+ },
+ "minItems" : 1
+ },
+ "hashtags" : {
+ "type" : "array",
+ "items" : {
+ "$ref" : "#/components/schemas/HashtagEntity"
+ },
+ "minItems" : 1
+ },
+ "mentions" : {
+ "type" : "array",
+ "items" : {
+ "$ref" : "#/components/schemas/MentionEntity"
+ },
+ "minItems" : 1
+ },
+ "cashtags" : {
+ "type" : "array",
+ "items" : {
+ "$ref" : "#/components/schemas/CashtagEntity"
+ },
+ "minItems" : 1
+ }
+ }
+ },
+ "TweetID" : {
+ "type" : "string",
+ "description" : "Unique identifier of this Tweet. This is returned as a string in order to avoid complications with languages and tools that cannot handle large integers.",
+ "pattern" : "^[0-9]{1,19}$",
+ "example" : "1346889436626259968"
+ },
+ "UserID" : {
+ "type" : "string",
+ "description" : "Unique identifier of this User. This is returned as a string in order to avoid complications with languages and tools that cannot handle large integers.",
+ "pattern" : "^[0-9]{1,19}$",
+ "example" : "2244994945"
+ },
+ "ReplySettings" : {
+ "type" : "string",
+ "pattern" : "^[A-Za-z]{1,12}$",
+ "enum" : [ "everyone", "mentionedUsers", "following", "other" ],
+ "description" : "Shows who can reply a Tweet. Fields returned are everyone, mentioned_users, and following."
+ },
+ "Error" : {
+ "required" : [ "code", "message" ],
+ "properties" : {
+ "code" : {
+ "type" : "integer",
+ "format" : "int32"
+ },
+ "message" : {
+ "type" : "string"
+ }
+ }
+ },
+ "Expansions" : {
+ "type" : "object",
+ "properties" : {
+ "users" : {
+ "type" : "array",
+ "items" : {
+ "$ref" : "#/components/schemas/User"
+ },
+ "minItems" : 1
+ },
+ "tweets" : {
+ "type" : "array",
+ "items" : {
+ "$ref" : "#/components/schemas/Tweet"
+ },
+ "minItems" : 1
+ },
+ "places" : {
+ "type" : "array",
+ "items" : {
+ "$ref" : "#/components/schemas/Place"
+ },
+ "minItems" : 1
+ },
+ "media" : {
+ "type" : "array",
+ "items" : {
+ "$ref" : "#/components/schemas/Media"
+ },
+ "minItems" : 1
+ },
+ "polls" : {
+ "type" : "array",
+ "items" : {
+ "$ref" : "#/components/schemas/Poll"
+ },
+ "minItems" : 1
+ }
+ }
+ },
+ "Tweet" : {
+ "type" : "object",
+ "required" : [ "id", "text" ],
+ "properties" : {
+ "id" : {
+ "$ref" : "#/components/schemas/TweetID"
+ },
+ "created_at" : {
+ "type" : "string",
+ "format" : "date-time",
+ "description" : "Creation time of the Tweet.",
+ "example" : "2021-01-06T18:40:40.000Z"
+ },
+ "text" : {
+ "type" : "string",
+ "description" : "The content of the Tweet.",
+ "example" : "Learn how to use the user Tweet timeline and user mention timeline endpoints in the Twitter API v2 to explore Tweet\\u2026 https:\\/\\/t.co\\/56a0vZUx7i"
+ },
+ "author_id" : {
+ "$ref" : "#/components/schemas/UserID"
+ },
+ "in_reply_to_user_id" : {
+ "$ref" : "#/components/schemas/UserID"
+ },
+ "conversation_id" : {
+ "$ref" : "#/components/schemas/TweetID"
+ },
+ "reply_settings" : {
+ "$ref" : "#/components/schemas/ReplySettings"
+ },
+ "referenced_tweets" : {
+ "type" : "array",
+ "description" : "A list of Tweets this Tweet refers to. For example, if the parent Tweet is a Retweet, a Quoted Tweet or a Reply, it will include the related Tweet referenced to by its parent.",
+ "items" : {
+ "type" : "object",
+ "required" : [ "type", "id" ],
+ "properties" : {
+ "type" : {
+ "type" : "string",
+ "enum" : [ "retweeted", "quoted", "replied_to" ]
+ },
+ "id" : {
+ "$ref" : "#/components/schemas/TweetID"
+ }
+ }
+ },
+ "minItems" : 1
+ },
+ "attachments" : {
+ "type" : "object",
+ "description" : "Specifies the type of attachments (if any) present in this Tweet.",
+ "properties" : {
+ "media_keys" : {
+ "type" : "array",
+ "description" : "A list of Media Keys for each one of the media attachments (if media are attached).",
+ "items" : {
+ "$ref" : "#/components/schemas/MediaKey"
+ },
+ "minItems" : 1
+ },
+ "poll_ids" : {
+ "type" : "array",
+ "description" : "A list of poll IDs (if polls are attached).",
+ "items" : {
+ "$ref" : "#/components/schemas/PollId"
+ },
+ "minItems" : 1
+ }
+ }
+ },
+ "context_annotations" : {
+ "type" : "array",
+ "items" : {
+ "$ref" : "#/components/schemas/ContextAnnotation"
+ },
+ "minItems" : 1
+ },
+ "withheld" : {
+ "$ref" : "#/components/schemas/TweetWithheld"
+ },
+ "geo" : {
+ "type" : "object",
+ "description" : "The location tagged on the Tweet, if the user provided one.",
+ "properties" : {
+ "coordinates" : {
+ "$ref" : "#/components/schemas/Point"
+ },
+ "place_id" : {
+ "$ref" : "#/components/schemas/PlaceId"
+ }
+ }
+ },
+ "entities" : {
+ "$ref" : "#/components/schemas/FullTextEntities"
+ },
+ "public_metrics" : {
+ "type" : "object",
+ "description" : "Engagement metrics for the Tweet at the time of the request.",
+ "required" : [ "retweet_count", "reply_count", "like_count" ],
+ "properties" : {
+ "retweet_count" : {
+ "type" : "integer",
+ "description" : "Number of times this Tweet has been Retweeted."
+ },
+ "reply_count" : {
+ "type" : "integer",
+ "description" : "Number of times this Tweet has been replied to."
+ },
+ "like_count" : {
+ "type" : "integer",
+ "description" : "Number of times this Tweet has been liked."
+ },
+ "quote_count" : {
+ "type" : "integer",
+ "description" : "Number of times this Tweet has been quoted."
+ }
+ }
+ },
+ "possibly_sensitive" : {
+ "type" : "boolean",
+ "description" : "Indicates if this Tweet contains URLs marked as sensitive, for example content suitable for mature audiences.",
+ "example" : false
+ },
+ "lang" : {
+ "type" : "string",
+ "description" : "Language of the Tweet, if detected by Twitter. Returned as a BCP47 language tag.",
+ "example" : "en"
+ },
+ "source" : {
+ "type" : "string",
+ "description" : "The name of the app the user Tweeted from."
+ },
+ "non_public_metrics" : {
+ "type" : "object",
+ "description" : "Nonpublic engagement metrics for the Tweet at the time of the request.",
+ "properties" : {
+ "impression_count" : {
+ "type" : "integer",
+ "format" : "int32",
+ "description" : "Number of times this Tweet has been viewed."
+ }
+ }
+ },
+ "promoted_metrics" : {
+ "type" : "object",
+ "description" : "Promoted nonpublic engagement metrics for the Tweet at the time of the request.",
+ "properties" : {
+ "impression_count" : {
+ "type" : "integer",
+ "format" : "int32",
+ "description" : "Number of times this Tweet has been viewed."
+ },
+ "like_count" : {
+ "type" : "integer",
+ "format" : "int32",
+ "description" : "Number of times this Tweet has been liked."
+ },
+ "reply_count" : {
+ "type" : "integer",
+ "format" : "int32",
+ "description" : "Number of times this Tweet has been replied to."
+ },
+ "retweet_count" : {
+ "type" : "integer",
+ "format" : "int32",
+ "description" : "Number of times this Tweet has been Retweeted."
+ }
+ }
+ },
+ "organic_metrics" : {
+ "type" : "object",
+ "description" : "Organic nonpublic engagement metrics for the Tweet at the time of the request.",
+ "required" : [ "impression_count", "retweet_count", "reply_count", "like_count" ],
+ "properties" : {
+ "impression_count" : {
+ "type" : "integer",
+ "description" : "Number of times this Tweet has been viewed."
+ },
+ "retweet_count" : {
+ "type" : "integer",
+ "description" : "Number of times this Tweet has been Retweeted."
+ },
+ "reply_count" : {
+ "type" : "integer",
+ "description" : "Number of times this Tweet has been replied to."
+ },
+ "like_count" : {
+ "type" : "integer",
+ "description" : "Number of times this Tweet has been liked."
+ }
+ }
+ }
+ },
+ "example" : {
+ "id" : "1346889436626259968",
+ "created_at" : "Wed Jan 06 18:40:40 +0000 2021",
+ "text" : "Learn how to use the user Tweet timeline and user mention timeline endpoints in the Twitter API v2 to explore Tweet\\u2026 https:\\/\\/t.co\\/56a0vZUx7i",
+ "author_id" : "2244994945"
+ }
+ },
+ "User" : {
+ "description" : "The Twitter User object",
+ "required" : [ "id", "name", "username" ],
+ "properties" : {
+ "id" : {
+ "$ref" : "#/components/schemas/UserID"
+ },
+ "created_at" : {
+ "type" : "string",
+ "format" : "date-time",
+ "description" : "Creation time of this user."
+ },
+ "name" : {
+ "type" : "string",
+ "description" : "The friendly name of this user, as shown on their profile."
+ },
+ "username" : {
+ "$ref" : "#/components/schemas/UserName"
+ },
+ "protected" : {
+ "type" : "boolean",
+ "description" : "Indicates if this user has chosen to protect their Tweets (in other words, if this user's Tweets are private)."
+ },
+ "verified" : {
+ "type" : "boolean",
+ "description" : "Indicate if this user is a verified Twitter User."
+ },
+ "withheld" : {
+ "$ref" : "#/components/schemas/UserWithheld"
+ },
+ "profile_image_url" : {
+ "type" : "string",
+ "description" : "The URL to the profile image for this user.",
+ "format" : "uri"
+ },
+ "location" : {
+ "type" : "string",
+ "description" : "The location specified in the user's profile, if the user provided one. As this is a freeform value, it may not indicate a valid location, but it may be fuzzily evaluated when performing searches with location queries."
+ },
+ "url" : {
+ "type" : "string",
+ "description" : "The URL specified in the user's profile."
+ },
+ "description" : {
+ "type" : "string",
+ "description" : "The text of this user's profile description (also known as bio), if the user provided one."
+ },
+ "entities" : {
+ "type" : "object",
+ "description" : "A list of metadata found in the user's profile description.",
+ "properties" : {
+ "url" : {
+ "type" : "object",
+ "description" : "Expanded details for the URL specified in the user's profile, with start and end indices.",
+ "properties" : {
+ "urls" : {
+ "type" : "array",
+ "items" : {
+ "$ref" : "#/components/schemas/UrlEntity"
+ },
+ "minItems" : 1
+ }
+ }
+ },
+ "description" : {
+ "$ref" : "#/components/schemas/FullTextEntities"
+ }
+ }
+ },
+ "pinned_tweet_id" : {
+ "$ref" : "#/components/schemas/TweetID"
+ },
+ "public_metrics" : {
+ "type" : "object",
+ "description" : "A list of metrics for this user",
+ "required" : [ "followers_count", "following_count", "tweet_count", "listed_count" ],
+ "properties" : {
+ "followers_count" : {
+ "type" : "integer",
+ "description" : "Number of users who are following this user."
+ },
+ "following_count" : {
+ "type" : "integer",
+ "description" : "Number of users this user is following."
+ },
+ "tweet_count" : {
+ "type" : "integer",
+ "description" : "The number of Tweets (including Retweets) posted by this user."
+ },
+ "listed_count" : {
+ "type" : "integer",
+ "description" : "The number of lists that include this user."
+ }
+ }
+ }
+ },
+ "example" : {
+ "id" : "2244994945",
+ "created_at" : "2013-12-14T04:35:55Z",
+ "name" : "Twitter Dev",
+ "username" : "TwitterDev",
+ "protected" : false
+ }
+ },
+ "UserName" : {
+ "type" : "string",
+ "description" : "The Twitter handle (screen name) of this user.",
+ "pattern" : "^[A-Za-z0-9_]{1,15}$"
+ },
+ "MultiUserLookupResponse" : {
+ "type" : "object",
+ "properties" : {
+ "data" : {
+ "type" : "array",
+ "items" : {
+ "$ref" : "#/components/schemas/User"
+ },
+ "minItems" : 1
+ },
+ "includes" : {
+ "$ref" : "#/components/schemas/Expansions"
+ },
+ "errors" : {
+ "type" : "array",
+ "items" : {
+ "$ref" : "#/components/schemas/Problem"
+ },
+ "minItems" : 1
+ }
+ }
+ },
+ "SingleUserLookupResponse" : {
+ "type" : "object",
+ "properties" : {
+ "data" : {
+ "$ref" : "#/components/schemas/User"
+ },
+ "includes" : {
+ "$ref" : "#/components/schemas/Expansions"
+ },
+ "errors" : {
+ "type" : "array",
+ "items" : {
+ "$ref" : "#/components/schemas/Problem"
+ },
+ "minItems" : 1
+ }
+ }
+ },
+ "MultiTweetLookupResponse" : {
+ "type" : "object",
+ "properties" : {
+ "data" : {
+ "type" : "array",
+ "items" : {
+ "$ref" : "#/components/schemas/Tweet"
+ },
+ "minItems" : 1
+ },
+ "includes" : {
+ "$ref" : "#/components/schemas/Expansions"
+ },
+ "errors" : {
+ "type" : "array",
+ "items" : {
+ "$ref" : "#/components/schemas/Problem"
+ },
+ "minItems" : 1
+ }
+ }
+ },
+ "SingleTweetLookupResponse" : {
+ "type" : "object",
+ "properties" : {
+ "data" : {
+ "$ref" : "#/components/schemas/Tweet"
+ },
+ "includes" : {
+ "$ref" : "#/components/schemas/Expansions"
+ },
+ "errors" : {
+ "type" : "array",
+ "items" : {
+ "$ref" : "#/components/schemas/Problem"
+ },
+ "minItems" : 1
+ }
+ }
+ },
+ "Point" : {
+ "type" : "object",
+ "description" : "A [GeoJson Point](https://tools.ietf.org/html/rfc7946#section-3.1.2) geometry object.",
+ "required" : [ "type", "coordinates" ],
+ "properties" : {
+ "type" : {
+ "type" : "string",
+ "enum" : [ "Point" ],
+ "example" : "Point"
+ },
+ "coordinates" : {
+ "$ref" : "#/components/schemas/Position"
+ }
+ }
+ },
+ "Position" : {
+ "type" : "array",
+ "description" : "A [GeoJson Position](https://tools.ietf.org/html/rfc7946#section-3.1.1) in the format `[longitude,latitude]`.",
+ "items" : {
+ "type" : "number"
+ },
+ "minItems" : 2,
+ "maxItems" : 2,
+ "example" : [ -105.18816086351444, 40.247749999999996 ]
+ },
+ "Geo" : {
+ "type" : "object",
+ "required" : [ "type", "bbox", "properties" ],
+ "properties" : {
+ "type" : {
+ "type" : "string",
+ "enum" : [ "Feature" ]
+ },
+ "bbox" : {
+ "type" : "array",
+ "maxItems" : 4,
+ "minItems" : 4,
+ "items" : {
+ "type" : "number",
+ "format" : "double",
+ "minimum" : -180,
+ "maximum" : 180
+ },
+ "example" : [ -105.193475, 39.60973, -105.053164, 39.761974 ]
+ },
+ "geometry" : {
+ "$ref" : "#/components/schemas/Point"
+ },
+ "properties" : {
+ "type" : "object"
+ }
+ }
+ },
+ "PlaceId" : {
+ "type" : "string",
+ "description" : "The identifier for this place",
+ "example" : "f7eb2fa2fea288b1"
+ },
+ "Place" : {
+ "required" : [ "id", "full_name" ],
+ "properties" : {
+ "id" : {
+ "$ref" : "#/components/schemas/PlaceId"
+ },
+ "name" : {
+ "type" : "string",
+ "description" : "The human readable name of this place.",
+ "example" : "Lakewood"
+ },
+ "country_code" : {
+ "$ref" : "#/components/schemas/CountryCode"
+ },
+ "place_type" : {
+ "$ref" : "#/components/schemas/PlaceType"
+ },
+ "full_name" : {
+ "type" : "string",
+ "description" : "The full name of this place.",
+ "example" : "Lakewood, CO"
+ },
+ "country" : {
+ "type" : "string",
+ "description" : "The full name of the county in which this place exists.",
+ "example" : "United States"
+ },
+ "contained_within" : {
+ "type" : "array",
+ "items" : {
+ "$ref" : "#/components/schemas/PlaceId"
+ },
+ "minItems" : 1
+ },
+ "geo" : {
+ "$ref" : "#/components/schemas/Geo"
+ }
+ }
+ },
+ "PlaceType" : {
+ "type" : "string",
+ "enum" : [ "poi", "neighborhood", "city", "admin", "country", "unknown" ],
+ "example" : "city"
+ },
+ "Poll" : {
+ "type" : "object",
+ "description" : "Represent a Poll attached to a Tweet",
+ "required" : [ "id", "options" ],
+ "properties" : {
+ "id" : {
+ "$ref" : "#/components/schemas/PollId"
+ },
+ "options" : {
+ "type" : "array",
+ "items" : {
+ "$ref" : "#/components/schemas/PollOption"
+ },
+ "minItems" : 2,
+ "maxItems" : 4
+ },
+ "voting_status" : {
+ "type" : "string",
+ "enum" : [ "open", "closed" ]
+ },
+ "end_datetime" : {
+ "type" : "string",
+ "format" : "date-time"
+ },
+ "duration_minutes" : {
+ "type" : "integer"
+ }
+ }
+ },
+ "PollId" : {
+ "type" : "string",
+ "description" : "Unique identifier of this poll.",
+ "pattern" : "^[0-9]{1,19}$",
+ "example" : "1365059861688410112"
+ },
+ "PollOption" : {
+ "type" : "object",
+ "description" : "Describes a choice in a Poll object.",
+ "required" : [ "position", "label", "votes" ],
+ "properties" : {
+ "position" : {
+ "type" : "integer",
+ "description" : "Position of this choice in the poll."
+ },
+ "label" : {
+ "type" : "string",
+ "description" : "The text of a poll choice."
+ },
+ "votes" : {
+ "type" : "integer",
+ "description" : "Number of users who voted for this choice."
+ }
+ }
+ },
+ "List" : {
+ "type" : "object",
+ "description" : "Represent a List",
+ "required" : [ "id", "name" ],
+ "properties" : {
+ "id" : {
+ "$ref" : "#/components/schemas/ListId"
+ },
+ "name" : {
+ "type" : "string",
+ "description" : "The name of this list."
+ },
+ "created_at" : {
+ "type" : "string",
+ "format" : "date-time"
+ },
+ "follower_count" : {
+ "type" : "integer"
+ },
+ "member_count" : {
+ "type" : "integer"
+ },
+ "visibility" : {
+ "type" : "string",
+ "enum" : [ "Public", "Private" ]
+ },
+ "description" : {
+ "type" : "string"
+ },
+ "owner_id" : {
+ "$ref" : "#/components/schemas/UserID"
+ }
+ }
+ },
+ "ListId" : {
+ "type" : "string",
+ "description" : "Unique identifier of this list.",
+ "pattern" : "^[0-9]{1,19}$",
+ "example" : "1146654567674912769"
+ },
+ "CommonMediaFields" : {
+ "properties" : {
+ "media_key" : {
+ "$ref" : "#/components/schemas/MediaKey"
+ },
+ "height" : {
+ "$ref" : "#/components/schemas/MediaHeight"
+ },
+ "width" : {
+ "$ref" : "#/components/schemas/MediaWidth"
+ }
+ }
+ },
+ "Media" : {
+ "type" : "object",
+ "oneOf" : [ {
+ "$ref" : "#/components/schemas/Photo"
+ }, {
+ "$ref" : "#/components/schemas/Video"
+ }, {
+ "$ref" : "#/components/schemas/AnimatedGif"
+ } ],
+ "properties" : {
+ "type" : {
+ "type" : "string"
+ }
+ },
+ "required" : [ "type" ],
+ "discriminator" : {
+ "propertyName" : "type",
+ "mapping" : {
+ "photo" : "#/components/schemas/Photo",
+ "video" : "#/components/schemas/Video",
+ "animated_gif" : "#/components/schemas/AnimatedGif"
+ }
+ }
+ },
+ "Photo" : {
+ "type" : "object",
+ "allOf" : [ {
+ "$ref" : "#/components/schemas/CommonMediaFields"
+ }, {
+ "type" : "object",
+ "properties" : {
+ "type" : {
+ "type" : "string",
+ "enum" : [ "photo" ]
+ },
+ "url" : {
+ "type" : "string",
+ "format" : "uri"
+ },
+ "alt_text" : {
+ "type" : "string"
+ }
+ }
+ } ]
+ },
+ "Video" : {
+ "type" : "object",
+ "allOf" : [ {
+ "$ref" : "#/components/schemas/CommonMediaFields"
+ }, {
+ "type" : "object",
+ "properties" : {
+ "type" : {
+ "type" : "string",
+ "enum" : [ "video" ]
+ },
+ "preview_image_url" : {
+ "type" : "string",
+ "format" : "uri"
+ },
+ "duration_ms" : {
+ "type" : "integer"
+ },
+ "public_metrics" : {
+ "type" : "object",
+ "description" : "Engagement metrics for the Media at the time of the request.",
+ "properties" : {
+ "view_count" : {
+ "type" : "integer",
+ "format" : "int32",
+ "description" : "Number of times this video has been viewed."
+ }
+ }
+ },
+ "non_public_metrics" : {
+ "type" : "object",
+ "description" : "Nonpublic engagement metrics for the Media at the time of the request.",
+ "properties" : {
+ "playback_0_count" : {
+ "type" : "integer",
+ "format" : "int32",
+ "description" : "Number of users who made it through 0% of the video."
+ },
+ "playback_25_count" : {
+ "type" : "integer",
+ "format" : "int32",
+ "description" : "Number of users who made it through 25% of the video."
+ },
+ "playback_50_count" : {
+ "type" : "integer",
+ "format" : "int32",
+ "description" : "Number of users who made it through 50% of the video."
+ },
+ "playback_75_count" : {
+ "type" : "integer",
+ "format" : "int32",
+ "description" : "Number of users who made it through 75% of the video."
+ },
+ "playback_100_count" : {
+ "type" : "integer",
+ "format" : "int32",
+ "description" : "Number of users who made it through 100% of the video."
+ }
+ }
+ },
+ "organic_metrics" : {
+ "type" : "object",
+ "description" : "Organic nonpublic engagement metrics for the Media at the time of the request.",
+ "properties" : {
+ "playback_0_count" : {
+ "type" : "integer",
+ "format" : "int32",
+ "description" : "Number of users who made it through 0% of the video."
+ },
+ "playback_25_count" : {
+ "type" : "integer",
+ "format" : "int32",
+ "description" : "Number of users who made it through 25% of the video."
+ },
+ "playback_50_count" : {
+ "type" : "integer",
+ "format" : "int32",
+ "description" : "Number of users who made it through 50% of the video."
+ },
+ "playback_75_count" : {
+ "type" : "integer",
+ "format" : "int32",
+ "description" : "Number of users who made it through 75% of the video."
+ },
+ "playback_100_count" : {
+ "type" : "integer",
+ "format" : "int32",
+ "description" : "Number of users who made it through 100% of the video."
+ },
+ "view_count" : {
+ "type" : "integer",
+ "format" : "int32",
+ "description" : "Number of times this video has been viewed."
+ }
+ }
+ },
+ "promoted_metrics" : {
+ "type" : "object",
+ "description" : "Promoted nonpublic engagement metrics for the Media at the time of the request.",
+ "properties" : {
+ "playback_0_count" : {
+ "type" : "integer",
+ "format" : "int32",
+ "description" : "Number of users who made it through 0% of the video."
+ },
+ "playback_25_count" : {
+ "type" : "integer",
+ "format" : "int32",
+ "description" : "Number of users who made it through 25% of the video."
+ },
+ "playback_50_count" : {
+ "type" : "integer",
+ "format" : "int32",
+ "description" : "Number of users who made it through 50% of the video."
+ },
+ "playback_75_count" : {
+ "type" : "integer",
+ "format" : "int32",
+ "description" : "Number of users who made it through 75% of the video."
+ },
+ "playback_100_count" : {
+ "type" : "integer",
+ "format" : "int32",
+ "description" : "Number of users who made it through 100% of the video."
+ },
+ "view_count" : {
+ "type" : "integer",
+ "format" : "int32",
+ "description" : "Number of times this video has been viewed."
+ }
+ }
+ }
+ }
+ } ]
+ },
+ "AnimatedGif" : {
+ "type" : "object",
+ "allOf" : [ {
+ "$ref" : "#/components/schemas/CommonMediaFields"
+ }, {
+ "type" : "object",
+ "properties" : {
+ "type" : {
+ "type" : "string",
+ "enum" : [ "animated_gif" ]
+ },
+ "preview_image_url" : {
+ "type" : "string",
+ "format" : "uri"
+ }
+ }
+ } ]
+ },
+ "MediaKey" : {
+ "type" : "string",
+ "description" : "The Media Key identifier for this attachment.",
+ "pattern" : "^([0-9]+)_([0-9]+)$"
+ },
+ "TweetWithheld" : {
+ "type" : "object",
+ "description" : "Indicates withholding details for [withheld content](https://help.twitter.com/en/rules-and-policies/tweet-withheld-by-country).",
+ "required" : [ "copyright", "country_codes" ],
+ "properties" : {
+ "copyright" : {
+ "type" : "boolean",
+ "description" : "Indicates if the content is being withheld for on the basis of copyright infringement."
+ },
+ "country_codes" : {
+ "type" : "array",
+ "description" : "Provides a list of countries where this content is not available.",
+ "items" : {
+ "$ref" : "#/components/schemas/CountryCode"
+ },
+ "uniqueItems" : true,
+ "minItems" : 1
+ },
+ "scope" : {
+ "type" : "string",
+ "description" : "Indicates whether the content being withheld is the `tweet` or a `user`.",
+ "enum" : [ "tweet", "user" ]
+ }
+ }
+ },
+ "UserWithheld" : {
+ "type" : "object",
+ "description" : "Indicates withholding details for [withheld content](https://help.twitter.com/en/rules-and-policies/tweet-withheld-by-country).",
+ "required" : [ "country_codes" ],
+ "properties" : {
+ "country_codes" : {
+ "type" : "array",
+ "description" : "Provides a list of countries where this content is not available.",
+ "items" : {
+ "$ref" : "#/components/schemas/CountryCode"
+ },
+ "uniqueItems" : true,
+ "minItems" : 1
+ },
+ "scope" : {
+ "type" : "string",
+ "description" : "Indicates that the content being withheld is a `user`.",
+ "enum" : [ "user" ]
+ }
+ }
+ },
+ "CountryCode" : {
+ "type" : "string",
+ "pattern" : "^[A-Z]{2}$",
+ "description" : "A two-letter ISO 3166-1 alpha-2 country code",
+ "example" : "US"
+ },
+ "ProblemFields" : {
+ "type" : "object",
+ "required" : [ "title", "detail" ],
+ "properties" : {
+ "title" : {
+ "type" : "string"
+ },
+ "detail" : {
+ "type" : "string"
+ }
+ }
+ },
+ "GenericProblem" : {
+ "description" : "A generic problem with no additional information beyond that provided by the HTTP status code.",
+ "allOf" : [ {
+ "$ref" : "#/components/schemas/ProblemFields"
+ }, {
+ "type" : "object",
+ "required" : [ "status" ],
+ "properties" : {
+ "type" : {
+ "type" : "string",
+ "enum" : [ "about:blank" ]
+ },
+ "status" : {
+ "type" : "integer"
+ }
+ }
+ } ]
+ },
+ "InvalidRequestProblem" : {
+ "description" : "A problem that indicates this request is invalid.",
+ "allOf" : [ {
+ "$ref" : "#/components/schemas/ProblemFields"
+ }, {
+ "type" : "object",
+ "properties" : {
+ "type" : {
+ "type" : "string",
+ "enum" : [ "https://api.twitter.com/2/problems/invalid-request" ]
+ },
+ "errors" : {
+ "type" : "array",
+ "items" : {
+ "type" : "object",
+ "properties" : {
+ "parameters" : {
+ "type" : "object",
+ "additionalProperties" : {
+ "type" : "array",
+ "items" : {
+ "type" : "string"
+ }
+ }
+ },
+ "message" : {
+ "type" : "string"
+ }
+ }
+ }
+ }
+ }
+ } ]
+ },
+ "ResourceNotFoundProblem" : {
+ "description" : "A problem that indicates that a given Tweet, User, etc. does not exist.",
+ "allOf" : [ {
+ "$ref" : "#/components/schemas/ProblemFields"
+ }, {
+ "required" : [ "parameter", "value", "resource_id", "resource_type" ],
+ "properties" : {
+ "type" : {
+ "type" : "string",
+ "enum" : [ "https://api.twitter.com/2/problems/resource-not-found" ]
+ },
+ "parameter" : {
+ "type" : "string",
+ "minLength" : 1
+ },
+ "value" : {
+ "description" : "Value will match the schema of the field."
+ },
+ "resource_id" : {
+ "type" : "string"
+ },
+ "resource_type" : {
+ "type" : "string",
+ "enum" : [ "user", "tweet", "media" ]
+ }
+ }
+ } ]
+ },
+ "ResourceUnauthorizedProblem" : {
+ "description" : "A problem that indicates you are not allowed to see a particular Tweet, User, etc.",
+ "type" : "object",
+ "allOf" : [ {
+ "$ref" : "#/components/schemas/ProblemFields"
+ }, {
+ "type" : "object",
+ "required" : [ "value", "resource_id", "resource_type", "section", "parameter" ],
+ "properties" : {
+ "type" : {
+ "type" : "string",
+ "enum" : [ "https://api.twitter.com/2/problems/not-authorized-for-resource" ]
+ },
+ "value" : {
+ "type" : "string"
+ },
+ "parameter" : {
+ "type" : "string"
+ },
+ "section" : {
+ "type" : "string",
+ "enum" : [ "data", "includes" ]
+ },
+ "resource_id" : {
+ "type" : "string"
+ },
+ "resource_type" : {
+ "type" : "string",
+ "enum" : [ "tweet", "user", "media" ]
+ }
+ }
+ } ]
+ },
+ "ResourceUnavailableProblem" : {
+ "description" : "A problem that indicates a particular Tweet, User, etc. is not available to you.",
+ "allOf" : [ {
+ "$ref" : "#/components/schemas/ProblemFields"
+ }, {
+ "required" : [ "parameter", "resource_id", "resource_type" ],
+ "properties" : {
+ "type" : {
+ "type" : "string",
+ "enum" : [ "https://api.twitter.com/2/problems/resource-unavailable" ]
+ },
+ "parameter" : {
+ "type" : "string",
+ "minLength" : 1
+ },
+ "resource_id" : {
+ "type" : "string"
+ },
+ "resource_type" : {
+ "type" : "string",
+ "enum" : [ "user", "tweet", "media" ]
+ }
+ }
+ } ]
+ },
+ "FieldUnauthorizedProblem" : {
+ "description" : "A problem that indicates that you are not allowed to see a particular field on a Tweet, User, etc.",
+ "type" : "object",
+ "allOf" : [ {
+ "$ref" : "#/components/schemas/ProblemFields"
+ }, {
+ "type" : "object",
+ "required" : [ "resource_type", "field", "section" ],
+ "properties" : {
+ "type" : {
+ "type" : "string",
+ "enum" : [ "https://api.twitter.com/2/problems/not-authorized-for-field" ]
+ },
+ "section" : {
+ "type" : "string",
+ "enum" : [ "data", "includes" ]
+ },
+ "resource_type" : {
+ "type" : "string",
+ "enum" : [ "tweet", "media" ]
+ },
+ "field" : {
+ "type" : "string"
+ }
+ }
+ } ]
+ },
+ "ClientForbiddenProblem" : {
+ "description" : "A problem that indicates your client is forbidden from making this request.",
+ "type" : "object",
+ "allOf" : [ {
+ "$ref" : "#/components/schemas/ProblemFields"
+ }, {
+ "type" : "object",
+ "properties" : {
+ "type" : {
+ "type" : "string",
+ "enum" : [ "https://api.twitter.com/2/problems/client-forbidden" ]
+ },
+ "reason" : {
+ "type" : "string",
+ "enum" : [ "official-client-forbidden", "client-not-enrolled" ]
+ },
+ "registration_url" : {
+ "type" : "string",
+ "format" : "uri"
+ }
+ }
+ } ]
+ },
+ "DisallowedResourceProblem" : {
+ "description" : "A problem that indicates that the resource requested violates the precepts of this API.",
+ "type" : "object",
+ "allOf" : [ {
+ "$ref" : "#/components/schemas/ProblemFields"
+ }, {
+ "type" : "object",
+ "required" : [ "resource_id", "resource_type", "section" ],
+ "properties" : {
+ "type" : {
+ "type" : "string",
+ "enum" : [ "https://api.twitter.com/2/problems/disallowed-resource" ]
+ },
+ "resource_id" : {
+ "type" : "string"
+ },
+ "resource_type" : {
+ "type" : "string",
+ "enum" : [ "tweet", "media" ]
+ },
+ "section" : {
+ "type" : "string",
+ "enum" : [ "data", "includes" ]
+ }
+ }
+ } ]
+ },
+ "UnsupportedAuthenticationProblem" : {
+ "description" : "A problem that indicates that the authentication used is not supported.",
+ "type" : "object",
+ "allOf" : [ {
+ "$ref" : "#/components/schemas/ProblemFields"
+ }, {
+ "type" : "object",
+ "properties" : {
+ "type" : {
+ "type" : "string",
+ "enum" : [ "https://api.twitter.com/2/problems/unsupported-authentication" ]
+ }
+ }
+ } ]
+ },
+ "UsageCapExceededProblem" : {
+ "description" : "A problem that indicates that a usage cap has been exceeded.",
+ "type" : "object",
+ "allOf" : [ {
+ "$ref" : "#/components/schemas/ProblemFields"
+ }, {
+ "type" : "object",
+ "properties" : {
+ "type" : {
+ "type" : "string",
+ "enum" : [ "https://api.twitter.com/2/problems/usage-capped" ]
+ },
+ "period" : {
+ "type" : "string",
+ "enum" : [ "Daily", "Monthly" ]
+ },
+ "scope" : {
+ "type" : "string",
+ "enum" : [ "Account", "Product" ]
+ }
+ }
+ } ]
+ },
+ "ConnectionExceptionProblem" : {
+ "description" : "A problem that indicates something is wrong with the connection",
+ "type" : "object",
+ "allOf" : [ {
+ "$ref" : "#/components/schemas/ProblemFields"
+ }, {
+ "properties" : {
+ "type" : {
+ "type" : "string",
+ "enum" : [ "https://api.twitter.com/2/problems/streaming-connection" ]
+ },
+ "connection_issue" : {
+ "type" : "string",
+ "enum" : [ "TooManyConnections", "ProvisioningSubscription", "RuleConfigurationIssue" ]
+ }
+ }
+ } ]
+ },
+ "ClientDisconnectedProblem" : {
+ "description" : "Your client has gone away.",
+ "type" : "object",
+ "allOf" : [ {
+ "$ref" : "#/components/schemas/ProblemFields"
+ }, {
+ "properties" : {
+ "type" : {
+ "type" : "string",
+ "enum" : [ "https://api.twitter.com/2/problems/client-disconnected" ]
+ }
+ }
+ } ]
+ },
+ "OperationalDisconnectProblem" : {
+ "description" : "You have been disconnected for operational reasons.",
+ "type" : "object",
+ "allOf" : [ {
+ "$ref" : "#/components/schemas/ProblemFields"
+ }, {
+ "properties" : {
+ "type" : {
+ "type" : "string",
+ "enum" : [ "https://api.twitter.com/2/problems/operational-disconnect" ]
+ },
+ "disconnect_type" : {
+ "type" : "string",
+ "enum" : [ "OperationalDisconnect", "UpstreamOperationalDisconnect", "ForceDisconnect", "UpstreamUncleanDisconnect", "SlowReader", "InternalError", "PackageUpgraded", "PackageDowngraded", "ClientApplicationStateDegraded" ]
+ }
+ }
+ } ]
+ },
+ "RulesCapProblem" : {
+ "description" : "You have exceeded the maximum number of rules.",
+ "type" : "object",
+ "allOf" : [ {
+ "$ref" : "#/components/schemas/ProblemFields"
+ }, {
+ "properties" : {
+ "type" : {
+ "type" : "string",
+ "enum" : [ "https://api.twitter.com/2/problems/rule-cap" ]
+ }
+ }
+ } ]
+ },
+ "InvalidRuleProblem" : {
+ "description" : "The rule you have submitted is invalid.",
+ "type" : "object",
+ "allOf" : [ {
+ "$ref" : "#/components/schemas/ProblemFields"
+ }, {
+ "properties" : {
+ "type" : {
+ "type" : "string",
+ "enum" : [ "https://api.twitter.com/2/problems/invalid-rules" ]
+ }
+ }
+ } ]
+ },
+ "DuplicateRuleProblem" : {
+ "description" : "The rule you have submitted is a duplicate.",
+ "type" : "object",
+ "allOf" : [ {
+ "$ref" : "#/components/schemas/ProblemFields"
+ }, {
+ "properties" : {
+ "type" : {
+ "type" : "string",
+ "enum" : [ "https://api.twitter.com/2/problems/duplicate-rules" ]
+ }
+ }
+ } ]
+ },
+ "Problem" : {
+ "description" : "An HTTP Problem Details object, as defined in IETF RFC 7807 (https://tools.ietf.org/html/rfc7807).",
+ "oneOf" : [ {
+ "$ref" : "#/components/schemas/GenericProblem"
+ }, {
+ "$ref" : "#/components/schemas/InvalidRequestProblem"
+ }, {
+ "$ref" : "#/components/schemas/ClientForbiddenProblem"
+ }, {
+ "$ref" : "#/components/schemas/ResourceNotFoundProblem"
+ }, {
+ "$ref" : "#/components/schemas/ResourceUnavailableProblem"
+ }, {
+ "$ref" : "#/components/schemas/ResourceUnauthorizedProblem"
+ }, {
+ "$ref" : "#/components/schemas/FieldUnauthorizedProblem"
+ }, {
+ "$ref" : "#/components/schemas/DisallowedResourceProblem"
+ }, {
+ "$ref" : "#/components/schemas/UnsupportedAuthenticationProblem"
+ }, {
+ "$ref" : "#/components/schemas/UsageCapExceededProblem"
+ }, {
+ "$ref" : "#/components/schemas/ConnectionExceptionProblem"
+ }, {
+ "$ref" : "#/components/schemas/ClientDisconnectedProblem"
+ }, {
+ "$ref" : "#/components/schemas/OperationalDisconnectProblem"
+ }, {
+ "$ref" : "#/components/schemas/RulesCapProblem"
+ }, {
+ "$ref" : "#/components/schemas/InvalidRuleProblem"
+ }, {
+ "$ref" : "#/components/schemas/DuplicateRuleProblem"
+ } ],
+ "properties" : {
+ "type" : {
+ "type" : "string"
+ }
+ },
+ "required" : [ "type" ],
+ "discriminator" : {
+ "propertyName" : "type",
+ "mapping" : {
+ "about:blank" : "#/components/schemas/GenericProblem",
+ "https://api.twitter.com/2/problems/invalid-request" : "#/components/schemas/InvalidRequestProblem",
+ "https://api.twitter.com/2/problems/client-forbidden" : "#/components/schemas/ClientForbiddenProblem",
+ "https://api.twitter.com/2/problems/resource-not-found" : "#/components/schemas/ResourceNotFoundProblem",
+ "https://api.twitter.com/2/problems/resource-unavailable" : "#/components/schemas/ResourceUnavailableProblem",
+ "https://api.twitter.com/2/problems/not-authorized-for-resource" : "#/components/schemas/ResourceUnauthorizedProblem",
+ "https://api.twitter.com/2/problems/not-authorized-for-field" : "#/components/schemas/FieldUnauthorizedProblem",
+ "https://api.twitter.com/2/problems/disallowed-resource" : "#/components/schemas/DisallowedResourceProblem",
+ "https://api.twitter.com/2/problems/unsupported-authentication" : "#/components/schemas/UnsupportedAuthenticationProblem",
+ "https://api.twitter.com/2/problems/usage-capped" : "#/components/schemas/UsageCapExceededProblem",
+ "https://api.twitter.com/2/problems/streaming-connection" : "#/components/schemas/ConnectionExceptionProblem",
+ "https://api.twitter.com/2/problems/client-disconnected" : "#/components/schemas/ClientDisconnectedProblem",
+ "https://api.twitter.com/2/problems/operational-disconnect" : "#/components/schemas/OperationalDisconnectProblem",
+ "https://api.twitter.com/2/problems/rule-cap" : "#/components/schemas/RulesCapProblem",
+ "https://api.twitter.com/2/problems/invalid-rules" : "#/components/schemas/InvalidRuleProblem",
+ "https://api.twitter.com/2/problems/duplicate-rules" : "#/components/schemas/DuplicateRuleProblem"
+ }
+ }
+ },
+ "TweetSearchResponse" : {
+ "type" : "object",
+ "properties" : {
+ "data" : {
+ "type" : "array",
+ "items" : {
+ "$ref" : "#/components/schemas/Tweet"
+ },
+ "minItems" : 1
+ },
+ "includes" : {
+ "$ref" : "#/components/schemas/Expansions"
+ },
+ "errors" : {
+ "type" : "array",
+ "items" : {
+ "$ref" : "#/components/schemas/Problem"
+ },
+ "minItems" : 1
+ },
+ "meta" : {
+ "type" : "object",
+ "properties" : {
+ "newest_id" : {
+ "type" : "string",
+ "pattern" : "^[0-9]{1,19}$",
+ "description" : "Most recent Tweet Id returned by search query"
+ },
+ "oldest_id" : {
+ "type" : "string",
+ "pattern" : "^[0-9]{1,19}$",
+ "description" : "Oldest Tweet Id returned by search query"
+ },
+ "next_token" : {
+ "description" : "This value is used to get the next 'page' of results by providing it to the next_token parameter.",
+ "type" : "string"
+ },
+ "result_count" : {
+ "description" : "Number of search query results",
+ "type" : "integer",
+ "format" : "int32"
+ }
+ }
+ }
+ }
+ },
+ "StreamingTweet" : {
+ "type" : "object",
+ "description" : "A tweet or error that can be returned by the streaming tweet API.",
+ "oneOf" : [ {
+ "$ref" : "#/components/schemas/SingleTweetLookupResponse"
+ }, {
+ "type" : "object",
+ "required" : [ "errors" ],
+ "properties" : {
+ "errors" : {
+ "type" : "array",
+ "items" : {
+ "$ref" : "#/components/schemas/Problem"
+ }
+ }
+ }
+ } ]
+ },
+ "FilteredStreamingTweet" : {
+ "type" : "object",
+ "description" : "A tweet or error that can be returned by the streaming tweet API",
+ "oneOf" : [ {
+ "type" : "object",
+ "description" : "The values returned with a successful streamed tweet. Includes the user provided rules that the tweet matched.",
+ "required" : [ "data", "matching_rules" ],
+ "properties" : {
+ "data" : {
+ "$ref" : "#/components/schemas/Tweet"
+ },
+ "matching_rules" : {
+ "type" : "array",
+ "description" : "The list of rules which matched the tweet",
+ "items" : {
+ "type" : "object",
+ "required" : [ "id" ],
+ "properties" : {
+ "id" : {
+ "$ref" : "#/components/schemas/RuleId"
+ },
+ "tag" : {
+ "type" : "string",
+ "description" : "The user-supplied tag assigned to the rule which matched"
+ }
+ }
+ }
+ },
+ "includes" : {
+ "$ref" : "#/components/schemas/Expansions"
+ }
+ }
+ }, {
+ "type" : "object",
+ "required" : [ "errors" ],
+ "properties" : {
+ "errors" : {
+ "type" : "array",
+ "items" : {
+ "$ref" : "#/components/schemas/Problem"
+ }
+ }
+ }
+ } ]
+ },
+ "RuleId" : {
+ "type" : "string",
+ "description" : "Unique identifier of this rule.",
+ "pattern" : "^[0-9]{1,19}$",
+ "example" : "120897978112909812"
+ },
+ "Rule" : {
+ "type" : "object",
+ "description" : "A user-provided stream filtering rule.",
+ "required" : [ "value" ],
+ "properties" : {
+ "value" : {
+ "$ref" : "#/components/schemas/RuleValue"
+ },
+ "tag" : {
+ "$ref" : "#/components/schemas/RuleTag"
+ },
+ "id" : {
+ "$ref" : "#/components/schemas/RuleId"
+ }
+ }
+ },
+ "RuleNoId" : {
+ "type" : "object",
+ "description" : "A user-provided stream filtering rule.",
+ "required" : [ "value" ],
+ "properties" : {
+ "value" : {
+ "$ref" : "#/components/schemas/RuleValue"
+ },
+ "tag" : {
+ "$ref" : "#/components/schemas/RuleTag"
+ }
+ }
+ },
+ "RuleTag" : {
+ "type" : "string",
+ "description" : "A tag meant for the labeling of user provided rules.",
+ "example" : "Non-retweeted coffee tweets"
+ },
+ "RuleValue" : {
+ "type" : "string",
+ "description" : "The filterlang value of the rule.",
+ "example" : "coffee -is:retweet"
+ },
+ "RulesResponseMetadata" : {
+ "type" : "object",
+ "required" : [ "sent" ],
+ "properties" : {
+ "sent" : {
+ "type" : "string"
+ },
+ "summary" : {
+ "$ref" : "#/components/schemas/RulesRequestSummary"
+ }
+ }
+ },
+ "RulesRequestSummary" : {
+ "type" : "object",
+ "oneOf" : [ {
+ "type" : "object",
+ "description" : "A summary of the results of the addition of user-specified stream filtering rules.",
+ "required" : [ "created", "not_created", "valid", "invalid" ],
+ "properties" : {
+ "created" : {
+ "type" : "integer",
+ "description" : "Number of user-specified stream filtering rules that were created.",
+ "format" : "int32",
+ "example" : 1
+ },
+ "not_created" : {
+ "type" : "integer",
+ "description" : "Number of user-specified stream filtering rules that were not created.",
+ "format" : "int32",
+ "example" : 1
+ },
+ "valid" : {
+ "type" : "integer",
+ "description" : "Number of valid user-specified stream filtering rules.",
+ "format" : "int32",
+ "example" : 1
+ },
+ "invalid" : {
+ "type" : "integer",
+ "description" : "Number of invalid user-specified stream filtering rules.",
+ "format" : "int32",
+ "example" : 1
+ }
+ }
+ }, {
+ "type" : "object",
+ "required" : [ "deleted", "not_deleted" ],
+ "properties" : {
+ "deleted" : {
+ "type" : "integer",
+ "description" : "Number of user-specified stream filtering rules that were deleted.",
+ "format" : "int32"
+ },
+ "not_deleted" : {
+ "type" : "integer",
+ "description" : "Number of user-specified stream filtering rules that were not deleted.",
+ "format" : "int32"
+ }
+ }
+ } ]
+ },
+ "AddOrDeleteRulesRequest" : {
+ "type" : "object",
+ "oneOf" : [ {
+ "$ref" : "#/components/schemas/AddRulesRequest"
+ }, {
+ "$ref" : "#/components/schemas/DeleteRulesRequest"
+ } ]
+ },
+ "AddOrDeleteRulesResponse" : {
+ "type" : "object",
+ "oneOf" : [ {
+ "$ref" : "#/components/schemas/AddRulesResponse"
+ }, {
+ "$ref" : "#/components/schemas/DeleteRulesResponse"
+ } ]
+ },
+ "AddRulesRequest" : {
+ "type" : "object",
+ "description" : "A request to add a user-specified stream filtering rule.",
+ "required" : [ "add" ],
+ "properties" : {
+ "add" : {
+ "type" : "array",
+ "items" : {
+ "$ref" : "#/components/schemas/RuleNoId"
+ }
+ }
+ }
+ },
+ "AddRulesResponse" : {
+ "type" : "object",
+ "description" : "A response from adding user-specified stream filtering rules.",
+ "required" : [ "data", "meta" ],
+ "properties" : {
+ "data" : {
+ "type" : "array",
+ "description" : "All user-specified stream filtering rules that were created.",
+ "items" : {
+ "$ref" : "#/components/schemas/Rule"
+ }
+ },
+ "meta" : {
+ "$ref" : "#/components/schemas/RulesResponseMetadata"
+ }
+ }
+ },
+ "DeleteRulesRequest" : {
+ "type" : "object",
+ "description" : "A response from deleting user-specified stream filtering rules.",
+ "required" : [ "delete" ],
+ "properties" : {
+ "delete" : {
+ "type" : "object",
+ "description" : "IDs and values of all deleted user-specified stream filtering rules.",
+ "properties" : {
+ "ids" : {
+ "type" : "array",
+ "description" : "IDs of all deleted user-specified stream filtering rules.",
+ "items" : {
+ "$ref" : "#/components/schemas/RuleId"
+ }
+ },
+ "values" : {
+ "type" : "array",
+ "description" : "Values of all deleted user-specified stream filtering rules.",
+ "items" : {
+ "$ref" : "#/components/schemas/RuleValue"
+ }
+ }
+ }
+ }
+ }
+ },
+ "DeleteRulesResponse" : {
+ "type" : "object",
+ "required" : [ "meta" ],
+ "properties" : {
+ "meta" : {
+ "$ref" : "#/components/schemas/RulesResponseMetadata"
+ }
+ }
+ },
+ "GenericTweetsTimelineResponse" : {
+ "type" : "object",
+ "properties" : {
+ "data" : {
+ "type" : "array",
+ "items" : {
+ "$ref" : "#/components/schemas/Tweet"
+ },
+ "minItems" : 1
+ },
+ "includes" : {
+ "$ref" : "#/components/schemas/Expansions"
+ },
+ "errors" : {
+ "type" : "array",
+ "items" : {
+ "$ref" : "#/components/schemas/Problem"
+ },
+ "minItems" : 1
+ },
+ "meta" : {
+ "type" : "object",
+ "properties" : {
+ "newest_id" : {
+ "description" : "Newest Tweet ID in the result set",
+ "$ref" : "#/components/schemas/TweetID"
+ },
+ "oldest_id" : {
+ "description" : "Oldest Tweet ID in the result set",
+ "$ref" : "#/components/schemas/TweetID"
+ },
+ "previous_token" : {
+ "description" : "The previous token",
+ "type" : "string",
+ "pattern" : "^[a-zA-Z0-9]+$"
+ },
+ "next_token" : {
+ "description" : "The next token",
+ "type" : "string",
+ "pattern" : "^[a-zA-Z0-9]+$"
+ },
+ "result_count" : {
+ "description" : "Number of Tweets in result set",
+ "type" : "integer",
+ "format" : "int32"
+ }
+ }
+ }
+ }
+ },
+ "UsersBlockingMutationResponse" : {
+ "type" : "object",
+ "properties" : {
+ "data" : {
+ "type" : "object",
+ "properties" : {
+ "blocking" : {
+ "type" : "boolean"
+ }
+ }
+ },
+ "errors" : {
+ "type" : "array",
+ "items" : {
+ "$ref" : "#/components/schemas/Problem"
+ },
+ "minItems" : 1
+ }
+ }
+ },
+ "GenericMultipleUsersLookupResponse" : {
+ "type" : "object",
+ "properties" : {
+ "data" : {
+ "type" : "array",
+ "items" : {
+ "$ref" : "#/components/schemas/User"
+ }
+ },
+ "includes" : {
+ "$ref" : "#/components/schemas/Expansions"
+ },
+ "errors" : {
+ "type" : "array",
+ "items" : {
+ "$ref" : "#/components/schemas/Problem"
+ },
+ "minItems" : 1
+ },
+ "meta" : {
+ "type" : "object",
+ "properties" : {
+ "previous_token" : {
+ "description" : "The previous token",
+ "type" : "string",
+ "pattern" : "^[a-zA-Z0-9]+$"
+ },
+ "next_token" : {
+ "description" : "The next token",
+ "type" : "string",
+ "pattern" : "^[a-zA-Z0-9]+$"
+ },
+ "result_count" : {
+ "description" : "The number of user results returned in this response",
+ "type" : "integer"
+ }
+ }
+ }
+ }
+ },
+ "UsersMutingMutationResponse" : {
+ "type" : "object",
+ "properties" : {
+ "data" : {
+ "type" : "object",
+ "properties" : {
+ "muting" : {
+ "type" : "boolean"
+ }
+ }
+ },
+ "errors" : {
+ "type" : "array",
+ "items" : {
+ "$ref" : "#/components/schemas/Problem"
+ },
+ "minItems" : 1
+ }
+ }
+ },
+ "UsersFollowingLookupResponse" : {
+ "type" : "object",
+ "properties" : {
+ "data" : {
+ "type" : "array",
+ "items" : {
+ "$ref" : "#/components/schemas/User"
+ }
+ },
+ "includes" : {
+ "$ref" : "#/components/schemas/Expansions"
+ },
+ "errors" : {
+ "type" : "array",
+ "items" : {
+ "$ref" : "#/components/schemas/Problem"
+ },
+ "minItems" : 1
+ },
+ "meta" : {
+ "type" : "object",
+ "properties" : {
+ "previous_token" : {
+ "description" : "The previous token",
+ "type" : "string",
+ "pattern" : "^[a-zA-Z0-9]+$"
+ },
+ "next_token" : {
+ "description" : "The next token",
+ "type" : "string",
+ "pattern" : "^[a-zA-Z0-9]+$"
+ },
+ "result_count" : {
+ "description" : "The number of user results returned in this response",
+ "type" : "integer"
+ }
+ }
+ }
+ }
+ },
+ "UsersFollowingCreateResponse" : {
+ "type" : "object",
+ "properties" : {
+ "data" : {
+ "type" : "object",
+ "properties" : {
+ "following" : {
+ "type" : "boolean"
+ },
+ "pending_follow" : {
+ "type" : "boolean"
+ }
+ }
+ },
+ "errors" : {
+ "type" : "array",
+ "items" : {
+ "$ref" : "#/components/schemas/Problem"
+ },
+ "minItems" : 1
+ }
+ }
+ },
+ "UsersFollowingDeleteResponse" : {
+ "type" : "object",
+ "properties" : {
+ "data" : {
+ "type" : "object",
+ "properties" : {
+ "following" : {
+ "type" : "boolean"
+ }
+ }
+ },
+ "errors" : {
+ "type" : "array",
+ "items" : {
+ "$ref" : "#/components/schemas/Problem"
+ },
+ "minItems" : 1
+ }
+ }
+ },
+ "UsersLikesCreateRequest" : {
+ "type" : "object",
+ "required" : [ "tweet_id" ],
+ "properties" : {
+ "tweet_id" : {
+ "type" : "string",
+ "pattern" : "^[0-9]{1,19}$"
+ }
+ }
+ },
+ "UsersLikesCreateResponse" : {
+ "type" : "object",
+ "properties" : {
+ "data" : {
+ "type" : "object",
+ "properties" : {
+ "liked" : {
+ "type" : "boolean"
+ }
+ }
+ },
+ "errors" : {
+ "type" : "array",
+ "items" : {
+ "$ref" : "#/components/schemas/Problem"
+ },
+ "minItems" : 1
+ }
+ }
+ },
+ "UsersLikesDeleteResponse" : {
+ "type" : "object",
+ "properties" : {
+ "data" : {
+ "type" : "object",
+ "properties" : {
+ "liked" : {
+ "type" : "boolean"
+ }
+ }
+ },
+ "errors" : {
+ "type" : "array",
+ "items" : {
+ "$ref" : "#/components/schemas/Problem"
+ },
+ "minItems" : 1
+ }
+ }
+ },
+ "UsersRetweetsCreateRequest" : {
+ "type" : "object",
+ "required" : [ "tweet_id" ],
+ "properties" : {
+ "tweet_id" : {
+ "type" : "string",
+ "pattern" : "^[0-9]{1,19}$"
+ }
+ }
+ },
+ "UsersRetweetsCreateResponse" : {
+ "type" : "object",
+ "properties" : {
+ "data" : {
+ "type" : "object",
+ "properties" : {
+ "retweeted" : {
+ "type" : "boolean"
+ }
+ }
+ },
+ "errors" : {
+ "type" : "array",
+ "items" : {
+ "$ref" : "#/components/schemas/Problem"
+ },
+ "minItems" : 1
+ }
+ }
+ },
+ "UsersRetweetsDeleteResponse" : {
+ "type" : "object",
+ "properties" : {
+ "data" : {
+ "type" : "object",
+ "properties" : {
+ "retweeted" : {
+ "type" : "boolean"
+ }
+ }
+ },
+ "errors" : {
+ "type" : "array",
+ "items" : {
+ "$ref" : "#/components/schemas/Problem"
+ },
+ "minItems" : 1
+ }
+ }
+ },
+ "TweetCountsResponse" : {
+ "type" : "object",
+ "properties" : {
+ "data" : {
+ "type" : "array",
+ "items" : {
+ "$ref" : "#/components/schemas/SearchCount"
+ },
+ "minItems" : 1
+ },
+ "errors" : {
+ "type" : "array",
+ "items" : {
+ "$ref" : "#/components/schemas/Problem"
+ },
+ "minItems" : 1
+ },
+ "meta" : {
+ "type" : "object",
+ "properties" : {
+ "next_token" : {
+ "description" : "This value is used to get the next 'page' of results by providing it to the next_token parameter.",
+ "type" : "string"
+ },
+ "total_tweet_count" : {
+ "description" : "Sum of search query count results",
+ "type" : "integer",
+ "format" : "int32"
+ }
+ }
+ }
+ }
+ },
+ "SearchCount" : {
+ "type" : "object",
+ "description" : "Represent a Search Count Result",
+ "required" : [ "end", "start", "tweet_count" ],
+ "properties" : {
+ "end" : {
+ "$ref" : "#/components/schemas/End"
+ },
+ "start" : {
+ "$ref" : "#/components/schemas/Start"
+ },
+ "tweet_count" : {
+ "$ref" : "#/components/schemas/TweetCount"
+ }
+ }
+ },
+ "Start" : {
+ "type" : "string",
+ "format" : "date-time",
+ "description" : "The start time of the bucket"
+ },
+ "End" : {
+ "type" : "string",
+ "format" : "date-time",
+ "description" : "The end time of the bucket"
+ },
+ "TweetCount" : {
+ "type" : "integer",
+ "description" : "The count for the bucket"
+ },
+ "Granularity" : {
+ "type" : "string",
+ "enum" : [ "minute", "hour", "day" ],
+ "default" : "hour"
+ }
+ },
+ "responses" : {
+ "HttpErrorResponse" : {
+ "description" : "The request has failed.",
+ "content" : {
+ "application/json" : {
+ "schema" : {
+ "$ref" : "#/components/schemas/Error"
+ }
+ },
+ "application/problem+json" : {
+ "schema" : {
+ "$ref" : "#/components/schemas/Problem"
+ }
+ }
+ }
+ }
+ },
+ "parameters" : {
+ "TweetExpansionsParameter" : {
+ "name" : "expansions",
+ "in" : "query",
+ "style" : "form",
+ "explode" : false,
+ "description" : "A comma separated list of fields to expand.",
+ "schema" : {
+ "description" : "The list of fields you can expand for a [Tweet](#Tweet) object. If the field has an ID, it can be expanded into a full object.",
+ "type" : "array",
+ "items" : {
+ "type" : "string",
+ "enum" : [ "author_id", "referenced_tweets.id", "in_reply_to_user_id", "geo.place_id", "attachments.media_keys", "attachments.poll_ids", "entities.mentions.username", "referenced_tweets.id.author_id" ]
+ },
+ "minItems" : 1,
+ "uniqueItems" : true,
+ "example" : [ "author_id", "referenced_tweets.id", "in_reply_to_user_id", "geo.place_id", "attachments.media_keys", "attachments.poll_ids", "entities.mentions.username", "referenced_tweets.id.author_id" ]
+ }
+ },
+ "UserExpansionsParameter" : {
+ "name" : "expansions",
+ "in" : "query",
+ "style" : "form",
+ "explode" : false,
+ "description" : "A comma separated list of fields to expand.",
+ "schema" : {
+ "description" : "The list of fields you can expand for a [User](#User) object. If the field has an ID, it can be expanded into a full object.",
+ "type" : "array",
+ "items" : {
+ "type" : "string",
+ "enum" : [ "pinned_tweet_id" ]
+ },
+ "minItems" : 1,
+ "uniqueItems" : true,
+ "example" : [ "pinned_tweet_id" ]
+ }
+ },
+ "TweetFieldsParameter" : {
+ "name" : "tweet.fields",
+ "in" : "query",
+ "style" : "form",
+ "explode" : false,
+ "required" : false,
+ "description" : "A comma separated list of Tweet fields to display.",
+ "schema" : {
+ "description" : "The list of fields you can display for a [Tweet](#Tweet) object.",
+ "type" : "array",
+ "items" : {
+ "type" : "string",
+ "enum" : [ "id", "created_at", "text", "author_id", "in_reply_to_user_id", "referenced_tweets", "attachments", "withheld", "geo", "entities", "public_metrics", "possibly_sensitive", "source", "lang", "context_annotations", "non_public_metrics", "promoted_metrics", "organic_metrics", "conversation_id", "reply_settings" ]
+ },
+ "minItems" : 1,
+ "uniqueItems" : true,
+ "example" : [ "created_at", "author_id", "entities", "conversation_id", "reply_settings", "public_metrics" ]
+ }
+ },
+ "UserFieldsParameter" : {
+ "name" : "user.fields",
+ "in" : "query",
+ "style" : "form",
+ "explode" : false,
+ "required" : false,
+ "description" : "A comma separated list of User fields to display.",
+ "schema" : {
+ "description" : "The list of fields you can display for a [User](#User) object.",
+ "type" : "array",
+ "items" : {
+ "type" : "string",
+ "enum" : [ "id", "created_at", "name", "username", "protected", "verified", "withheld", "profile_image_url", "location", "url", "description", "entities", "pinned_tweet_id", "public_metrics" ]
+ },
+ "minItems" : 1,
+ "uniqueItems" : true,
+ "example" : [ "username", "verified", "profile_image_url" ]
+ }
+ },
+ "MediaFieldsParameter" : {
+ "name" : "media.fields",
+ "in" : "query",
+ "style" : "form",
+ "explode" : false,
+ "required" : false,
+ "description" : "A comma separated list of Media fields to display.",
+ "schema" : {
+ "description" : "The list of fields you can display for a Media object.",
+ "type" : "array",
+ "items" : {
+ "type" : "string",
+ "enum" : [ "media_key", "duration_ms", "height", "preview_image_url", "type", "url", "width", "public_metrics", "non_public_metrics", "organic_metrics", "promoted_metrics", "alt_text" ]
+ },
+ "minItems" : 1,
+ "example" : [ "media_key", "duration_ms", "height", "preview_image_url", "type", "url", "width", "public_metrics" ],
+ "uniqueItems" : true
+ }
+ },
+ "PlaceFieldsParameter" : {
+ "name" : "place.fields",
+ "in" : "query",
+ "style" : "form",
+ "explode" : false,
+ "required" : false,
+ "description" : "A comma separated list of Place fields to display.",
+ "schema" : {
+ "description" : "The list of fields you can display for a Place object.",
+ "type" : "array",
+ "items" : {
+ "type" : "string",
+ "enum" : [ "id", "name", "country_code", "place_type", "full_name", "country", "contained_within", "geo" ]
+ },
+ "minItems" : 1,
+ "uniqueItems" : true,
+ "example" : [ "id", "name", "country_code", "place_type", "full_name", "country", "contained_within", "geo" ]
+ }
+ },
+ "PollFieldsParameter" : {
+ "name" : "poll.fields",
+ "in" : "query",
+ "style" : "form",
+ "explode" : false,
+ "required" : false,
+ "description" : "A comma separated list of Poll fields to display.",
+ "schema" : {
+ "description" : "The list of fields you can display for a Poll object.",
+ "type" : "array",
+ "items" : {
+ "type" : "string",
+ "enum" : [ "id", "options", "voting_status", "end_datetime", "duration_minutes" ]
+ },
+ "minItems" : 1,
+ "uniqueItems" : true,
+ "example" : [ "duration_minutes", "options", "voting_status", "end_datetime" ]
+ }
+ },
+ "ListFieldsParameter" : {
+ "name" : "list.fields",
+ "in" : "query",
+ "style" : "form",
+ "explode" : false,
+ "required" : false,
+ "description" : "A comma separated list of List fields to display.",
+ "schema" : {
+ "description" : "The list of fields you can display for a List object.",
+ "type" : "array",
+ "items" : {
+ "type" : "string",
+ "enum" : [ "created_at", "description", "follower_count", "id", "member_count", "name", "owner_id", "visibility" ]
+ },
+ "minItems" : 1,
+ "uniqueItems" : true,
+ "example" : [ "created_at", "description", "follower_count", "member_count", "name", "visibility" ]
+ }
+ },
+ "SinceIdRequestParameter" : {
+ "name" : "since_id",
+ "in" : "query",
+ "description" : "The minimum Tweet ID to be included in the result set. This parameter takes precedence over start_time if both are specified.",
+ "required" : false,
+ "example" : "791775337160081409",
+ "schema" : {
+ "$ref" : "#/components/schemas/TweetID"
+ }
+ },
+ "UntilIdRequestParameter" : {
+ "name" : "until_id",
+ "in" : "query",
+ "description" : "The maximum Tweet ID to be included in the result set. This parameter takes precedence over end_time if both are specified.",
+ "required" : false,
+ "example" : "1346889436626259968",
+ "schema" : {
+ "$ref" : "#/components/schemas/TweetID"
+ }
+ },
+ "PaginationTokenRequestParameter" : {
+ "name" : "pagination_token",
+ "in" : "query",
+ "description" : "This parameter is used to get the next 'page' of results.",
+ "required" : false,
+ "schema" : {
+ "type" : "string",
+ "minLength" : 1
+ }
+ },
+ "MaxResultsRequestParameter" : {
+ "name" : "max_results",
+ "in" : "query",
+ "description" : "The maximum number of results",
+ "required" : false,
+ "schema" : {
+ "type" : "integer",
+ "format" : "int32",
+ "minimum" : 5,
+ "maximum" : 100
+ }
+ },
+ "StartTimeRequestParameter" : {
+ "name" : "start_time",
+ "in" : "query",
+ "description" : "YYYY-MM-DDTHH:mm:ssZ. The earliest UTC timestamp from which the Tweets will be provided. The since_id parameter takes precedence if it is also specified.",
+ "required" : false,
+ "example" : "2021-02-01T18:40:40.000Z",
+ "schema" : {
+ "type" : "string",
+ "format" : "date-time"
+ }
+ },
+ "EndTimeRequestParameter" : {
+ "name" : "end_time",
+ "in" : "query",
+ "description" : "YYYY-MM-DDTHH:mm:ssZ. The latest UTC timestamp to which the Tweets will be provided. The until_id parameter takes precedence if it is also specified.",
+ "required" : false,
+ "example" : "2021-02-14T18:40:40.000Z",
+ "schema" : {
+ "type" : "string",
+ "format" : "date-time"
+ }
+ },
+ "TweetTypeExcludesRequestParameter" : {
+ "name" : "exclude",
+ "in" : "query",
+ "style" : "form",
+ "explode" : false,
+ "description" : "The set of entities to exclude (e.g. 'replies' or 'retweets')",
+ "required" : false,
+ "schema" : {
+ "description" : "The set of entities to exclude (e.g. 'replies' or 'retweets')",
+ "type" : "array",
+ "items" : {
+ "type" : "string",
+ "enum" : [ "replies", "retweets" ]
+ },
+ "minItems" : 1,
+ "uniqueItems" : true,
+ "example" : [ "replies", "retweets" ]
+ }
+ },
+ "BackfillMinutesRequestParameter" : {
+ "name" : "backfill_minutes",
+ "in" : "query",
+ "description" : "The number of minutes of backfill requested",
+ "required" : false,
+ "schema" : {
+ "type" : "integer",
+ "format" : "int32",
+ "minimum" : 0,
+ "maximum" : 5
+ }
+ }
+ }
+ },
+ "externalDocs" : {
+ "description" : "Find out more about Swagger",
+ "url" : "http://swagger.io"
+ }
+}
\ No newline at end of file
diff --git a/src/client/reducers/business.js b/src/client/reducers/business.js
index e1c2ebd01..db61779d4 100644
--- a/src/client/reducers/business.js
+++ b/src/client/reducers/business.js
@@ -1,431 +1,569 @@
-import format from "date-fns/format";
-import * as types from "../actions/actionTypes";
-
-const initialState = {
- currentTab: "First Tab",
- reqResArray: [],
- scheduledReqResArray: [],
- history: [],
- collections: [],
- warningMessage: {},
- newRequestFields: {
- protocol: "",
- restUrl: "http://",
- wsUrl: "ws://",
- gqlUrl: "https://",
- grpcUrl: "",
- url: "http://",
- method: "GET",
- graphQL: false,
- gRPC: false,
- network: "rest",
- testContent: "",
- testResults: [],
- },
- newRequestHeaders: {
- headersArr: [],
- count: 0,
- },
- newRequestStreams: {
- streamsArr: [],
- count: 0,
- streamContent: [],
- selectedPackage: null,
- selectedRequest: null,
- selectedService: null,
- selectedServiceObj: null,
- selectedStreamingType: null,
- initialQuery: null,
- queryArr: null,
- protoPath: null,
- services: null,
- protoContent: "",
- },
- newRequestCookies: {
- cookiesArr: [],
- count: 0,
- },
- newRequestBody: {
- bodyContent: "",
- bodyVariables: "",
- bodyType: "raw",
- rawType: "text/plain",
- JSONFormatted: true,
- bodyIsNew: false,
- },
- newRequestSSE: {
- isSSE: false,
- },
- introspectionData: { schemaSDL: null, clientSchema: null },
- dataPoints: {},
- currentResponse: {
- request: {
- network: "",
- },
- },
-};
-
-const businessReducer = (state = initialState, action) => {
- switch (action.type) {
- case types.GET_HISTORY: {
- return {
- ...state,
- history: action.payload,
- };
- }
-
- case types.DELETE_HISTORY: {
- const deleteId = action.payload.id;
- const deleteDate = format(action.payload.created_at, "MM/DD/YYYY");
- const newHistory = JSON.parse(JSON.stringify(state.history));
- newHistory.forEach((obj, i) => {
- if (obj.date === deleteDate)
- obj.history = obj.history.filter((hist) => hist.id !== deleteId);
- if (obj.history.length === 0) {
- newHistory.splice(i, 1);
- }
- });
- return {
- ...state,
- history: newHistory,
- };
- }
-
- case types.CLEAR_HISTORY: {
- return {
- ...state,
- history: [],
- };
- }
-
- case types.GET_COLLECTIONS: {
- return {
- ...state,
- collections: action.payload,
- };
- }
-
- case types.DELETE_COLLECTION: {
- const deleteId = action.payload.id;
- const newCollections = JSON.parse(JSON.stringify(state.collections));
- newCollections.forEach((obj, i) => {
- if (obj.id === deleteId) {
- newCollections.splice(i, 1);
- }
- });
- return {
- ...state,
- collections: newCollections,
- };
- }
-
- case types.RESET_COMPOSER_FIELDS: {
- return {
- ...state,
- newRequestHeaders: {
- headersArr: [],
- count: 0,
- },
- newRequestCookies: {
- cookiesArr: [],
- count: 0,
- },
- newRequestBody: {
- ...state.newRequestBody,
- bodyContent: "",
- bodyVariables: "",
- bodyType: "raw",
- rawType: "text/plain",
- JSONFormatted: true,
- },
- newRequestFields: {
- ...state.newRequestFields,
- protocol: "",
- },
- newRequestSSE: {
- isSSE: false,
- },
- warningMessage: {},
- };
- }
-
- case types.COLLECTION_TO_REQRES: {
- const reqResArray = JSON.parse(JSON.stringify(action.payload));
- return {
- ...state,
- reqResArray,
- };
- }
-
- case types.COLLECTION_ADD: {
- //add to collection to array in state
- return {
- ...state,
- collections: [action.payload, ...state.collections],
- };
- }
-
- case types.COLLECTION_UPDATE: {
- //update collection from state
- const collectionName = action.payload.name;
- const newCollections = JSON.parse(JSON.stringify(state.collections));
- newCollections.forEach((obj, i) => {
- if (obj.name === collectionName) {
- newCollections[i] = action.payload;
- }
- });
-
- return {
- ...state,
- collections: newCollections,
- };
- }
-
- case types.REQRES_CLEAR: {
- return {
- ...state,
- reqResArray: [],
- currentResponse: {
- request: {
- network: "",
- },
- },
- };
- }
-
- case types.REQRES_ADD: {
- const reqResArray = JSON.parse(JSON.stringify(state.reqResArray));
- reqResArray.push(action.payload);
- const addDate = format(action.payload.created_at, "MM/DD/YYYY");
- const newHistory = JSON.parse(JSON.stringify(state.history));
- let updated = false;
- //if there is history for added date, add query to beginning of history
- newHistory.forEach((obj) => {
- if (obj.date === addDate) {
- obj.history.unshift(action.payload);
- updated = true;
- }
- });
- //if there is not history at added date, create new history with new query
- if (!updated) {
- newHistory.unshift({
- date: addDate,
- history: [action.payload],
- });
- }
- return {
- ...state,
- reqResArray,
- history: newHistory,
- };
- }
-
- case types.REQRES_DELETE: {
- const deleteId = action.payload.id;
- const newReqResArray = state.reqResArray.filter(
- (reqRes) => reqRes.id !== deleteId
- );
- return {
- ...state,
- reqResArray: newReqResArray,
- };
- }
-
- case types.SET_CHECKS_AND_MINIS: {
- return {
- ...state,
- reqResArray: JSON.parse(JSON.stringify(action.payload)),
- };
- }
-
- case types.REQRES_UPDATE: {
- const reqResDeepCopy = JSON.parse(JSON.stringify(state.reqResArray));
- let indexToBeUpdated;
- reqResDeepCopy.forEach((reqRes, index) => {
- if (reqRes.id === action.payload.id) indexToBeUpdated = index;
- });
- if (indexToBeUpdated !== undefined) {
- action.payload.checked = state.reqResArray[indexToBeUpdated].checked;
- action.payload.minimized =
- state.reqResArray[indexToBeUpdated].minimized;
- reqResDeepCopy.splice(
- indexToBeUpdated,
- 1,
- JSON.parse(JSON.stringify(action.payload))
- ); //FOR SOME REASON THIS IS NECESSARY, MESSES UP CHECKS OTHERWISE
- }
-
- return {
- ...state,
- reqResArray: reqResDeepCopy,
- };
- }
-
- case types.SCHEDULED_REQRES_UPDATE: {
- const scheduledReqResArray = JSON.parse(
- JSON.stringify(state.scheduledReqResArray)
- );
- scheduledReqResArray.push(action.payload);
- return {
- ...state,
- scheduledReqResArray,
- };
- }
-
- case types.SCHEDULED_REQRES_DELETE: {
- const scheduledReqResArray = [];
- return {
- ...state,
- scheduledReqResArray,
- };
- }
-
- case types.UPDATE_GRAPH: {
- const { id } = action.payload;
- // action.payload is the latest reqRes object
-
- //dataPoints to be used by graph
- const dataPointsCopy = JSON.parse(JSON.stringify(state.dataPoints));
- dataPointsCopy.current = id;
- //if more than 8 points, data will shift down an index
- if (!dataPointsCopy[id]) {
- dataPointsCopy[id] = [];
- } else if (dataPointsCopy[id].length > 49) {
- dataPointsCopy[id] = dataPointsCopy[id].slice(1);
- }
-
- //check if new object is a closed request with timeSent and timeReceived
- if (
- !dataPointsCopy[id].some(
- (elem) => elem.timeSent === action.payload.timeSent
- )
- ) {
- // if a color hasn't been added to this specific request id, add a new one
- const color = !dataPointsCopy[id][0]?.color
- ? `${Math.random() * 256}, ${Math.random() * 256}, ${
- Math.random() * 256
- }`
- : dataPointsCopy[id][0].color;
-
- //add dataPoint to array connected to its id -and return to state
- dataPointsCopy[id].push({
- reqRes: action.payload,
- url: action.payload.url,
- timeSent: action.payload.timeSent,
- timeReceived: action.payload.timeReceived,
- created_at: action.payload.created_at,
- color,
- });
- return {
- ...state,
- dataPoints: dataPointsCopy,
- };
- }
- return {
- ...state,
- dataPoints: dataPointsCopy,
- };
- }
-
- case types.CLEAR_GRAPH: {
- const dataPointsCopy = JSON.parse(JSON.stringify(state.dataPoints));
- dataPointsCopy[action.payload] = [];
- return {
- ...state,
- dataPoints: dataPointsCopy,
- };
- }
-
- case types.CLEAR_ALL_GRAPH: {
- return {
- ...state,
- dataPoints: {},
- };
- }
-
- case types.SET_COMPOSER_WARNING_MESSAGE: {
- return {
- ...state,
- warningMessage: action.payload,
- };
- }
-
- case types.SET_NEW_REQUEST_FIELDS: {
- return {
- ...state,
- newRequestFields: action.payload,
- };
- }
-
- case types.SET_NEW_REQUEST_HEADERS: {
- return {
- ...state,
- newRequestHeaders: action.payload,
- };
- }
-
- case types.SET_NEW_REQUEST_STREAMS: {
- return {
- ...state,
- newRequestStreams: action.payload,
- };
- }
-
- case types.SET_NEW_REQUEST_BODY: {
- return {
- ...state,
- newRequestBody: action.payload,
- };
- }
-
- case types.SET_NEW_TEST_CONTENT: {
- return {
- ...state,
- newRequestFields: {
- ...state.newRequestFields,
- testContent: action.payload,
- },
- };
- }
-
- case types.SET_NEW_REQUEST_COOKIES: {
- return {
- ...state,
- newRequestCookies: action.payload,
- };
- }
-
- case types.SET_NEW_REQUEST_SSE: {
- return {
- ...state,
- newRequestSSE: { isSSE: action.payload },
- };
- }
-
- case types.SET_CURRENT_TAB: {
- return {
- ...state,
- currentTab: action.payload,
- };
- }
-
- case types.SET_INTROSPECTION_DATA: {
- return {
- ...state,
- introspectionData: action.payload,
- };
- }
-
- case types.SAVE_CURRENT_RESPONSE_DATA: {
- return {
- ...state,
- currentResponse: action.payload,
- };
- }
-
- default:
- return state;
- }
-};
-
-export default businessReducer;
+import format from 'date-fns/format';
+import * as types from '../actions/actionTypes';
+
+const initialState = {
+ currentTab: 'First Tab',
+ reqResArray: [],
+ scheduledReqResArray: [],
+ history: [],
+ collections: [],
+ warningMessage: {},
+ newRequestsOpenAPI: {
+ openapiMetadata: {
+ info: {},
+ tags: [],
+ serverUrls: [],
+ },
+ openapiReqArray: [],
+ },
+ newRequestFields: {
+ protocol: '',
+ restUrl: 'http://',
+ wsUrl: 'ws://',
+ gqlUrl: 'https://',
+ grpcUrl: '',
+ webrtcUrl: '',
+ url: 'http://',
+ method: 'GET',
+ graphQL: false,
+ gRPC: false,
+ ws: false,
+ openapi: false,
+ webrtc: false,
+ network: 'rest',
+ testContent: '',
+ testResults: [],
+ openapiReqObj: {},
+ },
+ newRequestHeaders: {
+ headersArr: [],
+ count: 0,
+ },
+ newRequestStreams: {
+ streamsArr: [],
+ count: 0,
+ streamContent: [],
+ selectedPackage: null,
+ selectedRequest: null,
+ selectedService: null,
+ selectedServiceObj: null,
+ selectedStreamingType: null,
+ initialQuery: null,
+ queryArr: null,
+ protoPath: null,
+ services: null,
+ protoContent: '',
+ },
+ newRequestCookies: {
+ cookiesArr: [],
+ count: 0,
+ },
+ newRequestBody: {
+ bodyContent: '',
+ bodyVariables: '',
+ bodyType: 'raw',
+ rawType: 'text/plain',
+ JSONFormatted: true,
+ bodyIsNew: false,
+ },
+ newRequestSSE: {
+ isSSE: false,
+ },
+ newRequestOpenAPIObject: {
+ request: {
+ id: 0,
+ enabled: true,
+ reqTags: [],
+ reqServers: [],
+ summary: '',
+ description: '',
+ operationId: '',
+ method: '',
+ endpoint: '',
+ headers: {},
+ parameters: [],
+ body: new Map(),
+ urls: [],
+ },
+ },
+ introspectionData: { schemaSDL: null, clientSchema: null },
+ dataPoints: {},
+ currentResponse: {
+ request: {
+ network: '',
+ },
+ },
+};
+
+const businessReducer = (state = initialState, action) => {
+ switch (action.type) {
+ case types.GET_HISTORY: {
+ return {
+ ...state,
+ history: action.payload,
+ };
+ }
+
+ case types.DELETE_HISTORY: {
+ const deleteId = action.payload.id;
+ const deleteDate = format(action.payload.created_at, 'MM/DD/YYYY');
+ const newHistory = JSON.parse(JSON.stringify(state.history));
+ newHistory.forEach((obj, i) => {
+ if (obj.date === deleteDate)
+ obj.history = obj.history.filter((hist) => hist.id !== deleteId);
+ if (obj.history.length === 0) {
+ newHistory.splice(i, 1);
+ }
+ });
+ return {
+ ...state,
+ history: newHistory,
+ };
+ }
+
+ case types.CLEAR_HISTORY: {
+ return {
+ ...state,
+ history: [],
+ };
+ }
+
+ case types.GET_COLLECTIONS: {
+ return {
+ ...state,
+ collections: action.payload,
+ };
+ }
+
+ case types.DELETE_COLLECTION: {
+ const deleteId = action.payload.id;
+ const newCollections = JSON.parse(JSON.stringify(state.collections));
+ newCollections.forEach((obj, i) => {
+ if (obj.id === deleteId) {
+ newCollections.splice(i, 1);
+ }
+ });
+ return {
+ ...state,
+ collections: newCollections,
+ };
+ }
+
+ case types.RESET_COMPOSER_FIELDS: {
+ return {
+ ...state,
+ newRequestHeaders: {
+ headersArr: [],
+ count: 0,
+ },
+ newRequestCookies: {
+ cookiesArr: [],
+ count: 0,
+ },
+ newRequestBody: {
+ ...state.newRequestBody,
+ bodyContent: '',
+ bodyVariables: '',
+ bodyType: 'raw',
+ rawType: 'text/plain',
+ JSONFormatted: true,
+ },
+ newRequestFields: {
+ ...state.newRequestFields,
+ protocol: '',
+ },
+ newRequestSSE: {
+ isSSE: false,
+ },
+ warningMessage: {},
+ };
+ }
+
+ case types.COLLECTION_TO_REQRES: {
+ const reqResArray = JSON.parse(JSON.stringify(action.payload));
+ return {
+ ...state,
+ reqResArray,
+ };
+ }
+
+ case types.COLLECTION_ADD: {
+ // add to collection to array in state
+ return {
+ ...state,
+ collections: [action.payload, ...state.collections],
+ };
+ }
+
+ case types.COLLECTION_UPDATE: {
+ // update collection from state
+ const collectionName = action.payload.name;
+ const newCollections = JSON.parse(JSON.stringify(state.collections));
+ newCollections.forEach((obj, i) => {
+ if (obj.name === collectionName) {
+ newCollections[i] = action.payload;
+ }
+ });
+
+ return {
+ ...state,
+ collections: newCollections,
+ };
+ }
+
+ case types.REQRES_CLEAR: {
+ return {
+ ...state,
+ reqResArray: [],
+ currentResponse: {
+ request: {
+ network: '',
+ },
+ },
+ };
+ }
+
+ case types.REQRES_ADD: {
+ const reqResArray = JSON.parse(JSON.stringify(state.reqResArray));
+ reqResArray.push(action.payload);
+ const addDate = format(action.payload.created_at, 'MM/DD/YYYY');
+ const newHistory = JSON.parse(JSON.stringify(state.history));
+ let updated = false;
+ // if there is history for added date, add query to beginning of history
+ newHistory.forEach((obj) => {
+ if (obj.date === addDate) {
+ obj.history.unshift(action.payload);
+ updated = true;
+ }
+ });
+ // if there is not history at added date, create new history with new query
+ if (!updated) {
+ newHistory.unshift({
+ date: addDate,
+ history: [action.payload],
+ });
+ }
+ return {
+ ...state,
+ reqResArray,
+ history: newHistory,
+ };
+ }
+
+ case types.REQRES_DELETE: {
+ const deleteId = action.payload.id;
+ const newReqResArray = state.reqResArray.filter(
+ (reqRes) => reqRes.id !== deleteId
+ );
+ return {
+ ...state,
+ reqResArray: newReqResArray,
+ };
+ }
+
+ case types.SET_CHECKS_AND_MINIS: {
+ return {
+ ...state,
+ reqResArray: JSON.parse(JSON.stringify(action.payload)),
+ };
+ }
+
+ case types.REQRES_UPDATE: {
+ const reqResDeepCopy = JSON.parse(JSON.stringify(state.reqResArray));
+ let indexToBeUpdated;
+ reqResDeepCopy.forEach((reqRes, index) => {
+ if (reqRes.id === action.payload.id) indexToBeUpdated = index;
+ });
+ if (indexToBeUpdated !== undefined) {
+ action.payload.checked = state.reqResArray[indexToBeUpdated].checked;
+ action.payload.minimized =
+ state.reqResArray[indexToBeUpdated].minimized;
+ reqResDeepCopy.splice(
+ indexToBeUpdated,
+ 1,
+ JSON.parse(JSON.stringify(action.payload))
+ ); // FOR SOME REASON THIS IS NECESSARY, MESSES UP CHECKS OTHERWISE
+ }
+
+ return {
+ ...state,
+ reqResArray: reqResDeepCopy,
+ };
+ }
+
+ case types.SCHEDULED_REQRES_UPDATE: {
+ const scheduledReqResArray = JSON.parse(
+ JSON.stringify(state.scheduledReqResArray)
+ );
+ scheduledReqResArray.push(action.payload);
+ return {
+ ...state,
+ scheduledReqResArray,
+ };
+ }
+
+ case types.SCHEDULED_REQRES_DELETE: {
+ const scheduledReqResArray = [];
+ return {
+ ...state,
+ scheduledReqResArray,
+ };
+ }
+
+ case types.UPDATE_GRAPH: {
+ const { id } = action.payload;
+ // action.payload is the latest reqRes object
+
+ // dataPoints to be used by graph
+ const dataPointsCopy = JSON.parse(JSON.stringify(state.dataPoints));
+ dataPointsCopy.current = id;
+ // if more than 8 points, data will shift down an index
+ if (!dataPointsCopy[id]) {
+ dataPointsCopy[id] = [];
+ } else if (dataPointsCopy[id].length > 49) {
+ dataPointsCopy[id] = dataPointsCopy[id].slice(1);
+ }
+
+ // check if new object is a closed request with timeSent and timeReceived
+ if (
+ !dataPointsCopy[id].some(
+ (elem) => elem.timeSent === action.payload.timeSent
+ )
+ ) {
+ // if a color hasn't been added to this specific request id, add a new one
+ const color = !dataPointsCopy[id][0]?.color
+ ? `${Math.random() * 256}, ${Math.random() * 256}, ${
+ Math.random() * 256
+ }`
+ : dataPointsCopy[id][0].color;
+
+ // add dataPoint to array connected to its id -and return to state
+ dataPointsCopy[id].push({
+ reqRes: action.payload,
+ url: action.payload.url,
+ timeSent: action.payload.timeSent,
+ timeReceived: action.payload.timeReceived,
+ created_at: action.payload.created_at,
+ color,
+ });
+ return {
+ ...state,
+ dataPoints: dataPointsCopy,
+ };
+ }
+ return {
+ ...state,
+ dataPoints: dataPointsCopy,
+ };
+ }
+
+ case types.CLEAR_GRAPH: {
+ const dataPointsCopy = JSON.parse(JSON.stringify(state.dataPoints));
+ dataPointsCopy[action.payload] = [];
+ return {
+ ...state,
+ dataPoints: dataPointsCopy,
+ };
+ }
+
+ case types.CLEAR_ALL_GRAPH: {
+ return {
+ ...state,
+ dataPoints: {},
+ };
+ }
+
+ case types.SET_COMPOSER_WARNING_MESSAGE: {
+ return {
+ ...state,
+ warningMessage: action.payload,
+ };
+ }
+
+ case types.SET_NEW_REQUEST_FIELDS: {
+ return {
+ ...state,
+ newRequestFields: action.payload,
+ };
+ }
+
+ case types.SET_NEW_REQUEST_HEADERS: {
+ return {
+ ...state,
+ newRequestHeaders: action.payload,
+ };
+ }
+
+ case types.SET_NEW_REQUEST_STREAMS: {
+ return {
+ ...state,
+ newRequestStreams: action.payload,
+ };
+ }
+
+ case types.SET_NEW_REQUEST_BODY: {
+ return {
+ ...state,
+ newRequestBody: action.payload,
+ };
+ }
+
+ case types.SET_NEW_TEST_CONTENT: {
+ return {
+ ...state,
+ newRequestFields: {
+ ...state.newRequestFields,
+ testContent: action.payload,
+ },
+ };
+ }
+
+ case types.SET_NEW_REQUEST_COOKIES: {
+ return {
+ ...state,
+ newRequestCookies: action.payload,
+ };
+ }
+
+ case types.SET_NEW_REQUEST_SSE: {
+ return {
+ ...state,
+ newRequestSSE: { isSSE: action.payload },
+ };
+ }
+
+ case types.SET_CURRENT_TAB: {
+ return {
+ ...state,
+ currentTab: action.payload,
+ };
+ }
+
+ case types.SET_INTROSPECTION_DATA: {
+ return {
+ ...state,
+ introspectionData: action.payload,
+ };
+ }
+
+ case types.SAVE_CURRENT_RESPONSE_DATA: {
+ return {
+ ...state,
+ currentResponse: action.payload,
+ };
+ }
+
+ // OPENAPI
+
+ case types.SET_NEW_REQUESTS_OPENAPI: {
+ return {
+ ...state,
+ newRequestsOpenAPI: { ...action.payload },
+ };
+ }
+
+ case types.SET_OPENAPI_SERVERS_GLOBAL: {
+ const openapiMetadata = { ...state.openapiMetadata };
+ openapiMetadata.serverUrls = [...state.openapiMetadata.serverUrls].filter(
+ (_, i) => action.payload.includes(i)
+ );
+ return {
+ ...state,
+ newRequestsOpenAPI: openapiMetadata,
+ };
+ }
+
+ case types.SET_NEW_OPENAPI_SERVERS: {
+ const { id, serverIds } = action.payload;
+ const request = [...state.openapiReqArray]
+ .filter(({ request }) => request.id === id)
+ .pop();
+ request.reqServers = [...state.openapiMetadata.serverUrls].filter(
+ (_, i) => serverIds.includes(i)
+ );
+ const openapiReqArray = [...state.openapiReqArray].push({ request });
+ return {
+ ...state,
+ newRequestsOpenAPI: openapiReqArray,
+ };
+ }
+
+ case types.SET_NEW_OPENAPI_PARAMETER: {
+ const { id, location, name, value } = action.payload;
+ const request = [...state.openapiReqArray]
+ .filter(({ request }) => request.id === id)
+ .pop();
+ const urls = [...request.reqServers].map(
+ (url) => (url += request.endpoint)
+ );
+ switch (location) {
+ case 'path': {
+ urls.map((url) => url.replace(`{${name}}`, value));
+ request.urls = urls;
+ const openapiReqArray = [...state.openapiReqArray].push({ request });
+ return {
+ ...state,
+ newRequestsOpenAPI: openapiReqArray,
+ };
+ }
+ case 'query': {
+ urls.map((url) => {
+ if (url.slice(-1) !== '?') url += '?';
+ url += `${name}=${value}&`;
+ });
+ request.urls = urls;
+ const openapiReqArray = [...state.openapiReqArray].push({ request });
+ return {
+ ...state,
+ newRequestsOpenAPI: openapiReqArray,
+ };
+ }
+ case 'header': {
+ if (['Content-Type', 'Authorization', 'Accepts'].includes(key)) break;
+ request.headers.push({ name: value });
+ const openapiReqArray = [...state.openapiReqArray].push({ request });
+ return {
+ ...state,
+ newRequestsOpenAPI: openapiReqArray,
+ };
+ }
+ case 'cookie': {
+ request.cookies = value;
+ const openapiReqArray = [...state.openapiReqArray].push({ request });
+ return {
+ ...state,
+ newRequestsOpenAPI: openapiReqArray,
+ };
+ }
+ default: {
+ return state;
+ }
+ }
+ break;
+ }
+ case types.SET_NEW_OPENAPI_REQUEST_BODY: {
+ const { id, mediaType, requestBody } = action.payload;
+ const request = [...state.openapiReqArray]
+ .filter(({ request }) => request.id === id)
+ .pop();
+ const { method } = request;
+ if (
+ !['get', 'delete', 'head'].includes(method) &&
+ requestBody !== undefined
+ ) {
+ request.body = requestBody;
+ request.rawType = mediaType;
+ }
+ const openapiReqArray = [...state.openapiReqArray].push({ request });
+ return {
+ ...state,
+ newRequestsOpenAPI: openapiReqArray,
+ };
+ }
+
+ default:
+ return state;
+ }
+};
+
+export default businessReducer;
diff --git a/src/client/reducers/index.js b/src/client/reducers/index.js
index ed6fe579c..cd6420927 100644
--- a/src/client/reducers/index.js
+++ b/src/client/reducers/index.js
@@ -1,14 +1,14 @@
-import { combineReducers } from "redux";
-
-// import all reducers here
-import businessReducer from "./business";
-import uiReducer from "./ui";
-
-// combine reducers
-const reducers = combineReducers({
- business: businessReducer,
- ui: uiReducer,
-});
-
-// make the combined reducers available for import
-export default reducers;
+import { combineReducers } from 'redux';
+
+// import all reducers here
+import businessReducer from './business';
+import uiReducer from './ui';
+
+// combine reducers
+const reducers = combineReducers({
+ business: businessReducer,
+ ui: uiReducer,
+});
+
+// make the combined reducers available for import
+export default reducers;
diff --git a/src/client/reducers/ui.js b/src/client/reducers/ui.js
index d6556d445..d4d3ac842 100644
--- a/src/client/reducers/ui.js
+++ b/src/client/reducers/ui.js
@@ -1,44 +1,44 @@
-import * as types from "../actions/actionTypes";
-
-const initialState = {
- composerDisplay: 'Request',
- sidebarActiveTab: 'composer',
- workspaceActiveTab: 'workspace',
- responsePaneActiveTab: 'events',
-};
-
-const uiReducer = (state = initialState, action) => {
- switch (action.type) {
- case types.SET_COMPOSER_DISPLAY: {
- return {
- ...state,
- composerDisplay: action.payload,
- };
- }
-
- case types.SET_SIDEBAR_ACTIVE_TAB : {
- return {
- ...state,
- sidebarActiveTab: action.payload,
- };
- }
- case types.SET_WORKSPACE_ACTIVE_TAB : {
- return {
- ...state,
- workspaceActiveTab: action.payload,
- };
- }
-
- case types.SET_RESPONSE_PANE_ACTIVE_TAB : {
- return {
- ...state,
- responsePaneActiveTab: action.payload,
- };
- }
-
- default:
- return state;
- }
-};
-
-export default uiReducer;
+import * as types from '../actions/actionTypes';
+
+const initialState = {
+ composerDisplay: 'Request',
+ sidebarActiveTab: 'composer',
+ workspaceActiveTab: 'workspace',
+ responsePaneActiveTab: 'events',
+};
+
+const uiReducer = (state = initialState, action) => {
+ switch (action.type) {
+ case types.SET_COMPOSER_DISPLAY: {
+ return {
+ ...state,
+ composerDisplay: action.payload,
+ };
+ }
+
+ case types.SET_SIDEBAR_ACTIVE_TAB: {
+ return {
+ ...state,
+ sidebarActiveTab: action.payload,
+ };
+ }
+ case types.SET_WORKSPACE_ACTIVE_TAB: {
+ return {
+ ...state,
+ workspaceActiveTab: action.payload,
+ };
+ }
+
+ case types.SET_RESPONSE_PANE_ACTIVE_TAB: {
+ return {
+ ...state,
+ responsePaneActiveTab: action.payload,
+ };
+ }
+
+ default:
+ return state;
+ }
+};
+
+export default uiReducer;
diff --git a/src/client/store.js b/src/client/store.js
index 091e3af4e..15f968a68 100644
--- a/src/client/store.js
+++ b/src/client/store.js
@@ -1,19 +1,8 @@
-/**
- * ************************************
- *
- * @module store.js
- * @author
- * @date
- * @description Redux 'single source of truth'
- *
- * ************************************
- */
-
-import { createStore } from "redux";
-import { composeWithDevTools } from "redux-devtools-extension";
-import reducers from "./reducers/index";
-
-// we are adding composeWithDevTools here to get easy access to the Redux dev tools
-const store = createStore(reducers, composeWithDevTools());
-
-export default store;
+import { createStore } from 'redux';
+import { composeWithDevTools } from 'redux-devtools-extension';
+import reducers from './reducers/index';
+
+// we are adding composeWithDevTools here to get easy access to the Redux dev tools
+const store = createStore(reducers, composeWithDevTools());
+
+export default store;
diff --git a/src/index.js b/src/index.js
index a8046b9dd..086e8de76 100644
--- a/src/index.js
+++ b/src/index.js
@@ -1,20 +1,20 @@
-import React from "react";
-import { render } from "react-dom";
-import { Provider } from "react-redux";
-import { App } from "./client/components/containers/App";
-import store from "./client/store";
-
-// Since we are using HtmlWebpackPlugin WITHOUT a template,
-// we should create our own root node in the body element before rendering into it
-const root = document.createElement("div");
-
-root.id = "root";
-document.body.appendChild(root);
-
-render(
- // wrap the App in the Provider and pass in the store
-
-
- ,
- document.getElementById("root")
-);
+import React from 'react';
+import { render } from 'react-dom';
+import { Provider } from 'react-redux';
+import App from './client/components/containers/App';
+import store from './client/store';
+
+// Since we are using HtmlWebpackPlugin WITHOUT a template,
+// we should create our own root node in the body element before rendering into it
+const root = document.createElement('div');
+
+root.id = 'root';
+document.body.appendChild(root);
+
+render(
+ // wrap the App in the Provider and pass in the store
+
+
+ ,
+ document.getElementById('root')
+);
diff --git a/src/types.ts b/src/types.ts
index 59d6b8ea6..8570440d3 100644
--- a/src/types.ts
+++ b/src/types.ts
@@ -1,49 +1,102 @@
-import { string } from "prop-types"
+export type Protocol = 'http://' | 'ws://';
+export type Network = 'rest' | 'ws' | 'webrtc' | 'graphQL' | 'grpc' | 'openapi';
+export type ConnectionStatus = 'uninitialized' | 'error' | 'open' | 'closed';
+export type Methods = 'GET'|'PUT'|'PATCH'|'DELETE'|'OPTIONS'|'HEAD'|'TRACE'|'QUERY'|'SUBSCRIPTION'|'INTROSPECTION'|'INITIATOR'|'RECEIVER';
export interface initialState {
currentTab: string;
- reqResArray: object[];
- history: object[];
- collections: object[];
- warningMessage: string;
+ reqResArray: NewRequestResponseObject[];
+ scheduledReqResArray: NewRequestResponseObject[];
+ history: Record[];
+ collections: Record[];
+ warningMessage: Record;
+ newRequestOpenAPI: NewRequestOpenAPI;
newRequestFields: NewRequestFields;
- newRequestHeaders: NewRequestHeaders;
- newRequestStreams: NewRequestStreams;
- newRequestCookies: NewRequestCookies;
- newRequestBody: NewRequestBody;
+ newRequestHeaders?: NewRequestHeaders;
+ newRequestStreams?: NewRequestStreams;
+ newRequestCookies?: NewRequestCookies;
+ newRequestBody?: NewRequestBody;
newRequestSSE: NewRequestSSE;
}
-export interface NewRequestFields {
- protocol: string;
+export interface NewRequestOpenAPI {
+ openapiMetadata: Record;
+ openapiReqArray: NewRequestOpenAPIObject[];
+}
+
+export interface NewRequestOpenAPIObject {
+ id: number;
+ enabled: boolean;
+ summary?: string;
+ description?: string;
+ operationId?: string;
+ reqTags: string;
+ reqServers: string[];
+ method: Methods;
+ endpoint: string;
+ headers?: NewRequestHeaders;
+ parameters?: Record[];
+ urls: string[];
+ body?: Map;
+ params?: Record;
+ queries?: Record;
+}
+export interface NewRequestResponseObject {
+ id: number;
+ graphQL: boolean;
+ closeCode: number;
+ protocol: Protocol;
+ request: NewRequestFields;
+ response: Record;
+ connection: ConnectionStatus;
+ connectionType: string;
+ isHTTP2: boolean;
url: string;
- method: string;
+ timeReceived: Date;
+ timeSent: Date;
+ rpc: string;
+ service: string;
+}
+export interface NewRequestFields {
+ protocol: Protocol;
graphQL: boolean;
gRPC: boolean;
+ ws: boolean;
+ webrtc: boolean;
+ restUrl?: string;
+ wsUrl?: string;
+ gqlUrl?: string;
+ grpcUrl?: string;
+ webrtcUrl?: string;
+ url?: string;
+ method?: string;
+ network: Network;
+ testContent: string;
+ testResults: string[];
}
export interface NewRequestHeaders {
- headersArr: object[];
+ headersArr: Record[];
count: number;
}
export interface NewRequestStreams {
- streamsArr: object[];
+ streamsArr: Record[];
count: number;
- streamContent: object[];
+ streamContent: Record[];
selectedPackage: string | null;
selectedRequest: string | null;
selectedService: string | null;
selectedStreamingType: string | null;
- initialQuery: any | null;
- queryArr: object[] | null;
- protoPath: any | null;
- services: object | null;
+ initialQuery: unknown | null;
+ queryArr: Record[] | null;
+ protoPath: unknown | null;
+ services: Record | null;
protoContent: string;
}
export interface NewRequestCookies {
- cookiesArr: object[];
+ cookiesArr: Record[];
count: number;
}
@@ -53,6 +106,7 @@ export interface NewRequestBody {
bodyType: string;
rawType: string;
JSONFormatted: boolean;
+ bodyIsNew: boolean;
};
export interface NewRequestSSE {
@@ -65,7 +119,7 @@ export interface Message {
data: string;
}
export interface WebSocketWindowProps {
- content: object[];
+ content: Record[];
outgoingMessages: Array;
incomingMessages: Array;
connection: string;
diff --git a/test/HTTP2_server.js b/test/HTTP2_server.js
index 795eb19c2..0e393bd72 100644
--- a/test/HTTP2_server.js
+++ b/test/HTTP2_server.js
@@ -1,76 +1,76 @@
-const http2 = require("http2");
-const fs = require("fs");
-const path = require("path");
-
-const CERT_PATH = path.join(__dirname, "/HTTP2_cert.pem");
-const PRIV_PATH = path.join(__dirname, "HTTP2_private.pem");
-
-const server = http2.createSecureServer({
- cert: fs.readFileSync(CERT_PATH),
- key: fs.readFileSync(PRIV_PATH),
-});
-
-server.on("error", (err) => console.error(err));
-
-const dispatch = (stream, headers, body = "") => {
- // respond with SSE stream if request accepts stream in headers
- if (headers.accept && headers.accept.includes("stream")) {
- sendStreamToClient(stream, body);
- return;
- }
-
- // else send a single event
- stream.respond({
- "content-type": "application/json; charset=utf-8",
- ":status": 200,
- });
- stream.end(JSON.stringify({ data: "hello and goodbye" + body }));
-};
-
-server.on("stream", (stream, headers) => {
- // dispatch async if there's a body
- if (headers[":method"] !== "GET") {
- stream.on("data", (chunk) => {
- dispatch(stream, headers, "- " + chunk);
- });
- } else {
- // otherwise dispatch sync
- dispatch(stream, headers);
- }
-});
-
-const sendStreamToClient = (stream, body) => {
- const STREAM_INTERVAL = 500;
- let count = 0;
- let streamIsOpen = true;
-
- stream.on("close", () => {
- streamIsOpen = false;
- console.log("stream closed");
- });
-
- stream.respond({
- "content-type": "text/event-stream; charset=utf-8",
- ":status": 200,
- });
-
- const sendEvent = (stream) => {
- if (!streamIsOpen) {
- count = 0;
- return;
- }
- count += 1;
- if (count < 50) {
- stream.write(`id: ${count}\nevent: testMessage\ndata: hello${body}\n\n`);
- setTimeout(() => sendEvent(stream), STREAM_INTERVAL);
- } else {
- stream.end(`id: ${count}\nevent: testMessage\ndata: goodbye${body}\n\n`);
- streamIsOpen = false;
- count = 0;
- }
- };
-
- sendEvent(stream);
-};
-
-server.listen(8443, () => console.log("server up on 8443"));
+const http2 = require('http2');
+const fs = require('fs');
+const path = require('path');
+
+const CERT_PATH = path.join(__dirname, '/HTTP2_cert.pem');
+const PRIV_PATH = path.join(__dirname, 'HTTP2_private.pem');
+
+const server = http2.createSecureServer({
+ cert: fs.readFileSync(CERT_PATH),
+ key: fs.readFileSync(PRIV_PATH),
+});
+
+server.on('error', (err) => console.error(err));
+
+const dispatch = (stream, headers, body = '') => {
+ // respond with SSE stream if request accepts stream in headers
+ if (headers.accept && headers.accept.includes('stream')) {
+ sendStreamToClient(stream, body);
+ return;
+ }
+
+ // else send a single event
+ stream.respond({
+ 'content-type': 'application/json; charset=utf-8',
+ ':status': 200,
+ });
+ stream.end(JSON.stringify({ data: `hello and goodbye${body}` }));
+};
+
+server.on('stream', (stream, headers) => {
+ // dispatch async if there's a body
+ if (headers[':method'] !== 'GET') {
+ stream.on('data', (chunk) => {
+ dispatch(stream, headers, `- ${chunk}`);
+ });
+ } else {
+ // otherwise dispatch sync
+ dispatch(stream, headers);
+ }
+});
+
+const sendStreamToClient = (stream, body) => {
+ const STREAM_INTERVAL = 500;
+ let count = 0;
+ let streamIsOpen = true;
+
+ stream.on('close', () => {
+ streamIsOpen = false;
+ console.log('stream closed');
+ });
+
+ stream.respond({
+ 'content-type': 'text/event-stream; charset=utf-8',
+ ':status': 200,
+ });
+
+ const sendEvent = (stream) => {
+ if (!streamIsOpen) {
+ count = 0;
+ return;
+ }
+ count += 1;
+ if (count < 50) {
+ stream.write(`id: ${count}\nevent: testMessage\ndata: hello${body}\n\n`);
+ setTimeout(() => sendEvent(stream), STREAM_INTERVAL);
+ } else {
+ stream.end(`id: ${count}\nevent: testMessage\ndata: goodbye${body}\n\n`);
+ streamIsOpen = false;
+ count = 0;
+ }
+ };
+
+ sendEvent(stream);
+};
+
+server.listen(8443, () => console.log('server up on 8443'));
diff --git a/test/SSE_HTTP1_server.js b/test/SSE_HTTP1_server.js
index f22a4e1c0..e398e122e 100644
--- a/test/SSE_HTTP1_server.js
+++ b/test/SSE_HTTP1_server.js
@@ -1,46 +1,46 @@
-const express = require('express');
-const SSE = require('express-sse');
-
-const app = express();
-const PORT = 3001;
-
-const sse = new SSE(['first message']);
-
-app.use(express.json());
-app.use(express.urlencoded({ extended: true }));
-
-const timeInterval = 3000;
-
-const sendStream = () => {
- // if listeners are gone, connection is closed
- if (sse.listenerCount('data') < 1) {
- console.log('connection closed by client');
- return;
- }
-
- // console.log(`the time is: ${Date.now()}`);
- sse.send(`the time is: ${Date.now()}`);
- setTimeout(sendStream, timeInterval);
-}
-
-const dispatchStreamOrHeaders = (req, res, next) => {
- if (req.headers.accept === 'text/event-stream') {
- setTimeout(sendStream, timeInterval);
- return next();
- }
-
- res.set({
- 'Connection': 'keep-alive',
- 'Content-Type': 'text/event-stream',
- 'Cache-Control': 'no-cache',
- 'Access-Control-Allow-Origin': '*',
- });
- res.status(201);
- return res.send();
-}
-
-app.get('/', dispatchStreamOrHeaders, sse.init);
-
-app.listen(PORT, () => {
- console.log(`HTTP1 SSE Server listening on port: ${PORT}`);
-});
\ No newline at end of file
+const express = require('express');
+const SSE = require('express-sse');
+
+const app = express();
+const PORT = 3001;
+
+const sse = new SSE(['first message']);
+
+app.use(express.json());
+app.use(express.urlencoded({ extended: true }));
+
+const timeInterval = 3000;
+
+const sendStream = () => {
+ // if listeners are gone, connection is closed
+ if (sse.listenerCount('data') < 1) {
+ console.log('connection closed by client');
+ return;
+ }
+
+ // console.log(`the time is: ${Date.now()}`);
+ sse.send(`the time is: ${Date.now()}`);
+ setTimeout(sendStream, timeInterval);
+};
+
+const dispatchStreamOrHeaders = (req, res, next) => {
+ if (req.headers.accept === 'text/event-stream') {
+ setTimeout(sendStream, timeInterval);
+ return next();
+ }
+
+ res.set({
+ Connection: 'keep-alive',
+ 'Content-Type': 'text/event-stream',
+ 'Cache-Control': 'no-cache',
+ 'Access-Control-Allow-Origin': '*',
+ });
+ res.status(201);
+ return res.send();
+};
+
+app.get('/', dispatchStreamOrHeaders, sse.init);
+
+app.listen(PORT, () => {
+ console.log(`HTTP1 SSE Server listening on port: ${PORT}`);
+});
diff --git a/test/dbModel.js b/test/dbModel.js
index f452fc9cb..e24b9f2fe 100644
--- a/test/dbModel.js
+++ b/test/dbModel.js
@@ -1,43 +1,43 @@
-//Commented full file because no MONGO_URI available for testing server
-
-// const mongoose = require("mongoose");
-// const path = require("path");
-// require("dotenv").config({ path: path.resolve(__dirname, "../.env") });
-
-// // console.log(process.env.MONGO_URI);
-// mongoose
-// .connect(process.env.MONGO_URI, {
-// // options for the connect method to parse the URI
-// useNewUrlParser: true,
-// useUnifiedTopology: true,
-// // sets the name of the DB that our collections are part of
-// // dbName: 'swell'
-// })
-// .then(() => console.log("Connected to Mongo DB."))
-// .catch((err) => console.log(err));
-
-// const Schema = mongoose.Schema;
-
-// // sets a schema for the 'bookSore' collection
-// const bookStoreSchema = new Schema({
-// title: {
-// type: String,
-// required: true,
-// },
-// author: {
-// type: String,
-// required: true,
-// },
-// pages: {
-// type: Number,
-// required: true,
-// },
-// });
-
-// // creats a model for the 'bookStore' collection that will be part of the export
-// const BookStore = mongoose.model("bookStore", bookStoreSchema);
-
-// // exports all the models in an object to be used in the controller
-// module.exports = {
-// BookStore,
-// };
+// Commented full file because no MONGO_URI available for testing server
+
+// const mongoose = require("mongoose");
+// const path = require("path");
+// require("dotenv").config({ path: path.resolve(__dirname, "../.env") });
+
+// // console.log(process.env.MONGO_URI);
+// mongoose
+// .connect(process.env.MONGO_URI, {
+// // options for the connect method to parse the URI
+// useNewUrlParser: true,
+// useUnifiedTopology: true,
+// // sets the name of the DB that our collections are part of
+// // dbName: 'swell'
+// })
+// .then(() => console.log("Connected to Mongo DB."))
+// .catch((err) => console.log(err));
+
+// const Schema = mongoose.Schema;
+
+// // sets a schema for the 'bookSore' collection
+// const bookStoreSchema = new Schema({
+// title: {
+// type: String,
+// required: true,
+// },
+// author: {
+// type: String,
+// required: true,
+// },
+// pages: {
+// type: Number,
+// required: true,
+// },
+// });
+
+// // creats a model for the 'bookStore' collection that will be part of the export
+// const BookStore = mongoose.model("bookStore", bookStoreSchema);
+
+// // exports all the models in an object to be used in the controller
+// module.exports = {
+// BookStore,
+// };
diff --git a/test/graphqlServer.js b/test/graphqlServer.js
index 04f2cc445..736bb6a00 100644
--- a/test/graphqlServer.js
+++ b/test/graphqlServer.js
@@ -1,98 +1,104 @@
-/* eslint-disable no-new */
-const express = require('express');
-const { ApolloServer } = require('apollo-server-express')
-const { execute, subscribe } = require('graphql');
-const { makeExecutableSchema } = require('graphql-tools');
-const { createServer } = require('http');
-const { SubscriptionServer } = require('subscriptions-transport-ws');
-const { PubSub } = require('graphql-subscriptions');
-const bodyParser = require('body-parser');
-
-const PORT = 4000;
-
-const app = express();
-const ws = createServer(app);
-
-const pubsub = new PubSub();
-
-const typeDefs = `
- type Query {
- feed: [Link!]!
- link(id: ID!): Link
- }
-
- type Mutation {
- post(url: String!, description: String!): Link!
- }
-
- type Subscription {
- newLink: Link
- }
-
- type Link {
- id: ID!
- description: String!
- url: String!
- }
-`;
-
-const links = [{
- id: 'link-0',
- url: 'www.getswell.io',
- description: 'One-stop-shop for testing API endpoints'
-}]
-
-let idCount = links.length
-
-const resolvers = {
- Query: {
- feed: () => links,
- },
- Mutation: {
- post: (parent, args) => {
- const link = {
- id: `link-${idCount++}`,
- description: args.description,
- url: args.url,
- }
- links.push(link);
- pubsub.publish("NEW_LINK", {newLink: link})
- return link
- },
- },
- Subscription: {
- newLink: { // create a subscription resolver function
- subscribe: () => {
- console.log('subscribed')
- return pubsub.asyncIterator("NEW_LINK")
- } // subscribe to changes in a topic
- }
- },
- Link: {
- id: (parent) => parent.id,
- description: (parent) => parent.description,
- url: (parent) => parent.url,
- },
-}
-
-const schema = makeExecutableSchema({ typeDefs, resolvers });
-
-app.use('/graphql', bodyParser.json());
-
-const apolloServer = new ApolloServer({ schema });
-
-apolloServer.applyMiddleware({ app });
-
-const graphqlApp = ws.listen(PORT, () => {
- console.log(`GraphQL Server is now running on http://localhost:${PORT}`);
- new SubscriptionServer({
- execute,
- subscribe,
- schema,
- }, {
- server: ws,
- path: '/graphql',
- });
-});
-
-module.exports = graphqlApp;
\ No newline at end of file
+/* eslint-disable no-new */
+const express = require('express');
+const { ApolloServer } = require('apollo-server-express');
+const { execute, subscribe } = require('graphql');
+const { makeExecutableSchema } = require('graphql-tools');
+const { createServer } = require('http');
+const { SubscriptionServer } = require('subscriptions-transport-ws');
+const { PubSub } = require('graphql-subscriptions');
+const bodyParser = require('body-parser');
+
+const PORT = 4000;
+
+const app = express();
+const ws = createServer(app);
+
+const pubsub = new PubSub();
+
+const typeDefs = `
+ type Query {
+ feed: [Link!]!
+ link(id: ID!): Link
+ }
+
+ type Mutation {
+ post(url: String!, description: String!): Link!
+ }
+
+ type Subscription {
+ newLink: Link
+ }
+
+ type Link {
+ id: ID!
+ description: String!
+ url: String!
+ }
+`;
+
+const links = [
+ {
+ id: 'link-0',
+ url: 'www.getswell.io',
+ description: 'One-stop-shop for testing API endpoints',
+ },
+];
+
+let idCount = links.length;
+
+const resolvers = {
+ Query: {
+ feed: () => links,
+ },
+ Mutation: {
+ post: (parent, args) => {
+ const link = {
+ id: `link-${idCount++}`,
+ description: args.description,
+ url: args.url,
+ };
+ links.push(link);
+ pubsub.publish('NEW_LINK', { newLink: link });
+ return link;
+ },
+ },
+ Subscription: {
+ newLink: {
+ // create a subscription resolver function
+ subscribe: () => {
+ console.log('subscribed');
+ return pubsub.asyncIterator('NEW_LINK');
+ }, // subscribe to changes in a topic
+ },
+ },
+ Link: {
+ id: (parent) => parent.id,
+ description: (parent) => parent.description,
+ url: (parent) => parent.url,
+ },
+};
+
+const schema = makeExecutableSchema({ typeDefs, resolvers });
+
+app.use('/graphql', bodyParser.json());
+
+const apolloServer = new ApolloServer({ schema });
+
+apolloServer.applyMiddleware({ app });
+
+const graphqlApp = ws.listen(PORT, () => {
+ console.log(`GraphQL Server is now running on http://localhost:${PORT}`);
+ new SubscriptionServer(
+ {
+ execute,
+ subscribe,
+ schema,
+ },
+ {
+ server: ws,
+ path: '/graphql',
+ }
+ );
+});
+
+module.exports = graphqlApp;
diff --git a/test/grpcServer.js b/test/grpcServer.js
index d0a968500..1dc34e2b2 100644
--- a/test/grpcServer.js
+++ b/test/grpcServer.js
@@ -1,104 +1,104 @@
-const path = require("path");
-const grpc = require("@grpc/grpc-js");
-const protoLoader = require("@grpc/proto-loader");
-
-// change PROTO_PATH to load a different mock proto file
-
-const PROTO_PATH = path.resolve(__dirname, "./hw2.proto");
-const PORT = "0.0.0.0:30051";
-// console.log("not working");
-// Service method to be used on unary test
-const SayHello = (call, callback) => {
- callback(null, { message: `Hello ${call.request.name}` });
-};
-
-// Service method to be used on nested unary test
-const SayHelloNested = (call, callback) => {
- callback(null, {
- serverMessage: [
- { message: `Hello! ${call.request.firstPerson.name}` },
- { message: `Hello! ${call.request.secondPerson.name}` },
- ],
- });
-};
-
-// Service method to be used on server streaming test
-const SayHellosSs = (call) => {
- const dataStream = [
- {
- message: "You",
- },
- {
- message: "Are",
- },
- {
- message: "doing IT",
- },
- {
- message: "Champ",
- },
- ];
- const reqMessage = { message: "hello!!! " + call.request.name };
- const updatedStream = [...dataStream, reqMessage];
- updatedStream.forEach((data) => {
- call.write(data);
- });
- call.end();
-};
-
-// Service method to be used on client streaming test
-const sayHelloCs = (call, callback) => {
- const messages = [];
- call.on("data", (data) => {
- messages.push(data);
- });
- call.on("end", () => {
- callback(null, {
- message: `received ${messages.length} messages`,
- });
- });
-};
-
-// Service method to be used on bidirectional streaming test
-const sayHelloBidi = (call, callback) => {
- call.on("data", (data) => {
- call.write({ message: "bidi stream: " + data.name });
- });
- call.on("end", () => {
- call.end();
- });
-};
-
-// function for starting a gRPC test server
-function main(status) {
- // load proto file
- const proto = protoLoader.loadSync(PROTO_PATH, {
- keepCase: true,
- longs: String,
- enums: String,
- defaults: true,
- oneofs: true,
- });
- const pkg = grpc.loadPackageDefinition(proto);
- if (status === "open") {
- // create new instance of grpc server
- const server = new grpc.Server();
-
- // add service and methods to the server
- server.addService(pkg.helloworld.Greeter.service, {
- SayHello,
- SayHelloNested,
- SayHellosSs,
- sayHelloCs,
- sayHelloBidi,
- });
-
- // bind specific port to the server and start the server
- server.bindAsync(PORT, grpc.ServerCredentials.createInsecure(), (port) => {
- server.start();
- console.log(`grpc server running on port ${PORT}`);
- });
- }
-}
-
-module.exports = main;
+const path = require('path');
+const grpc = require('@grpc/grpc-js');
+const protoLoader = require('@grpc/proto-loader');
+
+// change PROTO_PATH to load a different mock proto file
+
+const PROTO_PATH = path.resolve(__dirname, './hw2.proto');
+const PORT = '0.0.0.0:30051';
+// console.log("not working");
+// Service method to be used on unary test
+const SayHello = (call, callback) => {
+ callback(null, { message: `Hello ${call.request.name}` });
+};
+
+// Service method to be used on nested unary test
+const SayHelloNested = (call, callback) => {
+ callback(null, {
+ serverMessage: [
+ { message: `Hello! ${call.request.firstPerson.name}` },
+ { message: `Hello! ${call.request.secondPerson.name}` },
+ ],
+ });
+};
+
+// Service method to be used on server streaming test
+const SayHellosSs = (call) => {
+ const dataStream = [
+ {
+ message: 'You',
+ },
+ {
+ message: 'Are',
+ },
+ {
+ message: 'doing IT',
+ },
+ {
+ message: 'Champ',
+ },
+ ];
+ const reqMessage = { message: `hello!!! ${call.request.name}` };
+ const updatedStream = [...dataStream, reqMessage];
+ updatedStream.forEach((data) => {
+ call.write(data);
+ });
+ call.end();
+};
+
+// Service method to be used on client streaming test
+const sayHelloCs = (call, callback) => {
+ const messages = [];
+ call.on('data', (data) => {
+ messages.push(data);
+ });
+ call.on('end', () => {
+ callback(null, {
+ message: `received ${messages.length} messages`,
+ });
+ });
+};
+
+// Service method to be used on bidirectional streaming test
+const sayHelloBidi = (call, callback) => {
+ call.on('data', (data) => {
+ call.write({ message: `bidi stream: ${data.name}` });
+ });
+ call.on('end', () => {
+ call.end();
+ });
+};
+
+// function for starting a gRPC test server
+function main(status) {
+ // load proto file
+ const proto = protoLoader.loadSync(PROTO_PATH, {
+ keepCase: true,
+ longs: String,
+ enums: String,
+ defaults: true,
+ oneofs: true,
+ });
+ const pkg = grpc.loadPackageDefinition(proto);
+ if (status === 'open') {
+ // create new instance of grpc server
+ const server = new grpc.Server();
+
+ // add service and methods to the server
+ server.addService(pkg.helloworld.Greeter.service, {
+ SayHello,
+ SayHelloNested,
+ SayHellosSs,
+ sayHelloCs,
+ sayHelloBidi,
+ });
+
+ // bind specific port to the server and start the server
+ server.bindAsync(PORT, grpc.ServerCredentials.createInsecure(), (port) => {
+ server.start();
+ console.log(`grpc server running on port ${PORT}`);
+ });
+ }
+}
+
+module.exports = main;
diff --git a/test/httpServer.js b/test/httpServer.js
index 1c69006df..be01ade9a 100644
--- a/test/httpServer.js
+++ b/test/httpServer.js
@@ -1,41 +1,38 @@
-const express = require('express');
-const bodyParser = require('body-parser');
-const bookController = require('./mockController');
-
-
-const app = express();
-
-const PORT = 3000;
-
-app.use(bodyParser.json());
-app.use(bodyParser.urlencoded({ extended: true }));
-
-app.get('/clear', bookController.clearDB, (req, res) =>
- res.sendStatus(200)
-);
-
-app.get('/book', bookController.getAll, (req, res) =>
- res.status(200).json(res.locals.books)
-);
-
-app.post('/book', bookController.addBook, (req, res) =>
- res.status(200).json(res.locals.books)
-);
-
-app.put('/book/:title', bookController.updateEntireBook, (req, res) =>
- res.status(200).json(res.locals.books)
-);
-
-app.patch('/book/:title', bookController.patchBook, (req, res) =>
- res.status(200).json(res.locals.books)
-);
-
-app.delete('/book/:title', bookController.deleteBook, (req, res) =>
- res.status(200).json(res.locals.books)
-);
-
-const httpApp = app.listen(PORT, () => {
- console.log(`HTTP Server listening on port: ${PORT}`);
-});
-
-module.exports = httpApp;
+const express = require('express');
+const bodyParser = require('body-parser');
+const bookController = require('./mockController');
+
+const app = express();
+
+const PORT = 3000;
+
+app.use(bodyParser.json());
+app.use(bodyParser.urlencoded({ extended: true }));
+
+app.get('/clear', bookController.clearDB, (req, res) => res.sendStatus(200));
+
+app.get('/book', bookController.getAll, (req, res) =>
+ res.status(200).json(res.locals.books)
+);
+
+app.post('/book', bookController.addBook, (req, res) =>
+ res.status(200).json(res.locals.books)
+);
+
+app.put('/book/:title', bookController.updateEntireBook, (req, res) =>
+ res.status(200).json(res.locals.books)
+);
+
+app.patch('/book/:title', bookController.patchBook, (req, res) =>
+ res.status(200).json(res.locals.books)
+);
+
+app.delete('/book/:title', bookController.deleteBook, (req, res) =>
+ res.status(200).json(res.locals.books)
+);
+
+const httpApp = app.listen(PORT, () => {
+ console.log(`HTTP Server listening on port: ${PORT}`);
+});
+
+module.exports = httpApp;
diff --git a/test/mockController.js b/test/mockController.js
index f7ed7bb7a..68e03440d 100644
--- a/test/mockController.js
+++ b/test/mockController.js
@@ -1,57 +1,57 @@
-// const mongoose = require('mongoose');
-let db = [];
-
-// mongoose.set('useFindAndModify', false);
-
-const bookController = {};
-
-bookController.clearDB = (req, res, next) => {
- db = [];
- return next()
-}
-
-bookController.getAll = (req, res, next) => {
- res.locals.books = db;
- return next();
-}
-
-bookController.addBook = (req, res, next) => {
- const { title, author, pages } = req.body;
- const book = {title, author, pages};
- db.push(book);
- res.locals.books = db;
- return next();
-}
-
-bookController.updateEntireBook = (req, res, next) => {
- const { title } = req.params;
- const { author, pages } = req.body;
- const book = db.filter(book => book.title === title);
- book[0].title = title;
- book[0].author = author;
- book[0].pages = pages;
- db = [...db, ...book];
- res.locals.books = db;
- return next();
-}
-
-bookController.patchBook = (req, res, next) => {
- const { title } = req.params;
- const { author } = req.body;
- const book = db.filter(book => book.title === title);
- book[0].title = title;
- book[0].author = author;
- db = [...db, ...book];
- res.locals.books = db;
- return next();
-}
-
-bookController.deleteBook = (req, res, next) => {
- const { title } = req.params;
- const book = db.filter(book => book.title === title)[0];
- db = db.filter(book => book.title !== title);
- res.locals.books = book;
- return next();
-}
-
-module.exports = bookController;
\ No newline at end of file
+// const mongoose = require('mongoose');
+let db = [];
+
+// mongoose.set('useFindAndModify', false);
+
+const bookController = {};
+
+bookController.clearDB = (req, res, next) => {
+ db = [];
+ return next();
+};
+
+bookController.getAll = (req, res, next) => {
+ res.locals.books = db;
+ return next();
+};
+
+bookController.addBook = (req, res, next) => {
+ const { title, author, pages } = req.body;
+ const book = { title, author, pages };
+ db.push(book);
+ res.locals.books = db;
+ return next();
+};
+
+bookController.updateEntireBook = (req, res, next) => {
+ const { title } = req.params;
+ const { author, pages } = req.body;
+ const book = db.filter((book) => book.title === title);
+ book[0].title = title;
+ book[0].author = author;
+ book[0].pages = pages;
+ db = [...db, ...book];
+ res.locals.books = db;
+ return next();
+};
+
+bookController.patchBook = (req, res, next) => {
+ const { title } = req.params;
+ const { author } = req.body;
+ const book = db.filter((book) => book.title === title);
+ book[0].title = title;
+ book[0].author = author;
+ db = [...db, ...book];
+ res.locals.books = db;
+ return next();
+};
+
+bookController.deleteBook = (req, res, next) => {
+ const { title } = req.params;
+ const book = db.filter((book) => book.title === title)[0];
+ db = db.filter((book) => book.title !== title);
+ res.locals.books = book;
+ return next();
+};
+
+module.exports = bookController;
diff --git a/test/pageObjects/ComposerObj.js b/test/pageObjects/ComposerObj.js
index 5e6bba8b3..fecfe1e80 100644
--- a/test/pageObjects/ComposerObj.js
+++ b/test/pageObjects/ComposerObj.js
@@ -1,117 +1,117 @@
-const app = require("../testApp.js");
-
-class ComposerObj {
- get tabsComposer() {
- return app.client.$("a=Composer");
- }
-
- get tabsHistory() {
- return app.client.$("a=History");
- }
-
- // COMPOSER => COMPOSER
- // PROTOCOL SELECTOR
- get selectedNetwork() {
- return app.client.$("#selected-network");
- }
-
- get url() {
- return app.client.$(".input-is-medium");
- }
-
- get headers() {
- return app.client.$$(".header-row");
- }
-
- get cookies() {
- return app.client.$$(".cookie-row");
- }
-
- get restBodyCode() {
- const codeMirror = app.client.$("#body-entry-select");
- codeMirror.click();
- return codeMirror.$("textarea");
- }
-
- get gqlBodyCode() {
- const codeMirror = app.client.$("#gql-body-entry");
- codeMirror.click();
- return codeMirror.$("textarea");
- }
-
- get gqlVariableCode() {
- const codeMirror = app.client.$("#gql-var-entry");
- codeMirror.click();
- return codeMirror.$("textarea");
- }
-
- get addRequestBtn() {
- return app.client.$("button=Add New Request");
- }
-
- get closeConnectionBtn() {
- return app.client.$("button=Close Connection");
- }
-
- get reopenConnectionBtn() {
- return app.client.$("button=Re-Open Connection");
- }
-
- get testScriptCode() {
- const codeMirror = app.client.$("#test-script-entry");
- codeMirror.click();
- return codeMirror.$("textarea");
- }
-
- async clearRestBodyAndWriteKeys(keys, clear = true) {
- const backspace = [];
- for (let i = 0; i < 30; i += 1) {
- backspace.push("Backspace");
- }
-
- try {
- if (clear) await this.restBodyCode.keys(backspace);
- await this.restBodyCode.keys(keys);
- } catch (err) {
- console.error(err);
- }
- }
-
- async clearGQLBodyAndWriteKeys(keys, clear = true) {
- const backspace = [];
- for (let i = 0; i < 30; i += 1) {
- backspace.push("Backspace");
- }
-
- try {
- if (clear) await this.gqlBodyCode.keys(backspace);
- await this.gqlBodyCode.keys(keys);
- } catch (err) {
- console.error(err);
- }
- }
-
- async clickGQLVariablesAndWriteKeys(keys) {
- try {
- await this.gqlVariableCode.keys(keys);
- } catch (err) {
- console.error(err);
- }
- }
-
- async clearTestScriptAreaAndWriteKeys(keys, clear = true) {
- const backspace = [];
- for (let i = 0; i < 100; i += 1) {
- backspace.push("Backspace");
- }
-
- try {
- if (clear) await this.testScriptCode.keys(backspace);
- await this.testScriptCode.keys(keys);
- } catch (err) {
- console.error(err);
- }
- }
-}
-
-module.exports = new ComposerObj();
+const app = require('../testApp.js');
+
+class ComposerObj {
+ get tabsComposer() {
+ return app.client.$('a=Composer');
+ }
+
+ get tabsHistory() {
+ return app.client.$('a=History');
+ }
+
+ // COMPOSER => COMPOSER
+ // PROTOCOL SELECTOR
+ get selectedNetwork() {
+ return app.client.$('#selected-network');
+ }
+
+ get url() {
+ return app.client.$('.input-is-medium');
+ }
+
+ get headers() {
+ return app.client.$$('.header-row');
+ }
+
+ get cookies() {
+ return app.client.$$('.cookie-row');
+ }
+
+ get restBodyCode() {
+ const codeMirror = app.client.$('#body-entry-select');
+ codeMirror.click();
+ return codeMirror.$('textarea');
+ }
+
+ get gqlBodyCode() {
+ const codeMirror = app.client.$('#gql-body-entry');
+ codeMirror.click();
+ return codeMirror.$('textarea');
+ }
+
+ get gqlVariableCode() {
+ const codeMirror = app.client.$('#gql-var-entry');
+ codeMirror.click();
+ return codeMirror.$('textarea');
+ }
+
+ get addRequestBtn() {
+ return app.client.$('button=Add New Request');
+ }
+
+ get closeConnectionBtn() {
+ return app.client.$('button=Close Connection');
+ }
+
+ get reopenConnectionBtn() {
+ return app.client.$('button=Re-Open Connection');
+ }
+
+ get testScriptCode() {
+ const codeMirror = app.client.$('#test-script-entry');
+ codeMirror.click();
+ return codeMirror.$('textarea');
+ }
+
+ async clearRestBodyAndWriteKeys(keys, clear = true) {
+ const backspace = [];
+ for (let i = 0; i < 30; i += 1) {
+ backspace.push('Backspace');
+ }
+
+ try {
+ if (clear) await this.restBodyCode.keys(backspace);
+ await this.restBodyCode.keys(keys);
+ } catch (err) {
+ console.error(err);
+ }
+ }
+
+ async clearGQLBodyAndWriteKeys(keys, clear = true) {
+ const backspace = [];
+ for (let i = 0; i < 30; i += 1) {
+ backspace.push('Backspace');
+ }
+
+ try {
+ if (clear) await this.gqlBodyCode.keys(backspace);
+ await this.gqlBodyCode.keys(keys);
+ } catch (err) {
+ console.error(err);
+ }
+ }
+
+ async clickGQLVariablesAndWriteKeys(keys) {
+ try {
+ await this.gqlVariableCode.keys(keys);
+ } catch (err) {
+ console.error(err);
+ }
+ }
+
+ async clearTestScriptAreaAndWriteKeys(keys, clear = true) {
+ const backspace = [];
+ for (let i = 0; i < 100; i += 1) {
+ backspace.push('Backspace');
+ }
+
+ try {
+ if (clear) await this.testScriptCode.keys(backspace);
+ await this.testScriptCode.keys(keys);
+ } catch (err) {
+ console.error(err);
+ }
+ }
+}
+
+module.exports = new ComposerObj();
diff --git a/test/pageObjects/GrpcObj.js b/test/pageObjects/GrpcObj.js
index c32db13fd..6e4010dc9 100644
--- a/test/pageObjects/GrpcObj.js
+++ b/test/pageObjects/GrpcObj.js
@@ -1,141 +1,142 @@
-const app = require("../testApp.js");
-
-class GrpcObj {
- // opens up tabs for selecting network types
- get selectedNetwork() {
- return app.client.$("#selected-network");
- }
-
- // selects gRPC option from opened network tab
- get gRPCNetwork() {
- return app.client.$("a=gRPC");
- }
-
- // selects url box
- get url() {
- return app.client.$(".input-is-medium");
- }
- // selects codemirror text field for loading protofile
- get grpcProto() {
- const codeMirror = app.client.$("#grpcProtoEntryTextArea");
- codeMirror.click();
- return codeMirror.$("textarea");
- }
-
- // selects button for saving proto file
- get saveChanges() {
- return app.client.$("#save-proto");
- }
-
- // selects button for selecting service
- get openSelectServiceDropdown() {
- return app.client.$("#Select-Service-button");
- }
-
- // selects button for selecting greeter service
- get selectServiceGreeter() {
- return app.client.$("a=Greeter");
- }
-
- // selects button for selecting request
- get openRequestDropdown() {
- return app.client.$("#Select-Request-button");
- }
-
- // selects button for selecting SayHello request
- get selectRequestSayHelloFromDropDown() {
- return app.client.$("a=SayHello");
- }
-
- // selects button for opening dropdown for request after running SayHello
- get selectRequestSayHello() {
- return app.client.$("#SayHello-button");
- }
-
- // selects button for selecting SayHelloNested request
- get selectRequestSayHelloNestedFromDropDown() {
- return app.client.$("a=SayHelloNested");
- }
-
- // selects button for opening dropdown for request after running SayHelloNested
- get selectRequestSayHelloNested() {
- return app.client.$("#SayHelloNested-button");
- }
-
- // selects button for selecting SayHellosSs request
- get selectRequestSayHellosSsFromDropDown() {
- return app.client.$("a=SayHellosSs");
- }
-
- // selects button for opening dropdown for request after running SayHelloSs
- get selectRequestSayHellosSs() {
- return app.client.$("#SayHellosSs-button");
- }
-
- // selects button for selecting SayHelloCs request
- get selectRequestSayHelloCSFromDropDown() {
- return app.client.$("a=SayHelloCS");
- }
-
- // selects button for opening dropdown for request after running SayHelloCs
- get selectRequestSayHelloCS() {
- return app.client.$("#SayHelloCS-button");
- }
-
- // selects button for selecting SayHelloBidi request
- get selectRequestBidiFromDropDown() {
- return app.client.$("a=SayHelloBidi");
- }
-
- get selectRequestBidiButton() {
- return app.client.$("#SayHelloBidi-button");
- }
-
- // selects codemirror text field for displaying response
- get jsonPretty() {
- return app.client.$("#events-display");
- }
-
- // selects button for adding request to workspace pane
- get addRequestBtn() {
- return app.client.$("button=Add New Request");
- }
-
- // selects button for sending request
- get sendBtn() {
- return app.client.$("button=Send");
- }
-
- // selects button for removing single request from workspace pane
- get removeBtn() {
- return app.client.$("button=Remove");
- }
-
- get testScriptCode() {
- const codeMirror = app.client.$("#test-script-entry");
- codeMirror.click();
- return codeMirror.$("textarea");
- }
-
- get clearWorkspace() {
- return app.client.$("button=Clear Workspace");
- }
-
- async clearTestScriptAreaAndWriteKeys(keys, clear = true) {
- // It would be better to select the length of the previous test.
- // const pastTest = app.client.$('#test-script-entry').getText();
- const backspace = [];
- for (let i = 0; i < 100; i += 1) {
- backspace.push("Backspace");
- }
-
- try {
- if (clear) await this.testScriptCode.keys(backspace);
- await this.testScriptCode.keys(keys);
- } catch (err) {
- console.error(err);
- }
- }
-}
-
-module.exports = new GrpcObj();
+const app = require('../testApp.js');
+
+class GrpcObj {
+ // opens up tabs for selecting network types
+ get selectedNetwork() {
+ return app.client.$('#selected-network');
+ }
+
+ // selects gRPC option from opened network tab
+ get gRPCNetwork() {
+ return app.client.$('a=gRPC');
+ }
+
+ // selects url box
+ get url() {
+ return app.client.$('.input-is-medium');
+ }
+
+ // selects codemirror text field for loading protofile
+ get grpcProto() {
+ const codeMirror = app.client.$('#grpcProtoEntryTextArea');
+ codeMirror.click();
+ return codeMirror.$('textarea');
+ }
+
+ // selects button for saving proto file
+ get saveChanges() {
+ return app.client.$('#save-proto');
+ }
+
+ // selects button for selecting service
+ get openSelectServiceDropdown() {
+ return app.client.$('#Select-Service-button');
+ }
+
+ // selects button for selecting greeter service
+ get selectServiceGreeter() {
+ return app.client.$('a=Greeter');
+ }
+
+ // selects button for selecting request
+ get openRequestDropdown() {
+ return app.client.$('#Select-Request-button');
+ }
+
+ // selects button for selecting SayHello request
+ get selectRequestSayHelloFromDropDown() {
+ return app.client.$('a=SayHello');
+ }
+
+ // selects button for opening dropdown for request after running SayHello
+ get selectRequestSayHello() {
+ return app.client.$('#SayHello-button');
+ }
+
+ // selects button for selecting SayHelloNested request
+ get selectRequestSayHelloNestedFromDropDown() {
+ return app.client.$('a=SayHelloNested');
+ }
+
+ // selects button for opening dropdown for request after running SayHelloNested
+ get selectRequestSayHelloNested() {
+ return app.client.$('#SayHelloNested-button');
+ }
+
+ // selects button for selecting SayHellosSs request
+ get selectRequestSayHellosSsFromDropDown() {
+ return app.client.$('a=SayHellosSs');
+ }
+
+ // selects button for opening dropdown for request after running SayHelloSs
+ get selectRequestSayHellosSs() {
+ return app.client.$('#SayHellosSs-button');
+ }
+
+ // selects button for selecting SayHelloCs request
+ get selectRequestSayHelloCSFromDropDown() {
+ return app.client.$('a=SayHelloCS');
+ }
+
+ // selects button for opening dropdown for request after running SayHelloCs
+ get selectRequestSayHelloCS() {
+ return app.client.$('#SayHelloCS-button');
+ }
+
+ // selects button for selecting SayHelloBidi request
+ get selectRequestBidiFromDropDown() {
+ return app.client.$('a=SayHelloBidi');
+ }
+
+ get selectRequestBidiButton() {
+ return app.client.$('#SayHelloBidi-button');
+ }
+
+ // selects codemirror text field for displaying response
+ get jsonPretty() {
+ return app.client.$('#events-display');
+ }
+
+ // selects button for adding request to workspace pane
+ get addRequestBtn() {
+ return app.client.$('button=Add New Request');
+ }
+
+ // selects button for sending request
+ get sendBtn() {
+ return app.client.$('button=Send');
+ }
+
+ // selects button for removing single request from workspace pane
+ get removeBtn() {
+ return app.client.$('button=Remove');
+ }
+
+ get testScriptCode() {
+ const codeMirror = app.client.$('#test-script-entry');
+ codeMirror.click();
+ return codeMirror.$('textarea');
+ }
+
+ get clearWorkspace() {
+ return app.client.$('button=Clear Workspace');
+ }
+
+ async clearTestScriptAreaAndWriteKeys(keys, clear = true) {
+ // It would be better to select the length of the previous test.
+ // const pastTest = app.client.$('#test-script-entry').getText();
+ const backspace = [];
+ for (let i = 0; i < 100; i += 1) {
+ backspace.push('Backspace');
+ }
+
+ try {
+ if (clear) await this.testScriptCode.keys(backspace);
+ await this.testScriptCode.keys(keys);
+ } catch (err) {
+ console.error(err);
+ }
+ }
+}
+
+module.exports = new GrpcObj();
diff --git a/test/pageObjects/ReqRes.js b/test/pageObjects/ReqRes.js
index adbf45c55..27a36d124 100644
--- a/test/pageObjects/ReqRes.js
+++ b/test/pageObjects/ReqRes.js
@@ -1,53 +1,53 @@
-const app = require('../testApp.js');
-
-class ReqRes {
- get sendBtn() {
- return app.client.$('button=Send')
- }
-
- get removeBtn() {
- return app.client.$('button=Remove')
- }
-
- get mutationRemoveBtn() {
- return app.client.$('button#MUTATION')
- }
-
- get jsonPrettyError() {
- return app.client.$('pre.__json-pretty-error__')
- }
-
- get jsonPretty() {
- return app.client.$('pre.__json-pretty__')
- }
-
- get statusCode() {
- return app.client.$('div.tertiary-title')
- }
-
- get jsonKey() {
- return app.client.$('span.__json-key__')
- }
-
- get jsonValue() {
- return app.client.$('span.__json-value__')
- }
-
- get messageTextArea() {
- return app.client.$('input.websocket_input-text')
- }
-
- get messageBtn() {
- return app.client.$('button.websocket_input-btn')
- }
-
- get messageClient() {
- return app.client.$('div#id_websocket_message-client')
- }
-
- get messageServer() {
- return app.client.$('div#id_websocket_message-server')
- }
-}
-
-module.exports = new ReqRes();
\ No newline at end of file
+const app = require('../testApp.js');
+
+class ReqRes {
+ get sendBtn() {
+ return app.client.$('button=Send');
+ }
+
+ get removeBtn() {
+ return app.client.$('button=Remove');
+ }
+
+ get mutationRemoveBtn() {
+ return app.client.$('button#MUTATION');
+ }
+
+ get jsonPrettyError() {
+ return app.client.$('pre.__json-pretty-error__');
+ }
+
+ get jsonPretty() {
+ return app.client.$('pre.__json-pretty__');
+ }
+
+ get statusCode() {
+ return app.client.$('div.tertiary-title');
+ }
+
+ get jsonKey() {
+ return app.client.$('span.__json-key__');
+ }
+
+ get jsonValue() {
+ return app.client.$('span.__json-value__');
+ }
+
+ get messageTextArea() {
+ return app.client.$('input.websocket_input-text');
+ }
+
+ get messageBtn() {
+ return app.client.$('button.websocket_input-btn');
+ }
+
+ get messageClient() {
+ return app.client.$('div#id_websocket_message-client');
+ }
+
+ get messageServer() {
+ return app.client.$('div#id_websocket_message-server');
+ }
+}
+
+module.exports = new ReqRes();
diff --git a/test/pageObjects/WorkspaceObj.js b/test/pageObjects/WorkspaceObj.js
index 47929267c..c21b9c9fc 100644
--- a/test/pageObjects/WorkspaceObj.js
+++ b/test/pageObjects/WorkspaceObj.js
@@ -1,13 +1,13 @@
-const app = require('../testApp.js');
-
-class WorkspaceObj {
- get latestSendRequestBtn() {
- return app.client.$('button=Send');
- };
-
- get latestRemoveRequestBtn() {
- return app.client.$('button=Remove');
- };
-};
-
-module.exports = new WorkspaceObj();
\ No newline at end of file
+const app = require('../testApp.js');
+
+class WorkspaceObj {
+ get latestSendRequestBtn() {
+ return app.client.$('button=Send');
+ }
+
+ get latestRemoveRequestBtn() {
+ return app.client.$('button=Remove');
+ }
+}
+
+module.exports = new WorkspaceObj();
diff --git a/test/subSuites/appOpens.js b/test/subSuites/appOpens.js
index e30a69eb2..974c8e380 100644
--- a/test/subSuites/appOpens.js
+++ b/test/subSuites/appOpens.js
@@ -1,59 +1,59 @@
-const assert = require("assert"); //node's own assertion module
-const path = require("path");
-const fs = require("fs");
-const app = require("../testApp");
-
-module.exports = () => {
- describe("App opens and renders a page", () => {
- describe("Browser Window Tests", () => {
- it("window is visible", async () => {
- const isVisible = await app.browserWindow.isVisible();
- return assert.strictEqual(isVisible, true);
- });
-
- it("browser window title is 'Swell'", async () => {
- const titleWithBrowser = await app.browserWindow.getTitle();
- return assert.strictEqual(titleWithBrowser, "Swell");
- });
-
- it("Confirm browser window count is 1", async () => {
- const windowCount = await app.client.getWindowCount();
- return assert.strictEqual(1, windowCount);
- });
-
- it("take a snapshot of app", async () => {
- const imageBuffer = await app.browserWindow.capturePage();
- fs.writeFileSync(path.resolve(__dirname, "snapshot.png"), imageBuffer);
- });
- });
- describe("DOM Tests", () => {
- it("html file title is 'Swell'", async () => {
- const titleWithClient = await app.client
- .waitUntilWindowLoaded()
- .getTitle(); // the dom set inside dist/index.html which is set inside webpack htmlPlugin
- return assert.strictEqual(titleWithClient, "Swell");
- });
-
- it("devTool should NOT open since we are in production mode", async () => {
- const isOpen = await app.browserWindow.isDevToolsOpened();
- return assert.strictEqual(isOpen, false);
- });
- it("Composer panel exists", async () => {
- await app.client.waitUntilWindowLoaded();
- // $ is basically querySelector
- const composer = await app.client.$("#composer");
- return assert.notStrictEqual(composer.value, null);
- });
- it("Workspace exists", async () => {
- await app.client.waitUntilWindowLoaded();
- const workspace = await app.client.$("#workspace");
- return assert.notStrictEqual(workspace.value, null);
- });
- it("Responses panel exists", async () => {
- await app.client.waitUntilWindowLoaded();
- const responses = await app.client.$("#responses");
- return assert.notStrictEqual(responses.value, null);
- });
- });
- });
-};
+const assert = require('assert'); // node's own assertion module
+const path = require('path');
+const fs = require('fs');
+const app = require('../testApp');
+
+module.exports = () => {
+ describe('App opens and renders a page', () => {
+ describe('Browser Window Tests', () => {
+ it('window is visible', async () => {
+ const isVisible = await app.browserWindow.isVisible();
+ return assert.strictEqual(isVisible, true);
+ });
+
+ it("browser window title is 'Swell'", async () => {
+ const titleWithBrowser = await app.browserWindow.getTitle();
+ return assert.strictEqual(titleWithBrowser, 'Swell');
+ });
+
+ it('Confirm browser window count is 1', async () => {
+ const windowCount = await app.client.getWindowCount();
+ return assert.strictEqual(1, windowCount);
+ });
+
+ it('take a snapshot of app', async () => {
+ const imageBuffer = await app.browserWindow.capturePage();
+ fs.writeFileSync(path.resolve(__dirname, 'snapshot.png'), imageBuffer);
+ });
+ });
+ describe('DOM Tests', () => {
+ it("html file title is 'Swell'", async () => {
+ const titleWithClient = await app.client
+ .waitUntilWindowLoaded()
+ .getTitle(); // the dom set inside dist/index.html which is set inside webpack htmlPlugin
+ return assert.strictEqual(titleWithClient, 'Swell');
+ });
+
+ it('devTool should NOT open since we are in production mode', async () => {
+ const isOpen = await app.browserWindow.isDevToolsOpened();
+ return assert.strictEqual(isOpen, false);
+ });
+ it('Composer panel exists', async () => {
+ await app.client.waitUntilWindowLoaded();
+ // $ is basically querySelector
+ const composer = await app.client.$('#composer');
+ return assert.notStrictEqual(composer.value, null);
+ });
+ it('Workspace exists', async () => {
+ await app.client.waitUntilWindowLoaded();
+ const workspace = await app.client.$('#workspace');
+ return assert.notStrictEqual(workspace.value, null);
+ });
+ it('Responses panel exists', async () => {
+ await app.client.waitUntilWindowLoaded();
+ const responses = await app.client.$('#responses');
+ return assert.notStrictEqual(responses.value, null);
+ });
+ });
+ });
+};
diff --git a/test/subSuites/graphqlTest.js b/test/subSuites/graphqlTest.js
index 5b8767145..29a601d85 100644
--- a/test/subSuites/graphqlTest.js
+++ b/test/subSuites/graphqlTest.js
@@ -1,229 +1,253 @@
-const chai = require("chai");
-const composerObj = require('../pageObjects/ComposerObj.js');
-const workspaceObj = require('../pageObjects/WorkspaceObj.js');
-const app = require('../testApp.js');
-const graphqlServer = require('../graphqlServer')
-
-const expect = chai.expect;
-
-module.exports = () => {
- describe("GraphQL requests", () => {
-
- const fillGQLRequest = async (url, method, body = '', variables = '', headers = [], cookies = []) => {
-
- try {
- // click and check GRAPHQL
- await composerObj.selectedNetwork.click();
- await app.client.$('a=REST').click();
- await composerObj.selectedNetwork.click();
- await app.client.$('a=GRAPHQL').click();
-
- // click and select METHOD if it isn't QUERY
- if(method !== 'QUERY') {
- await app.client.$('span=QUERY').click();
- await app.client.$(`a=${method}`).click();
- }
-
- // type in url
- await composerObj.url.setValue(url);
-
- // set headers
- headers.forEach(async ({ key, value },index) => {
- await app.client.$(`//*[@id="header-row${index}"]/input[1]`).setValue(key);
- await app.client.$(`//*[@id="header-row${index}"]/input[2]`).setValue(value);
- await app.client.$('button=+ Header').click();
- });
-
- // set cookies
- cookies.forEach(async ({ key, value },index) => {
- await app.client.$(`//*[@id="cookie-row${index}"]/input[1]`).setValue(key);
- await app.client.$(`//*[@id="cookie-row${index}"]/input[2]`).setValue(value);
- await app.client.$('button=+ Cookie').click();
- });
-
- // select Body and type in body
- await composerObj.clearGQLBodyAndWriteKeys(body);
-
- // select Variables and type in variables
- await composerObj.clickGQLVariablesAndWriteKeys(variables);
-
- } catch(err) {
- console.error(err)
- }
- };
-
- const addAndSend = async () => {
- try {
- await composerObj.addRequestBtn.click();
- await workspaceObj.latestSendRequestBtn.click();
- } catch(err) {
- console.error(err);
- }
- }
-
- before(() => {
- app.client.$('button=Clear Workspace').click();
- })
-
- after(() => {
- try {
- graphqlServer.close();
- console.log('graphqlServer closed')
- } catch(err) {
- console.error(err)
- }
- })
-
- it("it should be able to introspect the schema (PUBLIC API)", async () => {
- try {
- // click and check GRAPHQL
- await composerObj.selectedNetwork.click();
- await app.client.$('a=GRAPHQL').click();
-
- // type in url
- await composerObj.url.setValue("https://countries.trevorblades.com/");
-
- // click introspect
- await app.client.$('button=Introspect').click();
-
- await new Promise((resolve) => {
- setTimeout(async () => {
- try {
- const introspectionResult = await app.client.$('#gql-introspection .CodeMirror-code').getText();
- expect(introspectionResult).to.include(`CacheControlScope`);
- resolve();
- } catch(err) {
- console.error(err)
- }
- }, 1000)
- });
- } catch(err) {
- console.error(err)
- }
- });
-
- it("it should be able to create queries using variables (PUBLIC API)", async () => {
- try {
- const method = "QUERY";
- const url = "https://countries.trevorblades.com/";
- const query = 'query($code: ID!) {country(code: $code) {capital}}';
- const variables = '{"code": "AE"}';
-
- // type in url
- await fillGQLRequest(url, method, query, variables );
- await addAndSend();
-
- await new Promise((resolve) => {
- setTimeout(async () => {
- try {
- const events = await app.client.$('#events-display .CodeMirror-code').getText();
- expect(events).to.include("Abu Dhabi");
- resolve();
- } catch(err) {
- console.error(err)
- }
- }, 1000)
- });
- } catch(err) {
- console.error(err)
- }
- });
-
- it("it should give you the appropriate error message with incorrect queries (LOCAL API)", async () => {
- try {
- const method = "QUERY";
- const url = "http://localhost:4000/graphql";
- const query = 'query {feed {descriptions}}';
-
- // type in url
- await fillGQLRequest(url, method, query);
- await addAndSend();
-
- await new Promise((resolve) => {
- setTimeout(async () => {
- try {
- const statusCode = await app.client.$('.status-tag').getText();
- const events = await app.client.$('#events-display .CodeMirror-code').getText();
-
- expect(statusCode).to.equal("Error");
- expect(events).to.include('"message": "Cannot query field');
- resolve();
- } catch(err) {
- console.error(err)
- }
- }, 1000)
- });
- } catch(err) {
- console.error(err)
- }
- });
-
- it("it should work with mutations (LOCAL API)", async () => {
- try {
- const method = "MUTATION";
- const url = "http://localhost:4000/graphql";
- const query = 'mutation {post(url: "www.piedpiper.com" description: "Middle-out compression") {url}}';
-
- // type in url
- await fillGQLRequest(url, method, query);
- await addAndSend();
-
- await new Promise((resolve) => {
- setTimeout(async () => {
- try {
- const statusCode = await app.client.$('.status-tag').getText();
- const events = await app.client.$('#events-display .CodeMirror-code').getText();
-
- expect(statusCode).to.equal("Success");
- expect(events).to.include('www.piedpiper.com');
- resolve();
- } catch(err) {
- console.error(err)
- }
- }, 1000)
- });
- } catch(err) {
- console.error(err)
- }
- });
-
- it("it should work with subscriptions (LOCAL API)", async () => {
- try {
- // START SUBSCRIPTION
- const method = "SUBSCRIPTION";
- const url = "http://localhost:4000/graphql";
- const query = 'subscription {newLink {id description}}';
-
- await fillGQLRequest(url, method, query);
- await addAndSend();
-
- // SEND MUTATION
- const method2 = "MUTATION";
- const url2 = "http://localhost:4000/graphql";
- const query2 = 'mutation {post(url: "www.gavinbelson.com" description: "Tethics") {url}}';
-
- await fillGQLRequest(url2, method2, query2);
- await addAndSend();
-
- await new Promise((resolve) => {
- setTimeout(async () => {
- try {
- await app.client.$('#view-button-3').click();
-
- const statusCode = await app.client.$('.status-tag').getText();
- const events = await app.client.$('#events-display .CodeMirror-code').getText();
-
- expect(statusCode).to.equal("Success");
- expect(events).to.include('Tethics');
- resolve();
- } catch(err) {
- console.error(err)
- }
- }, 1000)
- });
- } catch(err) {
- console.error(err)
- }
- });
- })
-}
+const chai = require('chai');
+const composerObj = require('../pageObjects/ComposerObj.js');
+const workspaceObj = require('../pageObjects/WorkspaceObj.js');
+const app = require('../testApp.js');
+const graphqlServer = require('../graphqlServer');
+
+const { expect } = chai;
+
+module.exports = () => {
+ describe('GraphQL requests', () => {
+ const fillGQLRequest = async (
+ url,
+ method,
+ body = '',
+ variables = '',
+ headers = [],
+ cookies = []
+ ) => {
+ try {
+ // click and check GRAPHQL
+ await composerObj.selectedNetwork.click();
+ await app.client.$('a=REST').click();
+ await composerObj.selectedNetwork.click();
+ await app.client.$('a=GRAPHQL').click();
+
+ // click and select METHOD if it isn't QUERY
+ if (method !== 'QUERY') {
+ await app.client.$('span=QUERY').click();
+ await app.client.$(`a=${method}`).click();
+ }
+
+ // type in url
+ await composerObj.url.setValue(url);
+
+ // set headers
+ headers.forEach(async ({ key, value }, index) => {
+ await app.client
+ .$(`//*[@id="header-row${index}"]/input[1]`)
+ .setValue(key);
+ await app.client
+ .$(`//*[@id="header-row${index}"]/input[2]`)
+ .setValue(value);
+ await app.client.$('button=+ Header').click();
+ });
+
+ // set cookies
+ cookies.forEach(async ({ key, value }, index) => {
+ await app.client
+ .$(`//*[@id="cookie-row${index}"]/input[1]`)
+ .setValue(key);
+ await app.client
+ .$(`//*[@id="cookie-row${index}"]/input[2]`)
+ .setValue(value);
+ await app.client.$('button=+ Cookie').click();
+ });
+
+ // select Body and type in body
+ await composerObj.clearGQLBodyAndWriteKeys(body);
+
+ // select Variables and type in variables
+ await composerObj.clickGQLVariablesAndWriteKeys(variables);
+ } catch (err) {
+ console.error(err);
+ }
+ };
+
+ const addAndSend = async () => {
+ try {
+ await composerObj.addRequestBtn.click();
+ await workspaceObj.latestSendRequestBtn.click();
+ } catch (err) {
+ console.error(err);
+ }
+ };
+
+ before(() => {
+ app.client.$('button=Clear Workspace').click();
+ });
+
+ after(() => {
+ try {
+ graphqlServer.close();
+ console.log('graphqlServer closed');
+ } catch (err) {
+ console.error(err);
+ }
+ });
+
+ it('it should be able to introspect the schema (PUBLIC API)', async () => {
+ try {
+ // click and check GRAPHQL
+ await composerObj.selectedNetwork.click();
+ await app.client.$('a=GRAPHQL').click();
+
+ // type in url
+ await composerObj.url.setValue('https://countries.trevorblades.com/');
+
+ // click introspect
+ await app.client.$('button=Introspect').click();
+
+ await new Promise((resolve) => {
+ setTimeout(async () => {
+ try {
+ const introspectionResult = await app.client
+ .$('#gql-introspection .CodeMirror-code')
+ .getText();
+ expect(introspectionResult).to.include(`CacheControlScope`);
+ resolve();
+ } catch (err) {
+ console.error(err);
+ }
+ }, 1000);
+ });
+ } catch (err) {
+ console.error(err);
+ }
+ });
+
+ it('it should be able to create queries using variables (PUBLIC API)', async () => {
+ try {
+ const method = 'QUERY';
+ const url = 'https://countries.trevorblades.com/';
+ const query = 'query($code: ID!) {country(code: $code) {capital}}';
+ const variables = '{"code": "AE"}';
+
+ // type in url
+ await fillGQLRequest(url, method, query, variables);
+ await addAndSend();
+
+ await new Promise((resolve) => {
+ setTimeout(async () => {
+ try {
+ const events = await app.client
+ .$('#events-display .CodeMirror-code')
+ .getText();
+ expect(events).to.include('Abu Dhabi');
+ resolve();
+ } catch (err) {
+ console.error(err);
+ }
+ }, 1000);
+ });
+ } catch (err) {
+ console.error(err);
+ }
+ });
+
+ it('it should give you the appropriate error message with incorrect queries (LOCAL API)', async () => {
+ try {
+ const method = 'QUERY';
+ const url = 'http://localhost:4000/graphql';
+ const query = 'query {feed {descriptions}}';
+
+ // type in url
+ await fillGQLRequest(url, method, query);
+ await addAndSend();
+
+ await new Promise((resolve) => {
+ setTimeout(async () => {
+ try {
+ const statusCode = await app.client.$('.status-tag').getText();
+ const events = await app.client
+ .$('#events-display .CodeMirror-code')
+ .getText();
+
+ expect(statusCode).to.equal('Error');
+ expect(events).to.include('"message": "Cannot query field');
+ resolve();
+ } catch (err) {
+ console.error(err);
+ }
+ }, 1000);
+ });
+ } catch (err) {
+ console.error(err);
+ }
+ });
+
+ it('it should work with mutations (LOCAL API)', async () => {
+ try {
+ const method = 'MUTATION';
+ const url = 'http://localhost:4000/graphql';
+ const query =
+ 'mutation {post(url: "www.piedpiper.com" description: "Middle-out compression") {url}}';
+
+ // type in url
+ await fillGQLRequest(url, method, query);
+ await addAndSend();
+
+ await new Promise((resolve) => {
+ setTimeout(async () => {
+ try {
+ const statusCode = await app.client.$('.status-tag').getText();
+ const events = await app.client
+ .$('#events-display .CodeMirror-code')
+ .getText();
+
+ expect(statusCode).to.equal('Success');
+ expect(events).to.include('www.piedpiper.com');
+ resolve();
+ } catch (err) {
+ console.error(err);
+ }
+ }, 1000);
+ });
+ } catch (err) {
+ console.error(err);
+ }
+ });
+
+ it('it should work with subscriptions (LOCAL API)', async () => {
+ try {
+ // START SUBSCRIPTION
+ const method = 'SUBSCRIPTION';
+ const url = 'http://localhost:4000/graphql';
+ const query = 'subscription {newLink {id description}}';
+
+ await fillGQLRequest(url, method, query);
+ await addAndSend();
+
+ // SEND MUTATION
+ const method2 = 'MUTATION';
+ const url2 = 'http://localhost:4000/graphql';
+ const query2 =
+ 'mutation {post(url: "www.gavinbelson.com" description: "Tethics") {url}}';
+
+ await fillGQLRequest(url2, method2, query2);
+ await addAndSend();
+
+ await new Promise((resolve) => {
+ setTimeout(async () => {
+ try {
+ await app.client.$('#view-button-3').click();
+
+ const statusCode = await app.client.$('.status-tag').getText();
+ const events = await app.client
+ .$('#events-display .CodeMirror-code')
+ .getText();
+
+ expect(statusCode).to.equal('Success');
+ expect(events).to.include('Tethics');
+ resolve();
+ } catch (err) {
+ console.error(err);
+ }
+ }, 1000);
+ });
+ } catch (err) {
+ console.error(err);
+ }
+ });
+ });
+};
diff --git a/test/subSuites/graphqlTestingTest.js b/test/subSuites/graphqlTestingTest.js
index 7cb7e9bcd..3aec7074c 100644
--- a/test/subSuites/graphqlTestingTest.js
+++ b/test/subSuites/graphqlTestingTest.js
@@ -1,173 +1,183 @@
-const chai = require("chai");
-const composerObj = require('../pageObjects/ComposerObj.js');
-const workspaceObj = require('../pageObjects/WorkspaceObj.js');
-const app = require('../testApp.js');
-const graphqlServer = require('../graphqlServer')
-
-const expect = chai.expect;
-
-module.exports = () => {
- describe("GraphQL Testing Controller", () => {
-
-
- // This will fill out the composer with a GraphQL request when invoked.
- const fillGQLRequest = async (url, method, body = '', variables = '', headers = [], cookies = []) => {
-
- try {
- // click and check GRAPHQL
- await composerObj.selectedNetwork.click();
- await app.client.$('a=REST').click();
- await composerObj.selectedNetwork.click();
- await app.client.$('a=GRAPHQL').click();
-
- // click and select METHOD if it isn't QUERY
- if (method !== 'QUERY') {
- await app.client.$('span=QUERY').click();
- await app.client.$(`a=${method}`).click();
- }
-
- // type in url
- await composerObj.url.setValue(url);
-
- // set headers
- headers.forEach(async ({ key, value }, index) => {
- await app.client.$(`//*[@id="header-row${index}"]/input[1]`).setValue(key);
- await app.client.$(`//*[@id="header-row${index}"]/input[2]`).setValue(value);
- await app.client.$('button=+ Header').click();
- });
-
- // set cookies
- cookies.forEach(async ({ key, value }, index) => {
- await app.client.$(`//*[@id="cookie-row${index}"]/input[1]`).setValue(key);
- await app.client.$(`//*[@id="cookie-row${index}"]/input[2]`).setValue(value);
- await app.client.$('button=+ Cookie').click();
- });
-
- // select Body and type in body
- await composerObj.clearGQLBodyAndWriteKeys(body);
-
- // select Variables and type in variables
- await composerObj.clickGQLVariablesAndWriteKeys(variables);
-
- } catch (err) {
- console.error(err)
- }
- };
-
- // This will add and send the most recent request in the workspace.
- const addAndSend = async () => {
- try {
- await composerObj.addRequestBtn.click();
- await workspaceObj.latestSendRequestBtn.click();
- } catch (err) {
- console.error(err);
- }
- }
-
- const clearAndFillTestScriptArea = async (script) => {
- try {
- // click the view tests button to reveal the test code editor
- await app.client.$("span=View Tests").click();
- // set the value of the code editor to be some hard coded simple assertion tests
- await composerObj.clearTestScriptAreaAndWriteKeys(script);
- // Close the tests view pane.
- await app.client.$("span=Hide Tests").click();
- } catch (err) {
- console.error(err);
- }
- };
-
- before(() => {
- app.client.$('button=Clear Workspace').click();
- })
-
- after(() => {
- try {
- graphqlServer.close();
- console.log('graphqlServer closed')
- } catch (err) {
- console.error(err)
- }
- })
-
- it("it should be able to resolve a simple passing test", async () => {
- const method = "QUERY";
- const url = "https://countries.trevorblades.com/";
- const query = 'query($code: ID!) {country(code: $code) {capital}}';
- const variables = '{"code": "AE"}';
- const script = "assert.strictEqual(3, 3, 'Expect correct types.');";
-
- // type in url
- await fillGQLRequest(url, method, query, variables);
- await clearAndFillTestScriptArea(script);
- await addAndSend();
-
- // Select the Tests column inside of Responses pane.
- await app.client.$("a=Tests").click();
-
- const testStatus = await new Promise((resolve) => {
- setTimeout(async () => {
- const text = await app.client.$("#TestResult-0-status").getText();
- resolve(text);
- }, 500);
- // block for 500 ms since we need to wait for a response; normally test server would
- // respond fast enough since it is localhost and not a remote server
- });
- expect(testStatus).to.equal("PASS");
- });
-
- it("it should be able to resolve a simple failing test", async () => {
- const method = "QUERY";
- const url = "https://countries.trevorblades.com/";
- const query = 'query($code: ID!) {country(code: $code) {capital}}';
- const variables = '{"code": "AE"}';
- const script = "assert.strictEqual(3, 2, 'Expect failing test.');";
-
- // type in url
- await fillGQLRequest(url, method, query, variables);
- await clearAndFillTestScriptArea(script);
- await addAndSend();
-
- // Select the Tests column inside of Responses pane.
- await app.client.$("a=Tests").click();
-
- const testStatus = await new Promise((resolve) => {
- setTimeout(async () => {
- const text = await app.client.$("#TestResult-0-status").getText();
- resolve(text);
- }, 1000);
- // block for 500 ms since we need to wait for a response; normally test server would
- // respond fast enough since it is localhost and not a remote server
- });
- expect(testStatus).to.equal("FAIL");
- });
-
- it("it should be run test on response data", async () => {
- const method = "QUERY";
- const url = "https://countries.trevorblades.com/";
- const query = 'query($code: ID!) {country(code: $code) {capital}}';
- const variables = '{"code": "AE"}';
- const script =
- "expect(response.headers, 'headers exists on response object').to.exist;";
-
- // type in url
- await fillGQLRequest(url, method, query, variables);
- await clearAndFillTestScriptArea(script);
- await addAndSend();
-
- // Select the Tests column inside of Responses pane.
- await app.client.$("a=Tests").click();
-
- const testStatus = await new Promise((resolve) => {
- setTimeout(async () => {
- const text = await app.client.$("#TestResult-0-status").getText();
- resolve(text);
- }, 500);
- // block for 500 ms since we need to wait for a response; normally test server would
- // respond fast enough since it is localhost and not a remote server
- });
- expect(testStatus).to.equal("PASS");
- });
-
- })
-}
+const chai = require('chai');
+const composerObj = require('../pageObjects/ComposerObj.js');
+const workspaceObj = require('../pageObjects/WorkspaceObj.js');
+const app = require('../testApp.js');
+const graphqlServer = require('../graphqlServer');
+
+const { expect } = chai;
+
+module.exports = () => {
+ describe('GraphQL Testing Controller', () => {
+ // This will fill out the composer with a GraphQL request when invoked.
+ const fillGQLRequest = async (
+ url,
+ method,
+ body = '',
+ variables = '',
+ headers = [],
+ cookies = []
+ ) => {
+ try {
+ // click and check GRAPHQL
+ await composerObj.selectedNetwork.click();
+ await app.client.$('a=REST').click();
+ await composerObj.selectedNetwork.click();
+ await app.client.$('a=GRAPHQL').click();
+
+ // click and select METHOD if it isn't QUERY
+ if (method !== 'QUERY') {
+ await app.client.$('span=QUERY').click();
+ await app.client.$(`a=${method}`).click();
+ }
+
+ // type in url
+ await composerObj.url.setValue(url);
+
+ // set headers
+ headers.forEach(async ({ key, value }, index) => {
+ await app.client
+ .$(`//*[@id="header-row${index}"]/input[1]`)
+ .setValue(key);
+ await app.client
+ .$(`//*[@id="header-row${index}"]/input[2]`)
+ .setValue(value);
+ await app.client.$('button=+ Header').click();
+ });
+
+ // set cookies
+ cookies.forEach(async ({ key, value }, index) => {
+ await app.client
+ .$(`//*[@id="cookie-row${index}"]/input[1]`)
+ .setValue(key);
+ await app.client
+ .$(`//*[@id="cookie-row${index}"]/input[2]`)
+ .setValue(value);
+ await app.client.$('button=+ Cookie').click();
+ });
+
+ // select Body and type in body
+ await composerObj.clearGQLBodyAndWriteKeys(body);
+
+ // select Variables and type in variables
+ await composerObj.clickGQLVariablesAndWriteKeys(variables);
+ } catch (err) {
+ console.error(err);
+ }
+ };
+
+ // This will add and send the most recent request in the workspace.
+ const addAndSend = async () => {
+ try {
+ await composerObj.addRequestBtn.click();
+ await workspaceObj.latestSendRequestBtn.click();
+ } catch (err) {
+ console.error(err);
+ }
+ };
+
+ const clearAndFillTestScriptArea = async (script) => {
+ try {
+ // click the view tests button to reveal the test code editor
+ await app.client.$('span=View Tests').click();
+ // set the value of the code editor to be some hard coded simple assertion tests
+ await composerObj.clearTestScriptAreaAndWriteKeys(script);
+ // Close the tests view pane.
+ await app.client.$('span=Hide Tests').click();
+ } catch (err) {
+ console.error(err);
+ }
+ };
+
+ before(() => {
+ app.client.$('button=Clear Workspace').click();
+ });
+
+ after(() => {
+ try {
+ graphqlServer.close();
+ console.log('graphqlServer closed');
+ } catch (err) {
+ console.error(err);
+ }
+ });
+
+ it('it should be able to resolve a simple passing test', async () => {
+ const method = 'QUERY';
+ const url = 'https://countries.trevorblades.com/';
+ const query = 'query($code: ID!) {country(code: $code) {capital}}';
+ const variables = '{"code": "AE"}';
+ const script = "assert.strictEqual(3, 3, 'Expect correct types.');";
+
+ // type in url
+ await fillGQLRequest(url, method, query, variables);
+ await clearAndFillTestScriptArea(script);
+ await addAndSend();
+
+ // Select the Tests column inside of Responses pane.
+ await app.client.$('a=Tests').click();
+
+ const testStatus = await new Promise((resolve) => {
+ setTimeout(async () => {
+ const text = await app.client.$('#TestResult-0-status').getText();
+ resolve(text);
+ }, 500);
+ // block for 500 ms since we need to wait for a response; normally test server would
+ // respond fast enough since it is localhost and not a remote server
+ });
+ expect(testStatus).to.equal('PASS');
+ });
+
+ it('it should be able to resolve a simple failing test', async () => {
+ const method = 'QUERY';
+ const url = 'https://countries.trevorblades.com/';
+ const query = 'query($code: ID!) {country(code: $code) {capital}}';
+ const variables = '{"code": "AE"}';
+ const script = "assert.strictEqual(3, 2, 'Expect failing test.');";
+
+ // type in url
+ await fillGQLRequest(url, method, query, variables);
+ await clearAndFillTestScriptArea(script);
+ await addAndSend();
+
+ // Select the Tests column inside of Responses pane.
+ await app.client.$('a=Tests').click();
+
+ const testStatus = await new Promise((resolve) => {
+ setTimeout(async () => {
+ const text = await app.client.$('#TestResult-0-status').getText();
+ resolve(text);
+ }, 1000);
+ // block for 500 ms since we need to wait for a response; normally test server would
+ // respond fast enough since it is localhost and not a remote server
+ });
+ expect(testStatus).to.equal('FAIL');
+ });
+
+ it('it should be run test on response data', async () => {
+ const method = 'QUERY';
+ const url = 'https://countries.trevorblades.com/';
+ const query = 'query($code: ID!) {country(code: $code) {capital}}';
+ const variables = '{"code": "AE"}';
+ const script =
+ "expect(response.headers, 'headers exists on response object').to.exist;";
+
+ // type in url
+ await fillGQLRequest(url, method, query, variables);
+ await clearAndFillTestScriptArea(script);
+ await addAndSend();
+
+ // Select the Tests column inside of Responses pane.
+ await app.client.$('a=Tests').click();
+
+ const testStatus = await new Promise((resolve) => {
+ setTimeout(async () => {
+ const text = await app.client.$('#TestResult-0-status').getText();
+ resolve(text);
+ }, 500);
+ // block for 500 ms since we need to wait for a response; normally test server would
+ // respond fast enough since it is localhost and not a remote server
+ });
+ expect(testStatus).to.equal('PASS');
+ });
+ });
+};
diff --git a/test/subSuites/grpcTest.js b/test/subSuites/grpcTest.js
index 55f7187fd..c35fa4d0c 100644
--- a/test/subSuites/grpcTest.js
+++ b/test/subSuites/grpcTest.js
@@ -1,131 +1,133 @@
-const chai = require("chai");
-const fs = require("fs");
-const path = require("path");
-const grpcObj = require("../pageObjects/GrpcObj.js");
-const grpcServer = require('../grpcServer.js');
-
-
-const expect = chai.expect;
-
-module.exports = () => {
- describe("gRPC requests", () => {
-
- let proto = "";
-
- before((done) => {
- try{
- fs.readFile(path.join(__dirname, "../hw2.proto"), "utf8", (err, data) => {
- if (err) console.log(err);
- proto = data;
- done();
- });
- } catch (err) {
- console.error(err);
- }
- });
-
- before(async () => {
- try {
- grpcServer('open')
- await composerSetup();
- await grpcObj.openSelectServiceDropdown.click();
- } catch(err) {
- console.error(err)
- }
- });
-
- after(async () => {
- try {
- await grpcObj.clearWorkspace.click();
- } catch (err) {
- console.log(err);
- }
- })
-
- const composerSetup = async () => {
- try {
- await grpcObj.selectedNetwork.click();
- await grpcObj.gRPCNetwork.click();
- await grpcObj.url.addValue("0.0.0.0:30051");
- await grpcObj.grpcProto.addValue(proto);
- await grpcObj.saveChanges.click();
- } catch(err) {
- console.error(err)
- }
- };
- const addReqAndSend = async () => {
- try {
- await grpcObj.addRequestBtn.click();
- await grpcObj.sendBtn.click();
- const res = await grpcObj.jsonPretty.getText();
- return res;
- } catch(err) {
- console.error(err)
- }
- };
-
- it("it should work on a unary request", async () => {
- try {
- await grpcObj.selectServiceGreeter.click();
- await grpcObj.openRequestDropdown.click();
- await grpcObj.selectRequestSayHelloFromDropDown.click();
- const jsonPretty = await addReqAndSend();
- await new Promise((resolve) =>
- setTimeout(() => {
- expect(jsonPretty).to.include(`"message": "Hello string"`);
- resolve();
- }, 800)
- );
- } catch(err) {
- console.error(err)
- }
- });
-
- it("it should work on a nested unary request", async () => {
- try {
- await grpcObj.selectRequestSayHello.click();
- await grpcObj.selectRequestSayHelloNestedFromDropDown.click();
- const jsonPretty = await addReqAndSend();
- expect(jsonPretty).to.include('"serverMessage":')
- expect(jsonPretty).to.include('"message": "Hello! string"')
- const helloStrArray = jsonPretty.match(/"message": "Hello! string"/g)
- expect(helloStrArray).to.have.lengthOf(2);
- } catch(err) {
- console.error(err)
- }
- });
-
- it("it should work on a server stream", async () => {
- try {
- await grpcObj.selectRequestSayHelloNested.click();
- await grpcObj.selectRequestSayHellosSsFromDropDown.click();
- const jsonPretty = await addReqAndSend();
- expect(jsonPretty.match(/"message"/g)).to.have.lengthOf(5);
- expect(jsonPretty).to.include("hello!!! string")
- } catch(err) {
- console.error(err)
- }
- });
-
- it("it should work on a client stream", async () => {
- try {
- await grpcObj.selectRequestSayHellosSs.click();
- await grpcObj.selectRequestSayHelloCSFromDropDown.click();
- const jsonPretty = await addReqAndSend();
- expect(jsonPretty).to.include('"message": "received 1 messages"');
- } catch(err) {
- console.error(err)
- }
- });
- it("it should work on a bidirectional stream", async () => {
- try {
- await grpcObj.selectRequestSayHelloCS.click();
- await grpcObj.selectRequestBidiFromDropDown.click();
- const jsonPretty = await addReqAndSend();
- expect(jsonPretty).to.include('"message": "bidi stream: string"');
- } catch(err) {
- console.error(err)
- }
- });
- });
-};
\ No newline at end of file
+const chai = require('chai');
+const fs = require('fs');
+const path = require('path');
+const grpcObj = require('../pageObjects/GrpcObj.js');
+const grpcServer = require('../grpcServer.js');
+
+const { expect } = chai;
+
+module.exports = () => {
+ describe('gRPC requests', () => {
+ let proto = '';
+
+ before((done) => {
+ try {
+ fs.readFile(
+ path.join(__dirname, '../hw2.proto'),
+ 'utf8',
+ (err, data) => {
+ if (err) console.log(err);
+ proto = data;
+ done();
+ }
+ );
+ } catch (err) {
+ console.error(err);
+ }
+ });
+
+ before(async () => {
+ try {
+ grpcServer('open');
+ await composerSetup();
+ await grpcObj.openSelectServiceDropdown.click();
+ } catch (err) {
+ console.error(err);
+ }
+ });
+
+ after(async () => {
+ try {
+ await grpcObj.clearWorkspace.click();
+ } catch (err) {
+ console.log(err);
+ }
+ });
+
+ const composerSetup = async () => {
+ try {
+ await grpcObj.selectedNetwork.click();
+ await grpcObj.gRPCNetwork.click();
+ await grpcObj.url.addValue('0.0.0.0:30051');
+ await grpcObj.grpcProto.addValue(proto);
+ await grpcObj.saveChanges.click();
+ } catch (err) {
+ console.error(err);
+ }
+ };
+ const addReqAndSend = async () => {
+ try {
+ await grpcObj.addRequestBtn.click();
+ await grpcObj.sendBtn.click();
+ const res = await grpcObj.jsonPretty.getText();
+ return res;
+ } catch (err) {
+ console.error(err);
+ }
+ };
+
+ it('it should work on a unary request', async () => {
+ try {
+ await grpcObj.selectServiceGreeter.click();
+ await grpcObj.openRequestDropdown.click();
+ await grpcObj.selectRequestSayHelloFromDropDown.click();
+ const jsonPretty = await addReqAndSend();
+ await new Promise((resolve) =>
+ setTimeout(() => {
+ expect(jsonPretty).to.include(`"message": "Hello string"`);
+ resolve();
+ }, 800)
+ );
+ } catch (err) {
+ console.error(err);
+ }
+ });
+
+ it('it should work on a nested unary request', async () => {
+ try {
+ await grpcObj.selectRequestSayHello.click();
+ await grpcObj.selectRequestSayHelloNestedFromDropDown.click();
+ const jsonPretty = await addReqAndSend();
+ expect(jsonPretty).to.include('"serverMessage":');
+ expect(jsonPretty).to.include('"message": "Hello! string"');
+ const helloStrArray = jsonPretty.match(/"message": "Hello! string"/g);
+ expect(helloStrArray).to.have.lengthOf(2);
+ } catch (err) {
+ console.error(err);
+ }
+ });
+
+ it('it should work on a server stream', async () => {
+ try {
+ await grpcObj.selectRequestSayHelloNested.click();
+ await grpcObj.selectRequestSayHellosSsFromDropDown.click();
+ const jsonPretty = await addReqAndSend();
+ expect(jsonPretty.match(/"message"/g)).to.have.lengthOf(5);
+ expect(jsonPretty).to.include('hello!!! string');
+ } catch (err) {
+ console.error(err);
+ }
+ });
+
+ it('it should work on a client stream', async () => {
+ try {
+ await grpcObj.selectRequestSayHellosSs.click();
+ await grpcObj.selectRequestSayHelloCSFromDropDown.click();
+ const jsonPretty = await addReqAndSend();
+ expect(jsonPretty).to.include('"message": "received 1 messages"');
+ } catch (err) {
+ console.error(err);
+ }
+ });
+ it('it should work on a bidirectional stream', async () => {
+ try {
+ await grpcObj.selectRequestSayHelloCS.click();
+ await grpcObj.selectRequestBidiFromDropDown.click();
+ const jsonPretty = await addReqAndSend();
+ expect(jsonPretty).to.include('"message": "bidi stream: string"');
+ } catch (err) {
+ console.error(err);
+ }
+ });
+ });
+};
diff --git a/test/subSuites/grpcTestingTest.js b/test/subSuites/grpcTestingTest.js
index c04f64f1b..017f3d569 100644
--- a/test/subSuites/grpcTestingTest.js
+++ b/test/subSuites/grpcTestingTest.js
@@ -1,143 +1,143 @@
-const chai = require("chai");
-const fs = require("fs");
-const path = require("path");
-const grpcObj = require("../pageObjects/GrpcObj.js");
-const grpcServer = require("../grpcServer.js");
-const app = require("../testApp.js");
-
-const { expect } = chai;
-
-module.exports = () => {
- describe("gRPC Testing Controller", () => {
- // Store the text data from hw2.proto into a variable labeled proto.
- let proto = "";
-
- // These functions run before any of the "it" tests.
- before(async () => {
- // try {
- await grpcObj.selectedNetwork.click();
- await grpcObj.gRPCNetwork.click();
- // Read the data on the hw2.proto file.
- fs.readFile(path.join(__dirname, "../hw2.proto"), "utf8", (err, data) => {
- if (err) console.log(err);
- // Save the data to the proto file.
- proto = data;
- });
- // } catch (err) {
- // console.error(err);
- // }
- });
-
- //comment back in if testing grpcTestingTest on its own
- // before(async () => {
- // try {
- // // Clear the workspace.
- // // await grpcObj.removeBtn.click();
- // // Invoke the "main" function (to instantiate a server) from grpcServer.js
- // // grpcServer("open");
- // // Set up the composer with some boilerplate.
- // // await composerSetup();
- // // await grpcObj.openSelectServiceDropdown.click();
- // } catch (err) {
- // console.error(err);
- // }
- // });
-
- const composerSetup = async () => {
- try {
- await grpcObj.selectedNetwork.click();
- await grpcObj.gRPCNetwork.click();
- await grpcObj.url.addValue("0.0.0.0:30051");
- await grpcObj.grpcProto.addValue(proto);
- await grpcObj.saveChanges.click();
- } catch (err) {
- console.error(err);
- }
- };
-
- const addReqAndSend = async () => {
- try {
- // Adds request to the Workspace.
- await grpcObj.addRequestBtn.click();
- // Sends the request.
- await grpcObj.sendBtn.click();
- } catch (err) {
- console.error(err);
- }
- };
-
- // Bring in the Clear & Fill Test Script Area for improved code readability.
- const clearAndFillTestScriptArea = async (script) => {
- try {
- // click the view tests button to reveal the test code editor
- await app.client.$("span=View Tests").click();
- // set the value of the code editor to be some hard coded simple assertion tests
- await grpcObj.clearTestScriptAreaAndWriteKeys(script);
- // Close the tests view pane.
- await app.client.$("span=Hide Tests").click();
- } catch (err) {
- console.error(err);
- }
- };
-
- it("Basic testing functionality should work.", async () => {
- // await grpcObj.selectServiceGreeter.click();
- await grpcObj.selectRequestBidiButton.click();
- await grpcObj.selectRequestSayHelloFromDropDown.click();
- // Write the script, and add it to the tests inside of composer.
- const script = "assert.strictEqual(3, 3, 'Expect correct types.');";
- await clearAndFillTestScriptArea(script);
- await addReqAndSend();
- // Select the Tests column inside of Responses pane.
- await app.client.$("a=Tests").click();
- // Select the results of the first test, and check to see its status.
- const testStatus = await app.client.$("#TestResult-0-status").getText();
- // Check status.
- expect(testStatus).to.equal("PASS");
- await grpcObj.removeBtn.click();
- });
-
- it("Testing functionality for expects should pass.", async () => {
- const script =
- "expect([1, 2]).to.be.an('array').that.does.not.include(3);";
- await clearAndFillTestScriptArea(script);
- await addReqAndSend();
- await app.client.$("a=Tests").click();
- const testStatus = await app.client.$("#TestResult-0-status").getText();
- expect(testStatus).to.equal("PASS");
- await grpcObj.removeBtn.click();
- });
-
- it("Should access headers properties within the response object.", async () => {
- // Dot notation does not work with hyphenated names.
- const script =
- "assert.strictEqual(response.headers['content-type'], 'application/grpc+proto');";
- await clearAndFillTestScriptArea(script);
- await addReqAndSend();
- await app.client.$("a=Tests").click();
- const testStatus = await app.client.$("#TestResult-0-status").getText();
- expect(testStatus).to.equal("PASS");
- await grpcObj.removeBtn.click();
- });
-
- it("Should access events properties within the response object.", async () => {
- const script = `assert.strictEqual(response.events[0].message, "Hello string");`;
- await clearAndFillTestScriptArea(script);
- await addReqAndSend();
- await app.client.$("a=Tests").click();
- const testStatus = await app.client.$("#TestResult-0-status").getText();
- expect(testStatus).to.equal("PASS");
- await grpcObj.removeBtn.click();
- });
-
- it("Should handle multiple variable declarations and newlines.", async () => {
- const script = `const grpcTestVariable = "Hello string"; \nassert.strictEqual(response.events[0].message, grpcTestVariable)`;
- await clearAndFillTestScriptArea(script);
- await addReqAndSend();
- await app.client.$("a=Tests").click();
- const testStatus = await app.client.$("#TestResult-0-status").getText();
- expect(testStatus).to.equal("PASS");
- await grpcObj.removeBtn.click();
- });
- });
-};
+const chai = require('chai');
+const fs = require('fs');
+const path = require('path');
+const grpcObj = require('../pageObjects/GrpcObj.js');
+const grpcServer = require('../grpcServer.js');
+const app = require('../testApp.js');
+
+const { expect } = chai;
+
+module.exports = () => {
+ describe('gRPC Testing Controller', () => {
+ // Store the text data from hw2.proto into a variable labeled proto.
+ let proto = '';
+
+ // These functions run before any of the "it" tests.
+ before(async () => {
+ // try {
+ await grpcObj.selectedNetwork.click();
+ await grpcObj.gRPCNetwork.click();
+ // Read the data on the hw2.proto file.
+ fs.readFile(path.join(__dirname, '../hw2.proto'), 'utf8', (err, data) => {
+ if (err) console.log(err);
+ // Save the data to the proto file.
+ proto = data;
+ });
+ // } catch (err) {
+ // console.error(err);
+ // }
+ });
+
+ // comment back in if testing grpcTestingTest on its own
+ // before(async () => {
+ // try {
+ // // Clear the workspace.
+ // // await grpcObj.removeBtn.click();
+ // // Invoke the "main" function (to instantiate a server) from grpcServer.js
+ // // grpcServer("open");
+ // // Set up the composer with some boilerplate.
+ // // await composerSetup();
+ // // await grpcObj.openSelectServiceDropdown.click();
+ // } catch (err) {
+ // console.error(err);
+ // }
+ // });
+
+ const composerSetup = async () => {
+ try {
+ await grpcObj.selectedNetwork.click();
+ await grpcObj.gRPCNetwork.click();
+ await grpcObj.url.addValue('0.0.0.0:30051');
+ await grpcObj.grpcProto.addValue(proto);
+ await grpcObj.saveChanges.click();
+ } catch (err) {
+ console.error(err);
+ }
+ };
+
+ const addReqAndSend = async () => {
+ try {
+ // Adds request to the Workspace.
+ await grpcObj.addRequestBtn.click();
+ // Sends the request.
+ await grpcObj.sendBtn.click();
+ } catch (err) {
+ console.error(err);
+ }
+ };
+
+ // Bring in the Clear & Fill Test Script Area for improved code readability.
+ const clearAndFillTestScriptArea = async (script) => {
+ try {
+ // click the view tests button to reveal the test code editor
+ await app.client.$('span=View Tests').click();
+ // set the value of the code editor to be some hard coded simple assertion tests
+ await grpcObj.clearTestScriptAreaAndWriteKeys(script);
+ // Close the tests view pane.
+ await app.client.$('span=Hide Tests').click();
+ } catch (err) {
+ console.error(err);
+ }
+ };
+
+ it('Basic testing functionality should work.', async () => {
+ // await grpcObj.selectServiceGreeter.click();
+ await grpcObj.selectRequestBidiButton.click();
+ await grpcObj.selectRequestSayHelloFromDropDown.click();
+ // Write the script, and add it to the tests inside of composer.
+ const script = "assert.strictEqual(3, 3, 'Expect correct types.');";
+ await clearAndFillTestScriptArea(script);
+ await addReqAndSend();
+ // Select the Tests column inside of Responses pane.
+ await app.client.$('a=Tests').click();
+ // Select the results of the first test, and check to see its status.
+ const testStatus = await app.client.$('#TestResult-0-status').getText();
+ // Check status.
+ expect(testStatus).to.equal('PASS');
+ await grpcObj.removeBtn.click();
+ });
+
+ it('Testing functionality for expects should pass.', async () => {
+ const script =
+ "expect([1, 2]).to.be.an('array').that.does.not.include(3);";
+ await clearAndFillTestScriptArea(script);
+ await addReqAndSend();
+ await app.client.$('a=Tests').click();
+ const testStatus = await app.client.$('#TestResult-0-status').getText();
+ expect(testStatus).to.equal('PASS');
+ await grpcObj.removeBtn.click();
+ });
+
+ it('Should access headers properties within the response object.', async () => {
+ // Dot notation does not work with hyphenated names.
+ const script =
+ "assert.strictEqual(response.headers['content-type'], 'application/grpc+proto');";
+ await clearAndFillTestScriptArea(script);
+ await addReqAndSend();
+ await app.client.$('a=Tests').click();
+ const testStatus = await app.client.$('#TestResult-0-status').getText();
+ expect(testStatus).to.equal('PASS');
+ await grpcObj.removeBtn.click();
+ });
+
+ it('Should access events properties within the response object.', async () => {
+ const script = `assert.strictEqual(response.events[0].message, "Hello string");`;
+ await clearAndFillTestScriptArea(script);
+ await addReqAndSend();
+ await app.client.$('a=Tests').click();
+ const testStatus = await app.client.$('#TestResult-0-status').getText();
+ expect(testStatus).to.equal('PASS');
+ await grpcObj.removeBtn.click();
+ });
+
+ it('Should handle multiple variable declarations and newlines.', async () => {
+ const script = `const grpcTestVariable = "Hello string"; \nassert.strictEqual(response.events[0].message, grpcTestVariable)`;
+ await clearAndFillTestScriptArea(script);
+ await addReqAndSend();
+ await app.client.$('a=Tests').click();
+ const testStatus = await app.client.$('#TestResult-0-status').getText();
+ expect(testStatus).to.equal('PASS');
+ await grpcObj.removeBtn.click();
+ });
+ });
+};
diff --git a/test/subSuites/httpTest.js b/test/subSuites/httpTest.js
index a5e0cc11e..79b3cc0d5 100644
--- a/test/subSuites/httpTest.js
+++ b/test/subSuites/httpTest.js
@@ -1,233 +1,260 @@
-const chai = require("chai");
-const chaiHttp = require("chai-http");
-const app = require('../testApp.js');
-const composerObj = require('../pageObjects/ComposerObj.js');
-const workspaceObj = require('../pageObjects/WorkspaceObj.js');
-const httpServer = require('../httpServer');
-
-chai.use(chaiHttp);
-const expect = chai.expect;
-
-module.exports = () => {
- describe("HTTP/S requests", () => {
- const fillRestRequest = async (url, method, body = '', headers = [], cookies = []) => {
-
- try {
-
- // click and check REST
- await composerObj.selectedNetwork.click();
- await app.client.$('a=REST').click();
-
- // click and select METHOD if it isn't GET
- if(method !== 'GET') {
- await app.client.$('span=GET').click();
- await app.client.$(`a=${method}`).click();
- }
-
- // type in url
- await composerObj.url.setValue(url);
-
- // set headers
- headers.forEach(async ({ key, value },index) => {
- await app.client.$(`//*[@id="header-row${index}"]/input[1]`).setValue(key);
- await app.client.$(`//*[@id="header-row${index}"]/input[2]`).setValue(value);
- await app.client.$('button=+ Header').click();
- });
-
- // set cookies
- cookies.forEach(async ({ key, value },index) => {
- await app.client.$(`//*[@id="cookie-row${index}"]/input[1]`).setValue(key);
- await app.client.$(`//*[@id="cookie-row${index}"]/input[2]`).setValue(value);
- await app.client.$('button=+ Cookie').click();
- });
-
- // Add BODY as JSON if it isn't GET
- if (method !== "GET") {
- // select body type JSON
- await app.client.$('span=text/plain').click();
- await app.client.$('a=application/json').click();
- // insert JSON content into body
- await composerObj.clearRestBodyAndWriteKeys(body);
- }
- } catch(err) {
- console.error(err)
- }
- };
-
- const addAndSend = async () => {
- try {
- await composerObj.addRequestBtn.click();
- await workspaceObj.latestSendRequestBtn.click();
- } catch(err) {
- console.error(err);
- }
- }
-
- describe("public API", () => {
- it("it should GET information from a public API", async () => {
- try {
- // TEST GET Request from JSON Placeholder
- const url = "http://jsonplaceholder.typicode.com/posts";
- const method = 'GET';
- await fillRestRequest(url, method);
- await addAndSend();
- await new Promise((resolve) =>
- setTimeout(async () => {
- const statusCode = await app.client.$('.status-tag').getText();
- const events = await app.client.$('#events-display .CodeMirror-code').getText();
- expect(statusCode).to.equal("200");
- expect(events.slice(1,100)).to.include("userId");
- resolve();
- }, 500)
- );
- } catch(err) {
- console.error(err)
- }
- });
- });
-
- /***************** !! FOR BELOW TO WORK, YOU MUST ADD YOUR OWN MONGO URI TO A .ENV FILE WITH (MONGO_URI = "YOUR_URI") !! *****************/
- describe("httpTest Server", () => {
-
- before("CLEAR DB", (done) => {
- chai.request("http://localhost:3000")
- .get("/clear")
- .end(function(err, res) {
- done(); // <= Call done to signal callback end
- });
- });
-
- after("CLEAR DB", (done) => {
- chai.request("http://localhost:3000")
- .get("/clear")
- .send()
- .end(function(err, res) {
- done(); // <= Call done to signal callback end
- });
- });
-
- it("it should GET information from an http test server", async () => {
- try {
- const url = "http://localhost:3000/book";
- const method = 'GET';
- await fillRestRequest(url, method);
- await addAndSend();
- await new Promise((resolve) =>
- setTimeout(async () => {
- const statusCode = await app.client.$('.status-tag').getText();
- const events = await app.client.$('#events-display .CodeMirror-code').getText();
- expect(statusCode).to.equal("200");
- expect(events).to.include("[]");
- resolve();
- }, 500)
- );
- } catch(err) {
- console.error(err)
- }
- });
-
- it("it should POST to local http test server", async () => {
- try {
- const url = "http://localhost:3000/book";
- const method = 'POST';
- const body = '"title": "HarryPotter", "author": "JK Rowling", "pages": 500}'
- await fillRestRequest(url, method, body);
- await addAndSend();
- await new Promise((resolve) =>
- setTimeout(async () => {
- const statusCode = await app.client.$('.status-tag').getText();
- const events = await app.client.$('#events-display .CodeMirror-code').getText();
- expect(statusCode).to.equal("200");
- expect(events).to.include("JK Rowling");
- resolve();
- }, 500)
- );
- } catch(err) {
- console.error(err)
- }
- });
-
- it("it should PUT to local http test server", async () => {
- try {
- const url = "http://localhost:3000/book/HarryPotter";
- const method = 'PUT';
- const body = '"author": "Ron Weasley", "pages": 400}';
- await fillRestRequest(url, method, body);
- await addAndSend();
- await new Promise((resolve) =>
- setTimeout(async () => {
- const statusCode = await app.client.$('.status-tag').getText();
- const events = await app.client.$('#events-display .CodeMirror-code').getText();
- expect(statusCode).to.equal("200");
- expect(events).to.include("Ron Weasley");
- resolve();
- }, 500)
- );
- } catch(err) {
- console.error(err)
- }
- });
-
- it("it should PATCH to local http test server", async () => {
- try {
- const url = "http://localhost:3000/book/HarryPotter";
- const method = 'PATCH';
- const body = '"author": "Hermoine Granger"}';
- await fillRestRequest(url, method, body);
- await addAndSend();
- await new Promise((resolve) =>
- setTimeout(async () => {
- const statusCode = await app.client.$('.status-tag').getText();
- const events = await app.client.$('#events-display .CodeMirror-code').getText();
- expect(statusCode).to.equal("200");
- expect(events).to.include("Hermoine Granger");
- resolve();
- }, 500)
- );
- } catch(err) {
- console.error(err)
- }
- });
-
- it("it should DELETE to local http test server", async () => {
- // DELETE HARRYPOTTER
- try {
- const url = "http://localhost:3000/book/HarryPotter";
- const method = 'DELETE';
- await fillRestRequest(url, method);
- await addAndSend();
- await new Promise((resolve) =>
- setTimeout(async () => {
- const statusCode = await app.client.$('.status-tag').getText();
- const events = await app.client.$('#events-display .CodeMirror-code').getText();
- expect(statusCode).to.equal("200");
- expect(events).to.include("Hermoine Granger");
- resolve();
- }, 500)
- );
- } catch(err) {
- console.error(err)
- }
- // CHECK TO SEE IF IT IS DELETED
- try {
- const url = "http://localhost:3000/book";
- const method = 'GET';
- await fillRestRequest(url, method);
- await addAndSend();
- await new Promise((resolve) =>
- setTimeout(async () => {
- const statusCode = await app.client.$('.status-tag').getText();
- const events = await app.client.$('#events-display .CodeMirror-code').getText();
- expect(statusCode).to.equal("200");
- expect(events).to.include("[]");
- resolve();
- }, 500)
- );
- } catch(err) {
- console.error(err)
- }
- });
-
- });
- });
-};
+const chai = require('chai');
+const chaiHttp = require('chai-http');
+const app = require('../testApp.js');
+const composerObj = require('../pageObjects/ComposerObj.js');
+const workspaceObj = require('../pageObjects/WorkspaceObj.js');
+const httpServer = require('../httpServer');
+
+chai.use(chaiHttp);
+const { expect } = chai;
+
+module.exports = () => {
+ describe('HTTP/S requests', () => {
+ const fillRestRequest = async (
+ url,
+ method,
+ body = '',
+ headers = [],
+ cookies = []
+ ) => {
+ try {
+ // click and check REST
+ await composerObj.selectedNetwork.click();
+ await app.client.$('a=REST').click();
+
+ // click and select METHOD if it isn't GET
+ if (method !== 'GET') {
+ await app.client.$('span=GET').click();
+ await app.client.$(`a=${method}`).click();
+ }
+
+ // type in url
+ await composerObj.url.setValue(url);
+
+ // set headers
+ headers.forEach(async ({ key, value }, index) => {
+ await app.client
+ .$(`//*[@id="header-row${index}"]/input[1]`)
+ .setValue(key);
+ await app.client
+ .$(`//*[@id="header-row${index}"]/input[2]`)
+ .setValue(value);
+ await app.client.$('button=+ Header').click();
+ });
+
+ // set cookies
+ cookies.forEach(async ({ key, value }, index) => {
+ await app.client
+ .$(`//*[@id="cookie-row${index}"]/input[1]`)
+ .setValue(key);
+ await app.client
+ .$(`//*[@id="cookie-row${index}"]/input[2]`)
+ .setValue(value);
+ await app.client.$('button=+ Cookie').click();
+ });
+
+ // Add BODY as JSON if it isn't GET
+ if (method !== 'GET') {
+ // select body type JSON
+ await app.client.$('span=text/plain').click();
+ await app.client.$('a=application/json').click();
+ // insert JSON content into body
+ await composerObj.clearRestBodyAndWriteKeys(body);
+ }
+ } catch (err) {
+ console.error(err);
+ }
+ };
+
+ const addAndSend = async () => {
+ try {
+ await composerObj.addRequestBtn.click();
+ await workspaceObj.latestSendRequestBtn.click();
+ } catch (err) {
+ console.error(err);
+ }
+ };
+
+ describe('public API', () => {
+ it('it should GET information from a public API', async () => {
+ try {
+ // TEST GET Request from JSON Placeholder
+ const url = 'http://jsonplaceholder.typicode.com/posts';
+ const method = 'GET';
+ await fillRestRequest(url, method);
+ await addAndSend();
+ await new Promise((resolve) =>
+ setTimeout(async () => {
+ const statusCode = await app.client.$('.status-tag').getText();
+ const events = await app.client
+ .$('#events-display .CodeMirror-code')
+ .getText();
+ expect(statusCode).to.equal('200');
+ expect(events.slice(1, 100)).to.include('userId');
+ resolve();
+ }, 500)
+ );
+ } catch (err) {
+ console.error(err);
+ }
+ });
+ });
+
+ /** *************** !! FOR BELOW TO WORK, YOU MUST ADD YOUR OWN MONGO URI TO A .ENV FILE WITH (MONGO_URI = "YOUR_URI") !! **************** */
+ describe('httpTest Server', () => {
+ before('CLEAR DB', (done) => {
+ chai
+ .request('http://localhost:3000')
+ .get('/clear')
+ .end((err, res) => {
+ done(); // <= Call done to signal callback end
+ });
+ });
+
+ after('CLEAR DB', (done) => {
+ chai
+ .request('http://localhost:3000')
+ .get('/clear')
+ .send()
+ .end((err, res) => {
+ done(); // <= Call done to signal callback end
+ });
+ });
+
+ it('it should GET information from an http test server', async () => {
+ try {
+ const url = 'http://localhost:3000/book';
+ const method = 'GET';
+ await fillRestRequest(url, method);
+ await addAndSend();
+ await new Promise((resolve) =>
+ setTimeout(async () => {
+ const statusCode = await app.client.$('.status-tag').getText();
+ const events = await app.client
+ .$('#events-display .CodeMirror-code')
+ .getText();
+ expect(statusCode).to.equal('200');
+ expect(events).to.include('[]');
+ resolve();
+ }, 500)
+ );
+ } catch (err) {
+ console.error(err);
+ }
+ });
+
+ it('it should POST to local http test server', async () => {
+ try {
+ const url = 'http://localhost:3000/book';
+ const method = 'POST';
+ const body =
+ '"title": "HarryPotter", "author": "JK Rowling", "pages": 500}';
+ await fillRestRequest(url, method, body);
+ await addAndSend();
+ await new Promise((resolve) =>
+ setTimeout(async () => {
+ const statusCode = await app.client.$('.status-tag').getText();
+ const events = await app.client
+ .$('#events-display .CodeMirror-code')
+ .getText();
+ expect(statusCode).to.equal('200');
+ expect(events).to.include('JK Rowling');
+ resolve();
+ }, 500)
+ );
+ } catch (err) {
+ console.error(err);
+ }
+ });
+
+ it('it should PUT to local http test server', async () => {
+ try {
+ const url = 'http://localhost:3000/book/HarryPotter';
+ const method = 'PUT';
+ const body = '"author": "Ron Weasley", "pages": 400}';
+ await fillRestRequest(url, method, body);
+ await addAndSend();
+ await new Promise((resolve) =>
+ setTimeout(async () => {
+ const statusCode = await app.client.$('.status-tag').getText();
+ const events = await app.client
+ .$('#events-display .CodeMirror-code')
+ .getText();
+ expect(statusCode).to.equal('200');
+ expect(events).to.include('Ron Weasley');
+ resolve();
+ }, 500)
+ );
+ } catch (err) {
+ console.error(err);
+ }
+ });
+
+ it('it should PATCH to local http test server', async () => {
+ try {
+ const url = 'http://localhost:3000/book/HarryPotter';
+ const method = 'PATCH';
+ const body = '"author": "Hermoine Granger"}';
+ await fillRestRequest(url, method, body);
+ await addAndSend();
+ await new Promise((resolve) =>
+ setTimeout(async () => {
+ const statusCode = await app.client.$('.status-tag').getText();
+ const events = await app.client
+ .$('#events-display .CodeMirror-code')
+ .getText();
+ expect(statusCode).to.equal('200');
+ expect(events).to.include('Hermoine Granger');
+ resolve();
+ }, 500)
+ );
+ } catch (err) {
+ console.error(err);
+ }
+ });
+
+ it('it should DELETE to local http test server', async () => {
+ // DELETE HARRYPOTTER
+ try {
+ const url = 'http://localhost:3000/book/HarryPotter';
+ const method = 'DELETE';
+ await fillRestRequest(url, method);
+ await addAndSend();
+ await new Promise((resolve) =>
+ setTimeout(async () => {
+ const statusCode = await app.client.$('.status-tag').getText();
+ const events = await app.client
+ .$('#events-display .CodeMirror-code')
+ .getText();
+ expect(statusCode).to.equal('200');
+ expect(events).to.include('Hermoine Granger');
+ resolve();
+ }, 500)
+ );
+ } catch (err) {
+ console.error(err);
+ }
+ // CHECK TO SEE IF IT IS DELETED
+ try {
+ const url = 'http://localhost:3000/book';
+ const method = 'GET';
+ await fillRestRequest(url, method);
+ await addAndSend();
+ await new Promise((resolve) =>
+ setTimeout(async () => {
+ const statusCode = await app.client.$('.status-tag').getText();
+ const events = await app.client
+ .$('#events-display .CodeMirror-code')
+ .getText();
+ expect(statusCode).to.equal('200');
+ expect(events).to.include('[]');
+ resolve();
+ }, 500)
+ );
+ } catch (err) {
+ console.error(err);
+ }
+ });
+ });
+ });
+};
diff --git a/test/subSuites/httpTestingTest.js b/test/subSuites/httpTestingTest.js
index dcd16c12b..8f8be7b7c 100644
--- a/test/subSuites/httpTestingTest.js
+++ b/test/subSuites/httpTestingTest.js
@@ -1,387 +1,387 @@
-const chai = require("chai");
-const chaiHttp = require("chai-http");
-const app = require("../testApp.js");
-const composerObj = require("../pageObjects/ComposerObj");
-const workspaceObj = require("../pageObjects/WorkspaceObj");
-
-const { expect } = chai;
-
-module.exports = () => {
- describe("HTTP Testing Controller", function () {
- const fillRestRequest = async (
- url,
- method,
- body = "",
- headers = [],
- cookies = []
- ) => {
- try {
- // click and check REST
- await composerObj.selectedNetwork.click();
- await app.client.$("a=REST").click();
-
- // click and select METHOD if it isn't GET
- if (method !== "GET") {
- await app.client.$("span=GET").click();
- await app.client.$(`a=${method}`).click();
- }
-
- // type in url
- await composerObj.url.setValue(url);
-
- // set headers
- headers.forEach(async ({ key, value }, index) => {
- await app.client
- .$(`//*[@id="header-row${index}"]/input[1]`)
- .setValue(key);
- await app.client
- .$(`//*[@id="header-row${index}"]/input[2]`)
- .setValue(value);
- await app.client.$("button=+ Header").click();
- });
-
- // set cookies
- cookies.forEach(async ({ key, value }, index) => {
- await app.client
- .$(`//*[@id="cookie-row${index}"]/input[1]`)
- .setValue(key);
- await app.client
- .$(`//*[@id="cookie-row${index}"]/input[2]`)
- .setValue(value);
- await app.client.$("button=+ Cookie").click();
- });
-
- // Add BODY as JSON if it isn't GET
- if (method !== "GET") {
- // select body type JSON
- await app.client.$("span=text/plain").click();
- await app.client.$("a=application/json").click();
- // insert JSON content into body
- await composerObj.clearRestBodyAndWriteKeys(body);
- }
- } catch (err) {
- console.error(err);
- }
- };
-
- const addAndSend = async () => {
- try {
- await composerObj.addRequestBtn.click();
- await workspaceObj.latestSendRequestBtn.click();
- } catch (err) {
- console.error(err);
- }
- };
-
- const clearAndFillTestScriptArea = async (script) => {
- try {
- // click the view tests button to reveal the test code editor
- await app.client.$("span=View Tests").click();
- // set the value of the code editor to be some hard coded simple assertion tests
- await composerObj.clearTestScriptAreaAndWriteKeys(script);
- } catch (err) {
- console.error(err);
- }
- };
-
- afterEach("HIDE TESTS", (done) => {
- (async () => {
- await app.client.$("span=Hide Tests").click();
- })();
- done();
- });
-
- // ==================================================================
-
- describe("simple assertions w/ chai.assert and chai.expect", function () {
- before("CLEAR DB", (done) => {
- chai
- .request("http://localhost:3000")
- .get("/clear")
- .end(function (err, res) {
- done(); // <= Call done to signal callback end
- });
- });
-
- after("CLEAR DB", (done) => {
- chai
- .request("http://localhost:3000")
- .get("/clear")
- .send()
- .end(function (err, res) {
- done(); // <= Call done to signal callback end
- });
- });
-
- it("a simple assertion (assert) should PASS when making a GET request", async function () {
- const url = "http://localhost:3000/book";
- const method = "GET";
- const script = "assert.strictEqual(3, 3, 'correct types');";
- await fillRestRequest(url, method);
- await clearAndFillTestScriptArea(script);
- await addAndSend();
-
- await app.client.$("a=Tests").click();
-
- const testStatus = await app.client.$("#TestResult-0-status").getText();
- expect(testStatus).to.equal("PASS");
- });
-
- it("a simple assertion (assert) should FAIL when making a GET request", async function () {
- const url = "http://localhost:3000/book";
- const method = "GET";
- const script = "assert.strictEqual(3, '3', 'wrong types');";
- await fillRestRequest(url, method);
- await clearAndFillTestScriptArea(script);
- await addAndSend();
-
- await app.client.$("a=Tests").click();
- const testStatus = await app.client.$("#TestResult-0-status").getText();
- expect(testStatus).to.equal("FAIL");
- });
-
- it("a simple assertion (expect) should PASS when making a GET request", async function () {
- const url = "http://localhost:3000/book";
- const method = "GET";
- const script = "expect(3, 'correct types').to.equal(3);";
- await fillRestRequest(url, method);
- await clearAndFillTestScriptArea(script);
- await addAndSend();
-
- await app.client.$("a=Tests").click();
- const testStatus = await app.client.$("#TestResult-0-status").getText();
- expect(testStatus).to.equal("PASS");
- });
-
- it("a simple assertion (expect) should FAIL when making a GET request", async function () {
- const url = "http://localhost:3000/book";
- const method = "GET";
- const script = "expect(3, 'correct types').to.equal('3');";
- await fillRestRequest(url, method);
- await clearAndFillTestScriptArea(script);
- await addAndSend();
-
- await app.client.$("a=Tests").click();
- const testStatus = await app.client.$("#TestResult-0-status").getText();
- expect(testStatus).to.equal("FAIL");
- });
- });
-
- describe("Multiple assertion statements", function () {
- it("should handle multiple different simple assert statements", async function () {
- const url = "http://localhost:3000/book";
- const method = "GET";
- const script =
- "assert.strictEqual(3, '3', 'wrong types');\nassert.strictEqual(3, '3', 'this assert is a message');\nassert.strictEqual(3, 3, 'correct types');\nassert.strictEqual(3, 3, 'this assert is a message');";
- await fillRestRequest(url, method);
- await clearAndFillTestScriptArea(script);
- await addAndSend();
-
- await app.client.$("a=Tests").click();
- const firstStatus = await app.client
- .$("#TestResult-0-status")
- .getText();
- const secondStatus = await app.client
- .$("#TestResult-1-status")
- .getText();
- const thirdStatus = await app.client
- .$("#TestResult-2-status")
- .getText();
- const fourthStatus = await app.client
- .$("#TestResult-3-status")
- .getText();
-
- expect(firstStatus).to.equal("FAIL");
- expect(secondStatus).to.equal("FAIL");
- expect(thirdStatus).to.equal("PASS");
- expect(fourthStatus).to.equal("PASS");
- });
-
- it("should handle multiple different simple expect statements", async function () {
- const url = "http://localhost:3000/book";
- const method = "GET";
- const script =
- "expect(3, 'wrong types').to.equal('3');\nexpect(3, 'this expect is a message').to.equal('3');\nexpect(3, 'correct types').to.equal(3);\nexpect(3, 'this expect is a message').to.equal(3);";
- await fillRestRequest(url, method);
- await clearAndFillTestScriptArea(script);
- await addAndSend();
-
- await app.client.$("a=Tests").click();
- const firstStatus = await app.client
- .$("#TestResult-0-status")
- .getText();
- const secondStatus = await app.client
- .$("#TestResult-1-status")
- .getText();
- const thirdStatus = await app.client
- .$("#TestResult-2-status")
- .getText();
- const fourthStatus = await app.client
- .$("#TestResult-3-status")
- .getText();
-
- expect(firstStatus).to.equal("FAIL");
- expect(secondStatus).to.equal("FAIL");
- expect(thirdStatus).to.equal("PASS");
- expect(fourthStatus).to.equal("PASS");
- });
- });
-
- describe("Assertions on response object", function () {
- it("chai.assert: should be able to access the response object", async function () {
- const url = "http://localhost:3000/book";
- const method = "GET";
- const script = "assert.exists(response, 'response is object');";
- await fillRestRequest(url, method);
- await clearAndFillTestScriptArea(script);
- await addAndSend();
-
- await app.client.$("a=Tests").click();
-
- const testStatus = await app.client.$("#TestResult-0-status").getText();
- expect(testStatus).to.equal("PASS");
- });
-
- it("chai.assert: should be able to access the status code from the response object", async function () {
- const url = "http://localhost:3000/book";
- const method = "GET";
- const script =
- "assert.strictEqual(response.status, 200, 'response is 200');";
- await fillRestRequest(url, method);
- await clearAndFillTestScriptArea(script);
- await addAndSend();
-
- await app.client.$("a=Tests").click();
-
- const testStatus = await app.client.$("#TestResult-0-status").getText();
- expect(testStatus).to.equal("PASS");
- });
-
- it("chai.assert: should be able to access the cookies from the response object", async function () {
- const url = "http://google.com";
- const method = "GET";
- const script =
- "assert.exists(response.cookies, 'cookies exists on response object');";
- await fillRestRequest(url, method);
- await clearAndFillTestScriptArea(script);
- await addAndSend();
-
- await app.client.$("a=Tests").click();
- const testStatus = await new Promise((resolve) => {
- setTimeout(async () => {
- const text = await app.client.$("#TestResult-0-status").getText();
- resolve(text);
- }, 500);
- // block for 500 ms since we need to wait for a response; normally test server would
- // respond fast enough since it is localhost and not a remote server
- });
-
- expect(testStatus).to.equal("PASS");
- });
-
- it("chai.assert: should be able to access the headers from the response object", async function () {
- const url = "http://google.com";
- const method = "GET";
- const script =
- "assert.exists(response.headers, 'headers exists on response object');";
- await fillRestRequest(url, method);
- await clearAndFillTestScriptArea(script);
- await addAndSend();
-
- await app.client.$("a=Tests").click();
- const testStatus = await new Promise((resolve) => {
- setTimeout(async () => {
- const text = await app.client.$("#TestResult-0-status").getText();
- resolve(text);
- }, 500);
- });
-
- expect(testStatus).to.equal("PASS");
- });
-
- it("chai.expect: should be able to access the response object", async function () {
- const url = "http://localhost:3000/book";
- const method = "GET";
- const script = "expect(response, 'response exists').to.exist;";
- await fillRestRequest(url, method);
- await clearAndFillTestScriptArea(script);
- await addAndSend();
-
- await app.client.$("a=Tests").click();
- const testStatus = await app.client.$("#TestResult-0-status").getText();
- expect(testStatus).to.equal("PASS");
- });
-
- it("chai.expect: should be able to access the status code from the response object", async function () {
- const url = "http://localhost:3000/book";
- const method = "GET";
- const script =
- "expect(response.status, 'response is 200').to.equal(200);";
- await fillRestRequest(url, method);
- await clearAndFillTestScriptArea(script);
- await addAndSend();
-
- await app.client.$("a=Tests").click();
- const testStatus = await app.client.$("#TestResult-0-status").getText();
- expect(testStatus).to.equal("PASS");
- });
-
- it("chai.expect: should be able to access the cookies from the response object", async function () {
- const url = "http://google.com";
- const method = "GET";
- const script =
- "expect(response.cookies, 'cookies exists on response object').to.exist;";
- await fillRestRequest(url, method);
- await clearAndFillTestScriptArea(script);
- await addAndSend();
-
- await app.client.$("a=Tests").click();
-
- const testStatus = await new Promise((resolve) => {
- setTimeout(async () => {
- const text = await app.client.$("#TestResult-0-status").getText();
- resolve(text);
- }, 500);
- });
-
- expect(testStatus).to.equal("PASS");
- });
-
- it("chai.expect: should be able to access the headers from the response object", async function () {
- const url = "http://google.com";
- const method = "GET";
- const script =
- "expect(response.headers, 'headers exists on reponse object').to.exist;";
- await fillRestRequest(url, method);
- await clearAndFillTestScriptArea(script);
- await addAndSend();
-
- await app.client.$("a=Tests").click();
-
- const testStatus = await new Promise((resolve) => {
- setTimeout(async () => {
- const text = await app.client.$("#TestResult-0-status").getText();
- resolve(text);
- }, 500);
- });
-
- expect(testStatus).to.equal("PASS");
- });
- });
-
- describe("Using variables", function () {
- it("Test results do not render if JavaScript is entered but specifically not assertion tests", async function () {
- const url = "http://localhost:3000/book";
- const method = "GET";
- const script = "const foo = 'bar';";
- await fillRestRequest(url, method);
- await clearAndFillTestScriptArea(script);
- await addAndSend();
-
- await app.client.$("a=Tests").click();
- const { selector } = await app.client.$(".empty-state-wrapper");
- expect(selector).to.equal(".empty-state-wrapper");
- });
- });
- });
-};
+const chai = require('chai');
+const chaiHttp = require('chai-http');
+const app = require('../testApp.js');
+const composerObj = require('../pageObjects/ComposerObj');
+const workspaceObj = require('../pageObjects/WorkspaceObj');
+
+const { expect } = chai;
+
+module.exports = () => {
+ describe('HTTP Testing Controller', () => {
+ const fillRestRequest = async (
+ url,
+ method,
+ body = '',
+ headers = [],
+ cookies = []
+ ) => {
+ try {
+ // click and check REST
+ await composerObj.selectedNetwork.click();
+ await app.client.$('a=REST').click();
+
+ // click and select METHOD if it isn't GET
+ if (method !== 'GET') {
+ await app.client.$('span=GET').click();
+ await app.client.$(`a=${method}`).click();
+ }
+
+ // type in url
+ await composerObj.url.setValue(url);
+
+ // set headers
+ headers.forEach(async ({ key, value }, index) => {
+ await app.client
+ .$(`//*[@id="header-row${index}"]/input[1]`)
+ .setValue(key);
+ await app.client
+ .$(`//*[@id="header-row${index}"]/input[2]`)
+ .setValue(value);
+ await app.client.$('button=+ Header').click();
+ });
+
+ // set cookies
+ cookies.forEach(async ({ key, value }, index) => {
+ await app.client
+ .$(`//*[@id="cookie-row${index}"]/input[1]`)
+ .setValue(key);
+ await app.client
+ .$(`//*[@id="cookie-row${index}"]/input[2]`)
+ .setValue(value);
+ await app.client.$('button=+ Cookie').click();
+ });
+
+ // Add BODY as JSON if it isn't GET
+ if (method !== 'GET') {
+ // select body type JSON
+ await app.client.$('span=text/plain').click();
+ await app.client.$('a=application/json').click();
+ // insert JSON content into body
+ await composerObj.clearRestBodyAndWriteKeys(body);
+ }
+ } catch (err) {
+ console.error(err);
+ }
+ };
+
+ const addAndSend = async () => {
+ try {
+ await composerObj.addRequestBtn.click();
+ await workspaceObj.latestSendRequestBtn.click();
+ } catch (err) {
+ console.error(err);
+ }
+ };
+
+ const clearAndFillTestScriptArea = async (script) => {
+ try {
+ // click the view tests button to reveal the test code editor
+ await app.client.$('span=View Tests').click();
+ // set the value of the code editor to be some hard coded simple assertion tests
+ await composerObj.clearTestScriptAreaAndWriteKeys(script);
+ } catch (err) {
+ console.error(err);
+ }
+ };
+
+ afterEach('HIDE TESTS', (done) => {
+ (async () => {
+ await app.client.$('span=Hide Tests').click();
+ })();
+ done();
+ });
+
+ // ==================================================================
+
+ describe('simple assertions w/ chai.assert and chai.expect', () => {
+ before('CLEAR DB', (done) => {
+ chai
+ .request('http://localhost:3000')
+ .get('/clear')
+ .end((err, res) => {
+ done(); // <= Call done to signal callback end
+ });
+ });
+
+ after('CLEAR DB', (done) => {
+ chai
+ .request('http://localhost:3000')
+ .get('/clear')
+ .send()
+ .end((err, res) => {
+ done(); // <= Call done to signal callback end
+ });
+ });
+
+ it('a simple assertion (assert) should PASS when making a GET request', async () => {
+ const url = 'http://localhost:3000/book';
+ const method = 'GET';
+ const script = "assert.strictEqual(3, 3, 'correct types');";
+ await fillRestRequest(url, method);
+ await clearAndFillTestScriptArea(script);
+ await addAndSend();
+
+ await app.client.$('a=Tests').click();
+
+ const testStatus = await app.client.$('#TestResult-0-status').getText();
+ expect(testStatus).to.equal('PASS');
+ });
+
+ it('a simple assertion (assert) should FAIL when making a GET request', async () => {
+ const url = 'http://localhost:3000/book';
+ const method = 'GET';
+ const script = "assert.strictEqual(3, '3', 'wrong types');";
+ await fillRestRequest(url, method);
+ await clearAndFillTestScriptArea(script);
+ await addAndSend();
+
+ await app.client.$('a=Tests').click();
+ const testStatus = await app.client.$('#TestResult-0-status').getText();
+ expect(testStatus).to.equal('FAIL');
+ });
+
+ it('a simple assertion (expect) should PASS when making a GET request', async () => {
+ const url = 'http://localhost:3000/book';
+ const method = 'GET';
+ const script = "expect(3, 'correct types').to.equal(3);";
+ await fillRestRequest(url, method);
+ await clearAndFillTestScriptArea(script);
+ await addAndSend();
+
+ await app.client.$('a=Tests').click();
+ const testStatus = await app.client.$('#TestResult-0-status').getText();
+ expect(testStatus).to.equal('PASS');
+ });
+
+ it('a simple assertion (expect) should FAIL when making a GET request', async () => {
+ const url = 'http://localhost:3000/book';
+ const method = 'GET';
+ const script = "expect(3, 'correct types').to.equal('3');";
+ await fillRestRequest(url, method);
+ await clearAndFillTestScriptArea(script);
+ await addAndSend();
+
+ await app.client.$('a=Tests').click();
+ const testStatus = await app.client.$('#TestResult-0-status').getText();
+ expect(testStatus).to.equal('FAIL');
+ });
+ });
+
+ describe('Multiple assertion statements', () => {
+ it('should handle multiple different simple assert statements', async () => {
+ const url = 'http://localhost:3000/book';
+ const method = 'GET';
+ const script =
+ "assert.strictEqual(3, '3', 'wrong types');\nassert.strictEqual(3, '3', 'this assert is a message');\nassert.strictEqual(3, 3, 'correct types');\nassert.strictEqual(3, 3, 'this assert is a message');";
+ await fillRestRequest(url, method);
+ await clearAndFillTestScriptArea(script);
+ await addAndSend();
+
+ await app.client.$('a=Tests').click();
+ const firstStatus = await app.client
+ .$('#TestResult-0-status')
+ .getText();
+ const secondStatus = await app.client
+ .$('#TestResult-1-status')
+ .getText();
+ const thirdStatus = await app.client
+ .$('#TestResult-2-status')
+ .getText();
+ const fourthStatus = await app.client
+ .$('#TestResult-3-status')
+ .getText();
+
+ expect(firstStatus).to.equal('FAIL');
+ expect(secondStatus).to.equal('FAIL');
+ expect(thirdStatus).to.equal('PASS');
+ expect(fourthStatus).to.equal('PASS');
+ });
+
+ it('should handle multiple different simple expect statements', async () => {
+ const url = 'http://localhost:3000/book';
+ const method = 'GET';
+ const script =
+ "expect(3, 'wrong types').to.equal('3');\nexpect(3, 'this expect is a message').to.equal('3');\nexpect(3, 'correct types').to.equal(3);\nexpect(3, 'this expect is a message').to.equal(3);";
+ await fillRestRequest(url, method);
+ await clearAndFillTestScriptArea(script);
+ await addAndSend();
+
+ await app.client.$('a=Tests').click();
+ const firstStatus = await app.client
+ .$('#TestResult-0-status')
+ .getText();
+ const secondStatus = await app.client
+ .$('#TestResult-1-status')
+ .getText();
+ const thirdStatus = await app.client
+ .$('#TestResult-2-status')
+ .getText();
+ const fourthStatus = await app.client
+ .$('#TestResult-3-status')
+ .getText();
+
+ expect(firstStatus).to.equal('FAIL');
+ expect(secondStatus).to.equal('FAIL');
+ expect(thirdStatus).to.equal('PASS');
+ expect(fourthStatus).to.equal('PASS');
+ });
+ });
+
+ describe('Assertions on response object', () => {
+ it('chai.assert: should be able to access the response object', async () => {
+ const url = 'http://localhost:3000/book';
+ const method = 'GET';
+ const script = "assert.exists(response, 'response is object');";
+ await fillRestRequest(url, method);
+ await clearAndFillTestScriptArea(script);
+ await addAndSend();
+
+ await app.client.$('a=Tests').click();
+
+ const testStatus = await app.client.$('#TestResult-0-status').getText();
+ expect(testStatus).to.equal('PASS');
+ });
+
+ it('chai.assert: should be able to access the status code from the response object', async () => {
+ const url = 'http://localhost:3000/book';
+ const method = 'GET';
+ const script =
+ "assert.strictEqual(response.status, 200, 'response is 200');";
+ await fillRestRequest(url, method);
+ await clearAndFillTestScriptArea(script);
+ await addAndSend();
+
+ await app.client.$('a=Tests').click();
+
+ const testStatus = await app.client.$('#TestResult-0-status').getText();
+ expect(testStatus).to.equal('PASS');
+ });
+
+ it('chai.assert: should be able to access the cookies from the response object', async () => {
+ const url = 'http://google.com';
+ const method = 'GET';
+ const script =
+ "assert.exists(response.cookies, 'cookies exists on response object');";
+ await fillRestRequest(url, method);
+ await clearAndFillTestScriptArea(script);
+ await addAndSend();
+
+ await app.client.$('a=Tests').click();
+ const testStatus = await new Promise((resolve) => {
+ setTimeout(async () => {
+ const text = await app.client.$('#TestResult-0-status').getText();
+ resolve(text);
+ }, 500);
+ // block for 500 ms since we need to wait for a response; normally test server would
+ // respond fast enough since it is localhost and not a remote server
+ });
+
+ expect(testStatus).to.equal('PASS');
+ });
+
+ it('chai.assert: should be able to access the headers from the response object', async () => {
+ const url = 'http://google.com';
+ const method = 'GET';
+ const script =
+ "assert.exists(response.headers, 'headers exists on response object');";
+ await fillRestRequest(url, method);
+ await clearAndFillTestScriptArea(script);
+ await addAndSend();
+
+ await app.client.$('a=Tests').click();
+ const testStatus = await new Promise((resolve) => {
+ setTimeout(async () => {
+ const text = await app.client.$('#TestResult-0-status').getText();
+ resolve(text);
+ }, 500);
+ });
+
+ expect(testStatus).to.equal('PASS');
+ });
+
+ it('chai.expect: should be able to access the response object', async () => {
+ const url = 'http://localhost:3000/book';
+ const method = 'GET';
+ const script = "expect(response, 'response exists').to.exist;";
+ await fillRestRequest(url, method);
+ await clearAndFillTestScriptArea(script);
+ await addAndSend();
+
+ await app.client.$('a=Tests').click();
+ const testStatus = await app.client.$('#TestResult-0-status').getText();
+ expect(testStatus).to.equal('PASS');
+ });
+
+ it('chai.expect: should be able to access the status code from the response object', async () => {
+ const url = 'http://localhost:3000/book';
+ const method = 'GET';
+ const script =
+ "expect(response.status, 'response is 200').to.equal(200);";
+ await fillRestRequest(url, method);
+ await clearAndFillTestScriptArea(script);
+ await addAndSend();
+
+ await app.client.$('a=Tests').click();
+ const testStatus = await app.client.$('#TestResult-0-status').getText();
+ expect(testStatus).to.equal('PASS');
+ });
+
+ it('chai.expect: should be able to access the cookies from the response object', async () => {
+ const url = 'http://google.com';
+ const method = 'GET';
+ const script =
+ "expect(response.cookies, 'cookies exists on response object').to.exist;";
+ await fillRestRequest(url, method);
+ await clearAndFillTestScriptArea(script);
+ await addAndSend();
+
+ await app.client.$('a=Tests').click();
+
+ const testStatus = await new Promise((resolve) => {
+ setTimeout(async () => {
+ const text = await app.client.$('#TestResult-0-status').getText();
+ resolve(text);
+ }, 500);
+ });
+
+ expect(testStatus).to.equal('PASS');
+ });
+
+ it('chai.expect: should be able to access the headers from the response object', async () => {
+ const url = 'http://google.com';
+ const method = 'GET';
+ const script =
+ "expect(response.headers, 'headers exists on reponse object').to.exist;";
+ await fillRestRequest(url, method);
+ await clearAndFillTestScriptArea(script);
+ await addAndSend();
+
+ await app.client.$('a=Tests').click();
+
+ const testStatus = await new Promise((resolve) => {
+ setTimeout(async () => {
+ const text = await app.client.$('#TestResult-0-status').getText();
+ resolve(text);
+ }, 500);
+ });
+
+ expect(testStatus).to.equal('PASS');
+ });
+ });
+
+ describe('Using variables', () => {
+ it('Test results do not render if JavaScript is entered but specifically not assertion tests', async () => {
+ const url = 'http://localhost:3000/book';
+ const method = 'GET';
+ const script = "const foo = 'bar';";
+ await fillRestRequest(url, method);
+ await clearAndFillTestScriptArea(script);
+ await addAndSend();
+
+ await app.client.$('a=Tests').click();
+ const { selector } = await app.client.$('.empty-state-wrapper');
+ expect(selector).to.equal('.empty-state-wrapper');
+ });
+ });
+ });
+};
diff --git a/test/subSuites/reqInputTests.js b/test/subSuites/reqInputTests.js
index 4dc236dd8..1af4221cb 100644
--- a/test/subSuites/reqInputTests.js
+++ b/test/subSuites/reqInputTests.js
@@ -1,163 +1,163 @@
-const assert = require("assert");
-const composerObj = require("../pageObjects/ComposerObj.js");
-const app = require("../testApp.js");
-
-module.exports = () => {
- describe("URL/request method inputs", () => {
- it("can switch tabs in the composer pane", async () => {
- // click and check history
- await composerObj.tabsHistory.click();
- const historySelected = await app.client.$(".is-active").getText();
- assert.strictEqual(historySelected, "History");
-
- // click and check composer
- await composerObj.tabsComposer.click();
- const composerSelected = await app.client.$(".is-active").getText();
- return assert.strictEqual(composerSelected, "Composer");
- });
-
- it("can select a request type", async () => {
- // click and check graphQL
- await composerObj.selectedNetwork.click();
- await app.client.$("a=GRAPHQL").click();
- assert.strictEqual(
- await composerObj.selectedNetwork.getText(),
- "GRAPHQL"
- );
-
- // click and check WS
- await composerObj.selectedNetwork.click();
- await app.client.$("a=WEB SOCKETS").click();
- assert.strictEqual(
- await composerObj.selectedNetwork.getText(),
- "WEB SOCKETS"
- );
-
- // click and check gRPC
- await composerObj.selectedNetwork.click();
- await app.client.$("a=gRPC").click();
- assert.strictEqual(await composerObj.selectedNetwork.getText(), "gRPC");
-
- // click and check REST
- await composerObj.selectedNetwork.click();
- await app.client.$("a=REST").click();
- return assert.strictEqual(
- await composerObj.selectedNetwork.getText(),
- "REST"
- );
- });
-
- it("can select a REST method", async () => {
- // click and select POST
- await app.client.$("span=GET").click();
- await app.client.$("a=POST").click();
- assert.notStrictEqual(await app.client.$("span=POST"), null);
-
- // click and select PUT
- await app.client.$("span=POST").click();
- await app.client.$("a=PUT").click();
- assert.notStrictEqual(await app.client.$("span=PUT"), null);
-
- //click and select GET
- await app.client.$("span=PUT").click();
- await app.client.$("a=GET").click();
- assert.notStrictEqual(await app.client.$("span=PUT"), null);
-
- //click and select PATCH
- await app.client.$("span=GET").click();
- await app.client.$("a=PATCH").click();
- assert.notStrictEqual(await app.client.$("span=GET"), null);
-
- //click and select DELETE
- await app.client.$("span=PATCH").click();
- await app.client.$("a=DELETE").click();
- assert.notStrictEqual(await app.client.$("span=PATCH"), null);
- });
-
- it("can type url into url input", async () => {
- await composerObj.url.setValue(
- "http://jsonplaceholder.typicode.com/posts/1"
- );
- const input = await composerObj.url.getValue();
-
- return assert.strictEqual(
- input,
- "http://jsonplaceholder.typicode.com/posts/1"
- );
- });
- });
-
- // W.I.P. .....
- // describe('headers inputs', async () => {
- // it('should open headers input, rendering single input at first', async () => {
- // // count header rows
- // const headers = await app.client.$$('.header-row');
- // assert.strictEqual(headers.length, 1);
- // });
-
- // it('can add new headers in request and type in keys & values', async () => {
- // // click add header
- // await app.client.$('button=+ Header').click();
-
- // // change 2nd header key / value
- // await app.client.$('//*[@id="header-row1"]/input[1]').setValue('header-key');
- // await app.client.$('//*[@id="header-row1"]/input[2]').setValue('header-value');
-
- // // select 2nd header key / value
- // const headerKey = await app.client.$('//*[@id="header-row1"]/input[1]').getValue();
- // const headerValue = await app.client.$('//*[@id="header-row1"]/input[2]').getValue();
-
- // assert.strictEqual(await headerKey, 'header-key');
- // assert.strictEqual(await headerValue, 'header-value');
- // });
-
- // });
-
- describe("cookies inputs", async () => {
- it("should open cookies input, rendering single input at first", async () => {
- // count cookie rows
- const cookies = await app.client.$$(".cookie-row");
- assert.strictEqual(cookies.length, 1);
- });
-
- it("can add new cookies in request and type in keys & values", async () => {
- // click add cookie
- await app.client.$("button=+ Cookie").click();
-
- // change 2nd cookie key / value
- await app.client
- .$('//*[@id="cookie-row1"]/input[1]')
- .setValue("cookie-key");
- await app.client
- .$('//*[@id="cookie-row1"]/input[2]')
- .setValue("cookie-value");
-
- // select 2nd cookie key / value
- const cookieKey = await app.client
- .$('//*[@id="cookie-row1"]/input[1]')
- .getValue();
- const cookieValue = await app.client
- .$('//*[@id="cookie-row1"]/input[2]')
- .getValue();
-
- assert.strictEqual(await cookieKey, "cookie-key");
- assert.strictEqual(await cookieValue, "cookie-value");
- });
- });
-
- describe("request body inputs", () => {
- it("body input appears for all other requests", async () => {
- bodyInputVisible = await app.client.$("#body-entry-select").isExisting();
- assert.strictEqual(bodyInputVisible, true);
- });
-
- it("can type plain text into body", async () => {
- const input = "Team Swell is the best!";
- await composerObj.clearRestBodyAndWriteKeys(input);
- assert.strictEqual(
- await app.client.$("#body-entry-select .CodeMirror-line").getText(),
- input
- );
- });
- });
-};
+const assert = require('assert');
+const composerObj = require('../pageObjects/ComposerObj.js');
+const app = require('../testApp.js');
+
+module.exports = () => {
+ describe('URL/request method inputs', () => {
+ it('can switch tabs in the composer pane', async () => {
+ // click and check history
+ await composerObj.tabsHistory.click();
+ const historySelected = await app.client.$('.is-active').getText();
+ assert.strictEqual(historySelected, 'History');
+
+ // click and check composer
+ await composerObj.tabsComposer.click();
+ const composerSelected = await app.client.$('.is-active').getText();
+ return assert.strictEqual(composerSelected, 'Composer');
+ });
+
+ it('can select a request type', async () => {
+ // click and check graphQL
+ await composerObj.selectedNetwork.click();
+ await app.client.$('a=GRAPHQL').click();
+ assert.strictEqual(
+ await composerObj.selectedNetwork.getText(),
+ 'GRAPHQL'
+ );
+
+ // click and check WS
+ await composerObj.selectedNetwork.click();
+ await app.client.$('a=WEB SOCKETS').click();
+ assert.strictEqual(
+ await composerObj.selectedNetwork.getText(),
+ 'WEB SOCKETS'
+ );
+
+ // click and check gRPC
+ await composerObj.selectedNetwork.click();
+ await app.client.$('a=gRPC').click();
+ assert.strictEqual(await composerObj.selectedNetwork.getText(), 'gRPC');
+
+ // click and check REST
+ await composerObj.selectedNetwork.click();
+ await app.client.$('a=REST').click();
+ return assert.strictEqual(
+ await composerObj.selectedNetwork.getText(),
+ 'REST'
+ );
+ });
+
+ it('can select a REST method', async () => {
+ // click and select POST
+ await app.client.$('span=GET').click();
+ await app.client.$('a=POST').click();
+ assert.notStrictEqual(await app.client.$('span=POST'), null);
+
+ // click and select PUT
+ await app.client.$('span=POST').click();
+ await app.client.$('a=PUT').click();
+ assert.notStrictEqual(await app.client.$('span=PUT'), null);
+
+ // click and select GET
+ await app.client.$('span=PUT').click();
+ await app.client.$('a=GET').click();
+ assert.notStrictEqual(await app.client.$('span=PUT'), null);
+
+ // click and select PATCH
+ await app.client.$('span=GET').click();
+ await app.client.$('a=PATCH').click();
+ assert.notStrictEqual(await app.client.$('span=GET'), null);
+
+ // click and select DELETE
+ await app.client.$('span=PATCH').click();
+ await app.client.$('a=DELETE').click();
+ assert.notStrictEqual(await app.client.$('span=PATCH'), null);
+ });
+
+ it('can type url into url input', async () => {
+ await composerObj.url.setValue(
+ 'http://jsonplaceholder.typicode.com/posts/1'
+ );
+ const input = await composerObj.url.getValue();
+
+ return assert.strictEqual(
+ input,
+ 'http://jsonplaceholder.typicode.com/posts/1'
+ );
+ });
+ });
+
+ // W.I.P. .....
+ // describe('headers inputs', async () => {
+ // it('should open headers input, rendering single input at first', async () => {
+ // // count header rows
+ // const headers = await app.client.$$('.header-row');
+ // assert.strictEqual(headers.length, 1);
+ // });
+
+ // it('can add new headers in request and type in keys & values', async () => {
+ // // click add header
+ // await app.client.$('button=+ Header').click();
+
+ // // change 2nd header key / value
+ // await app.client.$('//*[@id="header-row1"]/input[1]').setValue('header-key');
+ // await app.client.$('//*[@id="header-row1"]/input[2]').setValue('header-value');
+
+ // // select 2nd header key / value
+ // const headerKey = await app.client.$('//*[@id="header-row1"]/input[1]').getValue();
+ // const headerValue = await app.client.$('//*[@id="header-row1"]/input[2]').getValue();
+
+ // assert.strictEqual(await headerKey, 'header-key');
+ // assert.strictEqual(await headerValue, 'header-value');
+ // });
+
+ // });
+
+ describe('cookies inputs', async () => {
+ it('should open cookies input, rendering single input at first', async () => {
+ // count cookie rows
+ const cookies = await app.client.$$('.cookie-row');
+ assert.strictEqual(cookies.length, 1);
+ });
+
+ it('can add new cookies in request and type in keys & values', async () => {
+ // click add cookie
+ await app.client.$('button=+ Cookie').click();
+
+ // change 2nd cookie key / value
+ await app.client
+ .$('//*[@id="cookie-row1"]/input[1]')
+ .setValue('cookie-key');
+ await app.client
+ .$('//*[@id="cookie-row1"]/input[2]')
+ .setValue('cookie-value');
+
+ // select 2nd cookie key / value
+ const cookieKey = await app.client
+ .$('//*[@id="cookie-row1"]/input[1]')
+ .getValue();
+ const cookieValue = await app.client
+ .$('//*[@id="cookie-row1"]/input[2]')
+ .getValue();
+
+ assert.strictEqual(await cookieKey, 'cookie-key');
+ assert.strictEqual(await cookieValue, 'cookie-value');
+ });
+ });
+
+ describe('request body inputs', () => {
+ it('body input appears for all other requests', async () => {
+ bodyInputVisible = await app.client.$('#body-entry-select').isExisting();
+ assert.strictEqual(bodyInputVisible, true);
+ });
+
+ it('can type plain text into body', async () => {
+ const input = 'Team Swell is the best!';
+ await composerObj.clearRestBodyAndWriteKeys(input);
+ assert.strictEqual(
+ await app.client.$('#body-entry-select .CodeMirror-line').getText(),
+ input
+ );
+ });
+ });
+};
diff --git a/test/subSuites/websocketTest.js b/test/subSuites/websocketTest.js
index b5cf6f7e2..2f1008026 100644
--- a/test/subSuites/websocketTest.js
+++ b/test/subSuites/websocketTest.js
@@ -1,146 +1,135 @@
-const chai = require("chai");
-const path = require("path");
-//for this the previous group used Chai instead of 'assert'from node
-const app = require("../testApp.js");
-const composerObj = require("../pageObjects/ComposerObj.js");
-const workspaceObj = require("../pageObjects/WorkspaceObj.js");
-const websocketServer = require("../websocketServer");
-
-const expect = chai.expect;
-
-module.exports = () => {
- describe("Websocket requests", () => {
- before(() => {
- app.client.$("button=Clear Workspace").click();
- });
-
- const addAndSend = async () => {
- try {
- await composerObj.addRequestBtn.click();
- await workspaceObj.latestSendRequestBtn.click();
- } catch (err) {
- console.error(err);
- }
- };
-
- after(() => {
- app.client.$("button=Clear Workspace").click();
- });
-
- it("it should send and receive messages to the mock server", async () => {
- try {
- // select web sockets
- await composerObj.selectedNetwork.click();
- await app.client.$("a=WEB SOCKETS").click();
-
- // type in url
- await composerObj.url.setValue("ws://localhost:5000/");
-
- //
-
- await addAndSend();
-
- await new Promise((resolve) =>
- setTimeout(async () => {
- try {
- await app.client
- .$("#wsSendData")
- .click()
- .$("#wsMsgInput")
- .click()
- .keys("testing websocket protocol");
- await app.client.$("button=Send Message").click();
-
- await new Promise((resolve) =>
- setTimeout(async () => {
- try {
- const messageClient = await app.client
- .$("#ws-msg-0")
- .getText();
- const messageServer = await app.client
- .$("#ws-msg-1")
- .getText();
- expect(messageClient).to.include(
- "testing websocket protocol"
- );
- expect(messageServer).to.include(
- "testing websocket protocol"
- );
- resolve();
- } catch (err) {
- console.error(err);
- }
- }, 300)
- );
- resolve();
- } catch (err) {
- console.error(err);
- }
- }, 1000)
- );
- } catch (err) {
- console.error(err);
- }
- });
-
- it("it should send and receive images to public echo test", async () => {
- try {
- await new Promise(async (resolve) => {
- try {
- //instead of this, we need to click "select file", and choose a file
- const toUpload = path.join(
- __dirname,
- "..",
- "..",
- "build",
- "icons",
- "png",
- "128x128.png"
- );
-
- // /var/folders/wt/p4tb3xz50csfvrfj897gy4ww0000gn/T/.org.chromium.Chromium.kSmkVs/uploadyVlAPI/128x128.png
- await app.client.chooseFile("#wsFileInput", toUpload);
- // const val = app.client.getValue("#upload-test");
- await app.client.$("#wsSendImgBtn").click();
-
- await new Promise((resolve) =>
- setTimeout(async () => {
- try {
- const messageClient = await app.client
- .$("#ws-msg-0")
- .getText();
- const messageServer = await app.client
- .$("#ws-msg-1")
- .getText();
- expect(messageClient).to.include(
- "testing websocket protocol"
- );
- expect(messageServer).to.include(
- "testing websocket protocol"
- );
- resolve();
- } catch (err) {
- console.error(err);
- }
- }, 300)
- );
- resolve();
- } catch (err) {
- console.error(err);
- }
- });
- } catch (err) {
- console.error(err);
- }
- });
-
- // it("it should be able to close and re-open connection", async () => {
- // try {
- // await composerObj.closeConnectonBtn.click();
- // await composerObj.reopenConnectionBtn.click();
- // } catch (err) {
- // console.error(err);
- // }
- // });
- });
-};
+/* eslint-disable no-async-promise-executor */
+const chai = require('chai');
+const path = require('path');
+const app = require('../testApp.js');
+const composerObj = require('../pageObjects/ComposerObj.js');
+const workspaceObj = require('../pageObjects/WorkspaceObj.js');
+
+const { expect } = chai;
+
+module.exports = () => {
+ describe('Websocket requests', () => {
+ before(() => {
+ app.client.$('button=Clear Workspace').click();
+ });
+
+ const addAndSend = async () => {
+ try {
+ await composerObj.addRequestBtn.click();
+ await workspaceObj.latestSendRequestBtn.click();
+ } catch (err) {
+ console.error(err);
+ }
+ };
+
+ after(() => {
+ app.client.$('button=Clear Workspace').click();
+ });
+
+ it('it should send and receive messages to the mock server', async () => {
+ try {
+ // select web sockets
+ await composerObj.selectedNetwork.click();
+ await app.client.$('a=WEB SOCKETS').click();
+
+ // type in url
+ await composerObj.url.setValue('ws://localhost:5000/');
+
+ //
+
+ await addAndSend();
+
+ await new Promise((resolve) =>
+ setTimeout(async () => {
+ try {
+ await app.client
+ .$('#wsSendData')
+ .click()
+ .$('#wsMsgInput')
+ .click()
+ .keys('testing websocket protocol');
+ await app.client.$('button=Send Message').click();
+
+ await new Promise((resolve) =>
+ setTimeout(async () => {
+ try {
+ const messageClient = await app.client
+ .$('#ws-msg-0')
+ .getText();
+ const messageServer = await app.client
+ .$('#ws-msg-1')
+ .getText();
+ expect(messageClient).to.include(
+ 'testing websocket protocol'
+ );
+ expect(messageServer).to.include(
+ 'testing websocket protocol'
+ );
+ resolve();
+ } catch (err) {
+ console.error(err);
+ }
+ }, 300)
+ );
+ resolve();
+ } catch (err) {
+ console.error(err);
+ }
+ }, 1000)
+ );
+ } catch (err) {
+ console.error(err);
+ }
+ });
+
+ it('it should send and receive images to public echo test', async () => {
+ try {
+ await new Promise(async (resolve) => {
+ try {
+ // instead of this, we need to click "select file", and choose a file
+ const toUpload = path.join(
+ __dirname,
+ '..',
+ '..',
+ 'build',
+ 'icons',
+ 'png',
+ '128x128.png'
+ );
+
+ await app.client.chooseFile('#wsFileInput', toUpload);
+ // const val = app.client.getValue("#upload-test");
+ await app.client.$('#wsSendImgBtn').click();
+
+ await new Promise((resolve) =>
+ setTimeout(async () => {
+ try {
+ const messageClient = await app.client
+ .$('#ws-msg-0')
+ .getText();
+ const messageServer = await app.client
+ .$('#ws-msg-1')
+ .getText();
+ expect(messageClient).to.include(
+ 'testing websocket protocol'
+ );
+ expect(messageServer).to.include(
+ 'testing websocket protocol'
+ );
+ resolve();
+ } catch (err) {
+ console.error(err);
+ }
+ }, 300)
+ );
+ resolve();
+ } catch (err) {
+ console.error(err);
+ }
+ });
+ } catch (err) {
+ console.error(err);
+ }
+ });
+ });
+};
diff --git a/test/testApp.js b/test/testApp.js
index fdd62ef69..30bdf1088 100644
--- a/test/testApp.js
+++ b/test/testApp.js
@@ -1,27 +1,27 @@
-const { Application } = require("spectron");
-const electronPath = require("electron");
-// Require Electron from the binaries included in node_modules.
-const path = require("path");
-
-/* ## How to run tests
-
-To run functional tests that check for functionality of each API network protocol, use: "npm run test-mocha"
-
-Note: For the HTTP test to work, you will have to add a .env file with any MONGO URI.
-E.g., MONGO_URI = "mongodb+srv://YOUR_URL"
-*/
-
-const TEST_MODE = "TEST_MODE";
-
-const app = new Application({
- // Your electron path can be any binary
- // i.e for OSX an example path could be '/Applications/MyApp.app/Contents/MacOS/MyApp'
- // But for the sake of the example we fetch it from our node_modules.
- requireName: "electronRequire",
- path: electronPath,
- // The following line tells spectron to look and use the main.js file
- // and the package.json located 1 level above along with an arg, 'TEST_MODE'
- args: [path.join(__dirname, ".."), TEST_MODE],
-});
-
-module.exports = app;
+const { Application } = require('spectron');
+const electronPath = require('electron');
+// Require Electron from the binaries included in node_modules.
+const path = require('path');
+
+/* ## How to run tests
+
+To run functional tests that check for functionality of each API network protocol, use: "npm run test-mocha"
+
+Note: For the HTTP test to work, you will have to add a .env file with any MONGO URI.
+E.g., MONGO_URI = "mongodb+srv://YOUR_URL"
+*/
+
+const TEST_MODE = 'TEST_MODE';
+
+const app = new Application({
+ // Your electron path can be any binary
+ // i.e for OSX an example path could be '/Applications/MyApp.app/Contents/MacOS/MyApp'
+ // But for the sake of the example we fetch it from our node_modules.
+ requireName: 'electronRequire',
+ path: electronPath,
+ // The following line tells spectron to look and use the main.js file
+ // and the package.json located 1 level above along with an arg, 'TEST_MODE'
+ args: [path.join(__dirname, '..'), TEST_MODE],
+});
+
+module.exports = app;
diff --git a/test/testSuite.js b/test/testSuite.js
index bf4ab9344..659760146 100644
--- a/test/testSuite.js
+++ b/test/testSuite.js
@@ -1,48 +1,46 @@
-// Integration test for electron for spectron
-// ****** use "npm run test-mocha" to run these tests ******
-
-// import other tests
-const appOpensTests = require("./subSuites/appOpens");
-const reqInputTests = require("./subSuites/reqInputTests");
-const httpTest = require("./subSuites/httpTest");
-const websocketTest = require("./subSuites/websocketTest");
-const grpcTest = require("./subSuites/grpcTest");
-const graphqlTest = require("./subSuites/graphqlTest");
-
-const httpTestingTest = require("./subSuites/httpTestingTest");
-const graphqlTestingTest = require("./subSuites/graphqlTestingTest");
-const grpcTestingTest = require("./subSuites/grpcTestingTest");
-
-const app = require("./testApp");
-
-describe("Electron Tests", function () {
- this.timeout(60000);
- //before and after here are to test if the app can be opened and closed
- before(function () {
- return app.start();
- });
-
- after(function () {
- if (app && app.isRunning()) {
- return app.stop();
- }
- });
-
- // these are test suites within this broader suite
- appOpensTests();
-
- // execute differnt types of test here
- describe("CRUD functionality", function () {
- reqInputTests();
- // httpTest(); //Comment out because no Mongo URI for test server
- graphqlTest();
- websocketTest();
- grpcTest();
- });
-
- // describe("Swell Testing functionality", function () {
- // httpTestingTest();
- // grpcTestingTest();
- // graphqlTestingTest();
- // });
-});
+// Integration test for electron for spectron
+// ****** use "npm run test-mocha" to run these tests ******
+
+// import other tests
+const appOpensTests = require('./subSuites/appOpens');
+const reqInputTests = require('./subSuites/reqInputTests');
+const httpTest = require('./subSuites/httpTest');
+const websocketTest = require('./subSuites/websocketTest');
+const grpcTest = require('./subSuites/grpcTest');
+const graphqlTest = require('./subSuites/graphqlTest');
+
+const httpTestingTest = require('./subSuites/httpTestingTest');
+const graphqlTestingTest = require('./subSuites/graphqlTestingTest');
+const grpcTestingTest = require('./subSuites/grpcTestingTest');
+
+const app = require('./testApp');
+
+describe('Electron Tests', function () {
+ this.timeout(200000);
+ // before and after here are to test if the app can be opened and closed
+ before(() => app.start());
+
+ after(() => {
+ if (app && app.isRunning()) {
+ return app.stop();
+ }
+ });
+
+ // these are test suites within this broader suite
+ appOpensTests();
+
+ // execute differnt types of test here
+ describe('CRUD functionality', () => {
+ reqInputTests();
+ // httpTest(); //Comment out because no Mongo URI for test server
+ graphqlTest();
+ websocketTest();
+ grpcTest();
+ });
+
+ // describe("Swell Testing functionality", function () {
+ // httpTestingTest();
+ // grpcTestingTest();
+ // graphqlTestingTest();
+ // });
+});
diff --git a/test/webrtcWSServer.js b/test/webrtcWSServer.js
new file mode 100644
index 000000000..7759cb872
--- /dev/null
+++ b/test/webrtcWSServer.js
@@ -0,0 +1,251 @@
+/* eslint-disable default-case */
+const express = require('express');
+const WebSocketServer = require('ws').Server;
+
+const PORT_NUMBER = 9000;
+
+// WebSocket server timeout: 5 minutes
+const CONNECTION_TIMEOUT = 5 * 60 * 1000000;
+
+// Used for managing the text chat user list.
+
+let connectionArray = [];
+let nextID = Date.now();
+let appendToMakeUnique = 1;
+
+// Output logging information to console
+
+function log(text) {
+ const time = new Date();
+ console.log('[' + time.toLocaleTimeString() + '] ' + text);
+}
+
+// Scans the list of users and see if the specified name is unique. If it is,
+// return true. Otherwise, returns false. We want all users to have unique
+// names.
+
+function isUsernameUnique(name) {
+ let isUnique = true;
+ let i;
+
+ for (i = 0; i < connectionArray.length; i++) {
+ if (connectionArray[i].username === name) {
+ isUnique = false;
+ break;
+ }
+ }
+ return isUnique;
+}
+
+// Sends a message (which is already stringified JSON) to a single
+// user, given their username. We use this for the WebRTC signaling,
+// and we could use it for private text messaging.
+
+function sendToOneUser(target, msgString) {
+ const isUnique = true;
+ let i;
+
+ for (i = 0; i < connectionArray.length; i++) {
+ if (connectionArray[i].username === target) {
+ connectionArray[i].send(msgString);
+ break;
+ }
+ }
+}
+
+// Scan the list of connections and return the one for the specified
+// clientID. Each login gets an ID that doesn't change during the session,
+// so it can be tracked across username changes.
+
+function getConnectionForID(id) {
+ let connect = null;
+ let i;
+
+ for (i = 0; i < connectionArray.length; i++) {
+ if (connectionArray[i].clientID === id) {
+ connect = connectionArray[i];
+ break;
+ }
+ }
+
+ return connect;
+}
+
+// Builds a message object of type "userlist" which contains the names of
+// all connected users. Used to ramp up newly logged-in users and,
+// inefficiently, to handle name change notifications.
+
+function makeUserListMessage() {
+ const userListMsg = {
+ type: 'userlist',
+ users: [],
+ };
+ let i;
+
+ // Add the users to the list
+
+ for (i = 0; i < connectionArray.length; i++) {
+ userListMsg.users.push(connectionArray[i].username);
+ }
+
+ return userListMsg;
+}
+
+// Sends a "userlist" message to all chat members. This is a cheesy way
+// to ensure that every join/drop is reflected everywhere. It would be more
+// efficient to send simple join/drop messages to each user, but this is
+// good enough for this simple example.
+
+function sendUserListToAll() {
+ const userListMsg = makeUserListMessage();
+ const userListMsgStr = JSON.stringify(userListMsg);
+ let i;
+
+ for (i = 0; i < connectionArray.length; i++) {
+ connectionArray[i].send(userListMsgStr);
+ }
+}
+
+// Create the HTTP server.
+
+const app = express();
+const router = express.Router();
+
+app.use('/', router);
+app.use(express.static('views'));
+app.use(express.static('public'));
+
+log('HTTP server configured');
+
+const httpServer = app.listen(PORT_NUMBER, () => {
+ log(`Static web server now listening on PORT ${PORT_NUMBER}`);
+});
+
+// Create the WebSocket server.
+
+const wssOptions = {
+ server: httpServer,
+ timeout: CONNECTION_TIMEOUT,
+};
+
+const wss = new WebSocketServer(wssOptions);
+
+wss.on('connection', (ws) => {
+ log('Incoming connection...');
+
+ connectionArray.push(ws);
+ ws.clientID = nextID;
+ nextID++;
+
+ // Tell the client that it's connected and send it its ID token. It will
+ // send back its username in response.
+
+ const msg = {
+ type: 'id',
+ id: ws.clientID,
+ };
+ ws.send(JSON.stringify(msg));
+
+ // Handle the WebSocket's "message" event, which indicates a
+ // JSON message has been received from a client.
+
+ ws.on('message', (message) => {
+ log('Message received:');
+ log(message);
+
+ // Convert the JSON back to an object and process it.
+
+ let sendToClients = true;
+ let nameChanged = false;
+ let origName;
+
+ const msg = JSON.parse(message);
+ const connect = getConnectionForID(msg.id);
+ // Take a look at the incoming object and act on it based
+ // on its type. Unknown message types are passed through,
+ // since they may be used to implement client-side features.
+ // Messages with a "target" property are sent only to a user
+ // by that name.
+
+ switch (msg.type) {
+ // Public, textual message
+ case 'message':
+ msg.name = connect.username;
+ msg.text = msg.text.replace(/(<([^>]+)>)/gi, '');
+ break;
+
+ // Username change
+ case 'username':
+ nameChanged = false;
+ origName = msg.name;
+
+ // Ensure the name is unique by appending a number to it
+ // if it's not; keep trying that until it works.
+ while (!isUsernameUnique(msg.name)) {
+ msg.name = origName + appendToMakeUnique;
+ appendToMakeUnique++;
+ nameChanged = true;
+ }
+
+ // If the name had to be changed, we send a "rejectusername"
+ // message back to the user so they know their name has been
+ // altered by the server.
+
+ if (nameChanged) {
+ const changeMsg = {
+ id: msg.id,
+ type: 'rejectusername',
+ name: msg.name,
+ };
+ connect.send(JSON.stringify(changeMsg));
+ }
+
+ // Set this connection's final username and send out the
+ // updated user list to all users. Yeah, we're sending a full
+ // list instead of just updating. It's horribly inefficient
+ // but this is a demo. Don't do this in a real app.
+
+ connect.username = msg.name;
+ sendUserListToAll();
+ sendToClients = false; // We already sent the proper responses
+ break;
+ }
+
+ // Convert the revised message back to JSON and send it out
+ // to the specified client or all clients, as appropriate. We
+ // pass through any messages not specifically handled
+ // in the select block above. This allows the clients to
+ // exchange signaling and other control objects unimpeded.
+
+ if (sendToClients) {
+ const msgString = JSON.stringify(msg);
+ let i;
+
+ // If the message specifies a target username, only send the
+ // message to them. Otherwise, send it to every user.
+
+ if (msg.target && msg.target !== undefined && msg.target.length !== 0) {
+ sendToOneUser(msg.target, msgString);
+ } else {
+ for (i = 0; i < connectionArray.length; i++) {
+ connectionArray[i].send(msgString);
+ }
+ }
+ }
+ });
+
+ ws.on('close', (reason, description) => {
+ // First, remove the connection from the list of connections.
+ connectionArray = connectionArray.filter((el, idx, ar) => {
+ return el.connected;
+ });
+
+ // Build and output log output for close information.
+ let logMessage = 'Connection closed: ' + ws.remoteAddress + ' (' + reason;
+ if (description !== null && description.length !== 0) {
+ logMessage += ': ' + description;
+ }
+ logMessage += ')';
+ log(logMessage);
+ });
+});
diff --git a/test/websocketServer.js b/test/websocketServer.js
index eeee09b04..46a6a604b 100644
--- a/test/websocketServer.js
+++ b/test/websocketServer.js
@@ -1,56 +1,54 @@
-const WebSocketServer = require("websocket").server;
-const http = require("http");
-
-const server = http.createServer(function (request, response) {
- console.log(new Date() + " Received request for " + request.url);
- response.writeHead(404);
- response.end();
-});
-server.listen(5000, function () {
- console.log(new Date() + " Server is listening on port 5000");
-});
-
-wsServer = new WebSocketServer({
- httpServer: server,
- // You should not use autoAcceptConnections for production
- // applications, as it defeats all standard cross-origin protection
- // facilities built into the protocol and the browser. You should
- // *always* verify the connection's origin and decide whether or not
- // to accept it.
- autoAcceptConnections: false,
-});
-
-function originIsAllowed(origin) {
- // put logic here to detect whether the specified origin is allowed.
- return true;
-}
-
-wsServer.on("request", function (request) {
- if (!originIsAllowed(request.origin)) {
- // Make sure we only accept requests from an allowed origin
- request.reject();
- console.log(
- new Date() + " Connection from origin " + request.origin + " rejected."
- );
- return;
- }
- const connection = request.accept(null, request.origin);
-
- console.log(new Date() + " Connection accepted.");
- connection.on("message", function (message) {
- if (message.type === "utf8") {
- console.log("Received Message: " + message.utf8Data);
- connection.sendUTF(message.utf8Data);
- } else if (message.type === "binary") {
- console.log(
- "Received Binary Message of " + message.binaryData.length + " bytes"
- );
- connection.sendBytes(message.binaryData);
- }
- });
- connection.on("close", function (reasonCode, description) {
- console.log(
- new Date() + " Peer " + connection.remoteAddress + " disconnected."
- );
- });
-});
+const WebSocketServer = require('websocket').server;
+const http = require('npm-http-server');
+
+const server = http.createServer((request, response) => {
+ console.log(`${new Date()} Received request for ${request.url}`);
+ response.writeHead(404);
+ response.end();
+});
+server.listen(5000, () => {
+ console.log(`${new Date()} Server is listening on port 5000`);
+});
+
+wsServer = new WebSocketServer({
+ httpServer: server,
+ // You should not use autoAcceptConnections for production
+ // applications, as it defeats all standard cross-origin protection
+ // facilities built into the protocol and the browser. You should
+ // *always* verify the connection's origin and decide whether or not
+ // to accept it.
+ autoAcceptConnections: false,
+});
+
+function originIsAllowed(origin) {
+ // put logic here to detect whether the specified origin is allowed.
+ return true;
+}
+
+wsServer.on('request', (request) => {
+ if (!originIsAllowed(request.origin)) {
+ // Make sure we only accept requests from an allowed origin
+ request.reject();
+ console.log(
+ `${new Date()} Connection from origin ${request.origin} rejected.`
+ );
+ return;
+ }
+ const connection = request.accept(null, request.origin);
+
+ console.log(`${new Date()} Connection accepted.`);
+ connection.on('message', (message) => {
+ if (message.type === 'utf8') {
+ console.log(`Received Message: ${message.utf8Data}`);
+ connection.sendUTF(message.utf8Data);
+ } else if (message.type === 'binary') {
+ console.log(
+ `Received Binary Message of ${message.binaryData.length} bytes`
+ );
+ connection.sendBytes(message.binaryData);
+ }
+ });
+ connection.on('close', (reasonCode, description) => {
+ console.log(`${new Date()} Peer ${connection.remoteAddress} disconnected.`);
+ });
+});
diff --git a/tsconfig.json b/tsconfig.json
index 8f5ca020d..c41c3bf7e 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -1,7 +1,7 @@
{
"compilerOptions": {
/* Basic Options */
- // "outDir": "./dist/", // path to output directory. diabled because we are using babel-loader in webpack config
+ // "outDir": "./dist/", // path to output directory. disabled because we are using babel-loader in webpack config
"sourceMap": true, // allow sourcemap support
// "strictNullChecks": true, // enable strict null checks as a best practice
"module": "es6", // specify module code generation
@@ -23,6 +23,7 @@
/* Advanced Options */
"forceConsistentCasingInFileNames": true /* Disallow inconsistently-cased references to the same file. */
},
- "include": ["./src/", "main_process/protoParser.js"],
- "exclude": ["node_modules", "dist", "build", "coverage", "mocks"]
+ "include": ["./src/**/*.ts", "./src/**/*.tsx", "./main_process/*.ts"],
+ "exclude": ["node_modules", "dist", "build", "coverage", "mocks"],
+ "typeRoots": ["./node_modules/@types"]
}
diff --git a/webpack.config.js b/webpack.config.js
index aea4c0cd8..7c86713b1 100644
--- a/webpack.config.js
+++ b/webpack.config.js
@@ -1,93 +1,95 @@
-const path = require("path");
-const HtmlWebpackPlugin = require("html-webpack-plugin");
-const CspHtmlWebpackPlugin = require("csp-html-webpack-plugin");
-const MiniCssExtractPlugin = require("mini-css-extract-plugin");
-const BundleAnalyzerPlugin = require("webpack-bundle-analyzer")
- .BundleAnalyzerPlugin;
-
-module.exports = {
- target: "web",
- entry: ["./src/index.js"],
- output: {
- path: path.resolve(__dirname, "dist"),
- filename: "bundle.js",
- publicPath: "/",
- },
- module: {
- rules: [
- {
- test: /\.(ts|js)x?$/,
- include: [path.resolve(__dirname, "src")],
- use: {
- loader: "babel-loader",
- options: {
- presets: [
- "@babel/preset-env",
- "@babel/preset-react",
- "@babel/typescript",
- ],
- },
- },
- resolve: {
- extensions: [".ts", ".tsx", ".js", ".jsx", ".json"],
- },
- },
- {
- test: /\.scss$/,
- include: [path.resolve(__dirname, "src")],
- use: [MiniCssExtractPlugin.loader, "css-loader", "sass-loader"],
- resolve: {
- extensions: [".scss"],
- },
- },
- {
- test: /\.css$/,
- include: [
- path.resolve(__dirname, "src"),
- path.resolve(__dirname, "node_modules"),
- ],
- use: [MiniCssExtractPlugin.loader, "css-loader"],
- resolve: {
- extensions: [".css"],
- },
- },
- {
- test: /\.(eot|woff|woff2|ttf|svg|png|jpg|gif|)$/,
- use: "url-loader",
- },
- ],
- },
- plugins: [
- new MiniCssExtractPlugin({}),
- new HtmlWebpackPlugin({
- // if using template, add Swell and delete line 59.
- // template: path.resolve(__dirname, "index-csp.html"),
- filename: "index.html",
- title: "Swell",
- cspPlugin: {
- enabled: true,
- policy: {
- "base-uri": "'self'",
- "object-src": "'none'",
- "script-src": ["'self'"],
- "style-src": ["'self'"],
- },
- hashEnabled: {
- "script-src": true,
- "style-src": true,
- },
- nonceEnabled: {
- "script-src": true,
- "style-src": true,
- },
- },
- }),
- new CspHtmlWebpackPlugin(),
- // options here: https://github.com/webpack-contrib/webpack-bundle-analyzer
- // set to true to display bundle breakdown
- new BundleAnalyzerPlugin({
- openAnalyzer: false,
- analyzerMode: "static",
- }),
- ],
-};
+const path = require('path');
+const HtmlWebpackPlugin = require('html-webpack-plugin');
+const CspHtmlWebpackPlugin = require('csp-html-webpack-plugin');
+const MiniCssExtractPlugin = require('mini-css-extract-plugin');
+const { BundleAnalyzerPlugin } = require('webpack-bundle-analyzer');
+
+module.exports = {
+ target: 'web',
+ node: {
+ fs: 'empty',
+ },
+ entry: ['./src/index.js'],
+ output: {
+ path: path.resolve(__dirname, 'dist'),
+ filename: 'bundle.js',
+ publicPath: '/',
+ },
+ module: {
+ rules: [
+ {
+ test: /\.(ts|js)x?$/,
+ include: [path.resolve(__dirname, 'src')],
+ use: {
+ loader: 'babel-loader',
+ options: {
+ presets: [
+ '@babel/preset-env',
+ '@babel/preset-react',
+ '@babel/typescript',
+ ],
+ },
+ },
+ resolve: {
+ extensions: ['.ts', '.tsx', '.js', '.jsx', '.json'],
+ },
+ },
+ {
+ test: /\.scss$/,
+ include: [path.resolve(__dirname, 'src')],
+ use: [MiniCssExtractPlugin.loader, 'css-loader', 'sass-loader'],
+ resolve: {
+ extensions: ['.scss'],
+ },
+ },
+ {
+ test: /\.css$/,
+ include: [
+ path.resolve(__dirname, 'src'),
+ path.resolve(__dirname, 'node_modules'),
+ ],
+ use: [MiniCssExtractPlugin.loader, 'css-loader'],
+ resolve: {
+ extensions: ['.css'],
+ },
+ },
+ {
+ test: /\.(eot|woff|woff2|ttf|svg|png|jpg|gif|)$/,
+ use: 'url-loader',
+ },
+ ],
+ },
+ plugins: [
+ new MiniCssExtractPlugin({}),
+ new HtmlWebpackPlugin({
+ // if using template, add Swell and delete line 59.
+ // template: path.resolve(__dirname, "index-csp.html"),
+ filename: 'index.html',
+ title: 'Swell',
+ cspPlugin: {
+ enabled: true,
+ policy: {
+ 'base-uri': "'self'",
+ 'object-src': "'none'",
+ 'script-src': ["'self'"],
+ 'style-src': ["'self'"],
+ },
+ hashEnabled: {
+ 'script-src': true,
+ 'style-src': true,
+ },
+ nonceEnabled: {
+ 'script-src': true,
+ 'style-src': true,
+ },
+ },
+ }),
+ new CspHtmlWebpackPlugin(),
+ // options here: https://github.com/webpack-contrib/webpack-bundle-analyzer
+ // set to true to display bundle breakdown
+ new BundleAnalyzerPlugin({
+ openAnalyzer: false,
+ analyzerMode: 'static',
+ }),
+ ],
+};
diff --git a/webpack.development.js b/webpack.development.js
index 62a66a1d0..4bab11895 100644
--- a/webpack.development.js
+++ b/webpack.development.js
@@ -1,29 +1,29 @@
-const path = require("path");
-const merge = require("webpack-merge");
-const { spawn } = require("child_process");
-const base = require("./webpack.config");
-
-module.exports = merge(base, {
- mode: "development",
- devtool: "source-map",
- devServer: {
- host: "localhost",
- port: "8080",
- hot: true,
- compress: true,
- contentBase: path.resolve(__dirname, "dist"),
- watchContentBase: true,
- watchOptions: {
- ignored: /node_modules/,
- },
- before() {
- spawn("electron", [".", "dev"], {
- shell: true,
- env: process.env,
- stdio: "inherit",
- })
- .on("close", (code) => process.exit(0))
- .on("error", (spawnError) => console.error(spawnError));
- },
- },
-});
+const path = require('path');
+const merge = require('webpack-merge');
+const { spawn } = require('child_process');
+const base = require('./webpack.config');
+
+module.exports = merge(base, {
+ mode: 'development',
+ devtool: 'source-map',
+ devServer: {
+ host: 'localhost',
+ port: '8080',
+ hot: true,
+ compress: true,
+ contentBase: path.resolve(__dirname, 'dist'),
+ watchContentBase: true,
+ watchOptions: {
+ ignored: /node_modules/,
+ },
+ before() {
+ spawn('electron', ['.', 'dev'], {
+ shell: true,
+ env: process.env,
+ stdio: 'inherit',
+ })
+ .on('close', (code) => process.exit(0))
+ .on('error', (spawnError) => console.error(spawnError));
+ },
+ },
+});
diff --git a/webpack.production.js b/webpack.production.js
index 5d3b5f5b5..55ad4bc80 100644
--- a/webpack.production.js
+++ b/webpack.production.js
@@ -1,13 +1,13 @@
-const path = require("path");
-const merge = require("webpack-merge");
-const base = require("./webpack.config");
-
-module.exports = merge(base, {
- mode: "production",
- output: {
- path: path.resolve(__dirname, "dist"),
- filename: "bundle.js",
- publicPath: "./",
- },
- devtool: "nosources-source-map",
-});
+const path = require('path');
+const merge = require('webpack-merge');
+const base = require('./webpack.config');
+
+module.exports = merge(base, {
+ mode: 'production',
+ output: {
+ path: path.resolve(__dirname, 'dist'),
+ filename: 'bundle.js',
+ publicPath: './',
+ },
+ devtool: 'nosources-source-map',
+});