feat: add app connections w/ testing and deleting functions
This commit is contained in:
@@ -1,6 +1,7 @@
|
|||||||
import TwitterApi from 'twitter-api-v2';
|
import TwitterApi from 'twitter-api-v2';
|
||||||
import App from '../../models/app';
|
import App from '../../models/app';
|
||||||
import Field from '../../types/field';
|
import Field from '../../types/field';
|
||||||
|
import appData from './info';
|
||||||
|
|
||||||
export default class Twitter {
|
export default class Twitter {
|
||||||
client: any
|
client: any
|
||||||
@@ -16,7 +17,7 @@ export default class Twitter {
|
|||||||
});
|
});
|
||||||
|
|
||||||
this.connectionData = connectionData;
|
this.connectionData = connectionData;
|
||||||
this.appData = App.findOneByKey('twitter');
|
this.appData = appData;
|
||||||
}
|
}
|
||||||
|
|
||||||
async createAuthLink() {
|
async createAuthLink() {
|
||||||
|
@@ -1,5 +1,6 @@
|
|||||||
import { GraphQLString, GraphQLNonNull } from 'graphql';
|
import { GraphQLString, GraphQLNonNull } from 'graphql';
|
||||||
import Connection from '../../models/connection';
|
import Connection from '../../models/connection';
|
||||||
|
import App from '../../models/app';
|
||||||
import connectionType from '../types/connection';
|
import connectionType from '../types/connection';
|
||||||
import twitterCredentialInputType from '../types/twitter-credential-input';
|
import twitterCredentialInputType from '../types/twitter-credential-input';
|
||||||
import RequestWithCurrentUser from '../../types/express/request-with-current-user';
|
import RequestWithCurrentUser from '../../types/express/request-with-current-user';
|
||||||
@@ -9,13 +10,17 @@ type Params = {
|
|||||||
data: object
|
data: object
|
||||||
}
|
}
|
||||||
const createConnectionResolver = async (params: Params, req: RequestWithCurrentUser) => {
|
const createConnectionResolver = async (params: Params, req: RequestWithCurrentUser) => {
|
||||||
|
const app = await App.findOneByKey(params.key);
|
||||||
const connection = await Connection.query().insert({
|
const connection = await Connection.query().insert({
|
||||||
key: params.key,
|
key: params.key,
|
||||||
data: params.data,
|
data: params.data,
|
||||||
userId: req.currentUser.id
|
userId: req.currentUser.id
|
||||||
});
|
});
|
||||||
|
|
||||||
return connection;
|
return {
|
||||||
|
...connection,
|
||||||
|
app,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
const createConnection = {
|
const createConnection = {
|
||||||
|
@@ -1,5 +1,6 @@
|
|||||||
import { GraphQLList, GraphQLString, GraphQLNonNull } from 'graphql';
|
import { GraphQLList, GraphQLString, GraphQLNonNull } from 'graphql';
|
||||||
import Connection from '../../models/connection';
|
import Connection from '../../models/connection';
|
||||||
|
import App from '../../models/app';
|
||||||
import RequestWithCurrentUser from '../../types/express/request-with-current-user';
|
import RequestWithCurrentUser from '../../types/express/request-with-current-user';
|
||||||
import connectionType from '../types/connection';
|
import connectionType from '../types/connection';
|
||||||
|
|
||||||
@@ -8,10 +9,14 @@ type Params = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const getAppConnectionsResolver = async (params: Params, req: RequestWithCurrentUser) => {
|
const getAppConnectionsResolver = async (params: Params, req: RequestWithCurrentUser) => {
|
||||||
|
const app = await App.findOneByKey(params.key);
|
||||||
const connections = await Connection.query()
|
const connections = await Connection.query()
|
||||||
.where({ user_id: req.currentUser.id, verified: true, key: params.key })
|
.where({ user_id: req.currentUser.id, key: params.key })
|
||||||
|
|
||||||
return connections;
|
return connections.map((connection: any) => ({
|
||||||
|
...connection,
|
||||||
|
app,
|
||||||
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
const getAppConnections = {
|
const getAppConnections = {
|
||||||
|
@@ -1,17 +1,32 @@
|
|||||||
import { GraphQLString, GraphQLNonNull } from 'graphql';
|
import { GraphQLString, GraphQLNonNull } from 'graphql';
|
||||||
import App from '../../models/app';
|
import App from '../../models/app';
|
||||||
import appType from '../types/app';
|
import appType from '../types/app';
|
||||||
|
import RequestWithCurrentUser from '../../types/express/request-with-current-user';
|
||||||
|
import Connection from '../../models/connection';
|
||||||
|
import connectionType from '../types/connection';
|
||||||
|
|
||||||
type Params = {
|
type Params = {
|
||||||
key: string
|
key: string
|
||||||
}
|
}
|
||||||
|
|
||||||
const getAppResolver = (params: Params) => {
|
const getAppResolver = async (params: Params, req: RequestWithCurrentUser) => {
|
||||||
if(!params.key) {
|
if(!params.key) {
|
||||||
throw new Error('No key provided.')
|
throw new Error('No key provided.')
|
||||||
}
|
}
|
||||||
|
|
||||||
return App.findOneByKey(params.key)
|
const app = await App.findOneByKey(params.key);
|
||||||
|
|
||||||
|
if (req.currentUser?.id) {
|
||||||
|
const connections = await Connection.query()
|
||||||
|
.where({ user_id: req.currentUser.id, key: params.key });
|
||||||
|
|
||||||
|
return {
|
||||||
|
...app,
|
||||||
|
connections,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return app;
|
||||||
}
|
}
|
||||||
|
|
||||||
const getApp = {
|
const getApp = {
|
||||||
@@ -19,9 +34,7 @@ const getApp = {
|
|||||||
args: {
|
args: {
|
||||||
key: { type: GraphQLNonNull(GraphQLString) },
|
key: { type: GraphQLNonNull(GraphQLString) },
|
||||||
},
|
},
|
||||||
resolve: (_: any, params: Params) => getAppResolver(params)
|
resolve: (_: any, params: Params, req: RequestWithCurrentUser) => getAppResolver(params, req)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
export default getApp;
|
export default getApp;
|
||||||
|
@@ -15,7 +15,7 @@ const testConnectionResolver = async (params: Params, req: RequestWithCurrentUse
|
|||||||
|
|
||||||
const appClass = (await import(`../../apps/${connection.key}`)).default;
|
const appClass = (await import(`../../apps/${connection.key}`)).default;
|
||||||
|
|
||||||
const appInstance = new appClass(connection.data)
|
const appInstance = new appClass(connection.data);
|
||||||
const isStillVerified = await appInstance.isStillVerified();
|
const isStillVerified = await appInstance.isStillVerified();
|
||||||
|
|
||||||
connection = await connection.$query().patchAndFetch({
|
connection = await connection.$query().patchAndFetch({
|
||||||
|
@@ -4,15 +4,20 @@ import authenticationStepType from './authentication-step';
|
|||||||
|
|
||||||
const appType = new GraphQLObjectType({
|
const appType = new GraphQLObjectType({
|
||||||
name: 'App',
|
name: 'App',
|
||||||
fields: {
|
fields: () => {
|
||||||
name: { type: GraphQLString },
|
const connectionType = require('./connection').default;
|
||||||
key: { type: GraphQLString },
|
|
||||||
connectionCount: { type: GraphQLInt },
|
return {
|
||||||
iconUrl: { type: GraphQLString },
|
name: { type: GraphQLString },
|
||||||
docUrl: { type: GraphQLString },
|
key: { type: GraphQLString },
|
||||||
primaryColor: { type: GraphQLString },
|
connectionCount: { type: GraphQLInt },
|
||||||
fields: { type: GraphQLList(fieldType) },
|
iconUrl: { type: GraphQLString },
|
||||||
authenticationSteps: { type: GraphQLList(authenticationStepType) }
|
docUrl: { type: GraphQLString },
|
||||||
|
primaryColor: { type: GraphQLString },
|
||||||
|
fields: { type: GraphQLList(fieldType) },
|
||||||
|
authenticationSteps: { type: GraphQLList(authenticationStepType) },
|
||||||
|
connections: { type: GraphQLList(connectionType) },
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@@ -3,11 +3,16 @@ import connectionDataType from './connection-data';
|
|||||||
|
|
||||||
const connectionType = new GraphQLObjectType({
|
const connectionType = new GraphQLObjectType({
|
||||||
name: 'connection',
|
name: 'connection',
|
||||||
fields: {
|
fields: () => {
|
||||||
id: { type: GraphQLString },
|
const appType = require('./app').default;
|
||||||
key: { type: GraphQLString },
|
|
||||||
data: { type: connectionDataType },
|
return {
|
||||||
verified: { type: GraphQLBoolean },
|
id: { type: GraphQLString },
|
||||||
|
key: { type: GraphQLString },
|
||||||
|
data: { type: connectionDataType },
|
||||||
|
verified: { type: GraphQLBoolean },
|
||||||
|
app: { type: appType }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
import { useCallback, useState } from 'react';
|
import { useState } from 'react';
|
||||||
import { useQuery } from '@apollo/client';
|
import { useQuery } from '@apollo/client';
|
||||||
import { Link } from 'react-router-dom';
|
import { Link } from 'react-router-dom';
|
||||||
import DialogTitle from '@mui/material/DialogTitle';
|
import DialogTitle from '@mui/material/DialogTitle';
|
||||||
|
@@ -0,0 +1,57 @@
|
|||||||
|
import * as React from 'react';
|
||||||
|
import { Link } from 'react-router-dom';
|
||||||
|
import Menu from '@mui/material/Menu';
|
||||||
|
import type { PopoverProps } from '@mui/material/Popover';
|
||||||
|
import MenuItem from '@mui/material/MenuItem';
|
||||||
|
|
||||||
|
import * as URLS from 'config/urls';
|
||||||
|
import useFormatMessage from 'hooks/useFormatMessage';
|
||||||
|
|
||||||
|
type Action = {
|
||||||
|
type: 'test' | 'reconnect' | 'delete' | 'viewFlows';
|
||||||
|
};
|
||||||
|
|
||||||
|
type ContextMenuProps = {
|
||||||
|
appKey: string;
|
||||||
|
onClose: () => void;
|
||||||
|
onMenuItemClick: (event: React.MouseEvent, action: Action) => void;
|
||||||
|
anchorEl: PopoverProps['anchorEl'];
|
||||||
|
};
|
||||||
|
|
||||||
|
export default function ContextMenu(props: ContextMenuProps) {
|
||||||
|
const { appKey, onClose, onMenuItemClick, anchorEl } = props;
|
||||||
|
const formatMessage = useFormatMessage();
|
||||||
|
|
||||||
|
const createActionHandler = React.useCallback((action: Action) => {
|
||||||
|
return function clickHandler(event: React.MouseEvent) {
|
||||||
|
onMenuItemClick(event, action);
|
||||||
|
|
||||||
|
onClose();
|
||||||
|
};
|
||||||
|
}, [onMenuItemClick, onClose]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Menu
|
||||||
|
open={true}
|
||||||
|
onClose={onClose}
|
||||||
|
hideBackdrop={false}
|
||||||
|
anchorEl={anchorEl}
|
||||||
|
>
|
||||||
|
<MenuItem component={Link} to={URLS.APP_FLOWS(appKey)} onClick={createActionHandler({ type: 'viewFlows' })}>
|
||||||
|
{formatMessage('connection.viewFlows')}
|
||||||
|
</MenuItem>
|
||||||
|
|
||||||
|
<MenuItem onClick={createActionHandler({ type: 'test' })}>
|
||||||
|
{formatMessage('connection.testConnection')}
|
||||||
|
</MenuItem>
|
||||||
|
|
||||||
|
<MenuItem onClick={createActionHandler({ type: 'reconnect' })}>
|
||||||
|
{formatMessage('connection.reconnect')}
|
||||||
|
</MenuItem>
|
||||||
|
|
||||||
|
<MenuItem onClick={createActionHandler({ type: 'delete' })}>
|
||||||
|
{formatMessage('connection.delete')}
|
||||||
|
</MenuItem>
|
||||||
|
</Menu>
|
||||||
|
);
|
||||||
|
};
|
94
packages/web/src/components/AppConnectionRow/index.tsx
Normal file
94
packages/web/src/components/AppConnectionRow/index.tsx
Normal file
@@ -0,0 +1,94 @@
|
|||||||
|
import * as React from 'react';
|
||||||
|
import { useLazyQuery, useMutation } from '@apollo/client';
|
||||||
|
import Card from '@mui/material/Card';
|
||||||
|
import Box from '@mui/material/Box';
|
||||||
|
import CardActionArea from '@mui/material/CardActionArea';
|
||||||
|
import MoreHorizIcon from '@mui/icons-material/MoreHoriz';
|
||||||
|
|
||||||
|
import { DELETE_CONNECTION } from 'graphql/mutations/delete-connection';
|
||||||
|
import { TEST_CONNECTION } from 'graphql/queries/test-connection';
|
||||||
|
import ConnectionContextMenu from 'components/AppConnectionContextMenu';
|
||||||
|
import useFormatMessage from 'hooks/useFormatMessage';
|
||||||
|
import type { Connection } from 'types/connection';
|
||||||
|
import { CardContent, Typography } from './style';
|
||||||
|
|
||||||
|
type AppConnectionRowProps = {
|
||||||
|
connection: Connection;
|
||||||
|
}
|
||||||
|
|
||||||
|
const countTranslation = (value: React.ReactNode) => (<><strong>{value}</strong><br /></>);
|
||||||
|
|
||||||
|
function AppConnectionRow(props: AppConnectionRowProps) {
|
||||||
|
const [testConnection, { data: testData, called: testCalled, loading: testLoading }] = useLazyQuery(TEST_CONNECTION);
|
||||||
|
const [deleteConnection] = useMutation(DELETE_CONNECTION);
|
||||||
|
|
||||||
|
const formatMessage = useFormatMessage();
|
||||||
|
const { id, key, data } = props.connection;
|
||||||
|
|
||||||
|
const contextButtonRef = React.useRef<SVGSVGElement | null>(null);
|
||||||
|
const [anchorEl, setAnchorEl] = React.useState<SVGSVGElement | null>(null);
|
||||||
|
|
||||||
|
const handleClose = () => {
|
||||||
|
setAnchorEl(null);
|
||||||
|
};
|
||||||
|
|
||||||
|
const onContextMenuClick = (event: React.MouseEvent<HTMLButtonElement>) => setAnchorEl(contextButtonRef.current);
|
||||||
|
const onContextMenuAction = React.useCallback((event, action: { [key: string]: string }) => {
|
||||||
|
if (action.type === 'delete') {
|
||||||
|
deleteConnection({
|
||||||
|
variables: { id },
|
||||||
|
update: (cache, mutationResult) => {
|
||||||
|
const connectionCacheId = cache.identify({
|
||||||
|
__typename: 'connection',
|
||||||
|
id,
|
||||||
|
});
|
||||||
|
|
||||||
|
cache.evict({
|
||||||
|
id: connectionCacheId,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else if (action.type === 'test') {
|
||||||
|
testConnection({ variables: { id } });
|
||||||
|
}
|
||||||
|
}, [deleteConnection, id, testConnection]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Card sx={{ my: 2 }}>
|
||||||
|
<CardActionArea onClick={onContextMenuClick}>
|
||||||
|
<CardContent>
|
||||||
|
<Box>
|
||||||
|
<Typography variant="h6">
|
||||||
|
{data.screenName}
|
||||||
|
</Typography>
|
||||||
|
</Box>
|
||||||
|
|
||||||
|
<Box>
|
||||||
|
{testCalled && !testLoading && (testData ? 'yes' : 'no')}
|
||||||
|
</Box>
|
||||||
|
|
||||||
|
<Box sx={{ px: 2 }}>
|
||||||
|
<Typography variant="body2">
|
||||||
|
{formatMessage('connection.flowCount', { count: countTranslation(0) })}
|
||||||
|
</Typography>
|
||||||
|
</Box>
|
||||||
|
|
||||||
|
<Box>
|
||||||
|
<MoreHorizIcon ref={contextButtonRef} />
|
||||||
|
</Box>
|
||||||
|
</CardContent>
|
||||||
|
</CardActionArea>
|
||||||
|
</Card>
|
||||||
|
|
||||||
|
{anchorEl && <ConnectionContextMenu
|
||||||
|
appKey={key}
|
||||||
|
onClose={handleClose}
|
||||||
|
onMenuItemClick={onContextMenuAction}
|
||||||
|
anchorEl={anchorEl}
|
||||||
|
/>}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default AppConnectionRow;
|
17
packages/web/src/components/AppConnectionRow/style.ts
Normal file
17
packages/web/src/components/AppConnectionRow/style.ts
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
import { styled } from '@mui/material/styles';
|
||||||
|
import MuiCardContent from '@mui/material/CardContent';
|
||||||
|
import MuiTypography from '@mui/material/Typography';
|
||||||
|
|
||||||
|
export const CardContent = styled(MuiCardContent)(({ theme }) => ({
|
||||||
|
display: 'grid',
|
||||||
|
gridTemplateRows: 'auto',
|
||||||
|
gridTemplateColumns: '1fr auto auto auto',
|
||||||
|
gridColumnGap: theme.spacing(2),
|
||||||
|
alignItems: 'center',
|
||||||
|
}));
|
||||||
|
|
||||||
|
|
||||||
|
export const Typography = styled(MuiTypography)(({ theme }) => ({
|
||||||
|
textAlign: 'center',
|
||||||
|
display: 'inline-block',
|
||||||
|
}));
|
23
packages/web/src/components/AppConnections/index.tsx
Normal file
23
packages/web/src/components/AppConnections/index.tsx
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
import { useQuery } from '@apollo/client';
|
||||||
|
import { GET_APP_CONNECTIONS } from 'graphql/queries/get-app-connections';
|
||||||
|
|
||||||
|
import AppConnectionRow from 'components/AppConnectionRow';
|
||||||
|
import type { Connection } from 'types/connection';
|
||||||
|
|
||||||
|
type AppConnectionsProps = {
|
||||||
|
appKey: String;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function AppConnections(props: AppConnectionsProps) {
|
||||||
|
const { appKey: key } = props;
|
||||||
|
const { data } = useQuery(GET_APP_CONNECTIONS, { variables: { key } });
|
||||||
|
const appConnections: Connection[] = data?.getApp?.connections || [];
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{appConnections.map((appConnection: Connection) => (
|
||||||
|
<AppConnectionRow key={appConnection.id} connection={appConnection} />
|
||||||
|
))}
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
};
|
@@ -24,7 +24,7 @@ function AppRow(props: AppRowProps) {
|
|||||||
<Link to={URLS.APP(name.toLowerCase())}>
|
<Link to={URLS.APP(name.toLowerCase())}>
|
||||||
<Card sx={{ my: 2 }}>
|
<Card sx={{ my: 2 }}>
|
||||||
<CardActionArea>
|
<CardActionArea>
|
||||||
<CardContent>
|
<CardContent>
|
||||||
<Box>
|
<Box>
|
||||||
<AppIcon name={name} url={iconUrl} color={primaryColor} />
|
<AppIcon name={name} url={iconUrl} color={primaryColor} />
|
||||||
</Box>
|
</Box>
|
||||||
|
@@ -19,12 +19,10 @@ export default function InputCreator(props: InputCreatorProps) {
|
|||||||
const {
|
const {
|
||||||
key: name,
|
key: name,
|
||||||
label,
|
label,
|
||||||
type,
|
|
||||||
required,
|
required,
|
||||||
readOnly,
|
readOnly,
|
||||||
value,
|
value,
|
||||||
description,
|
description,
|
||||||
docUrl,
|
|
||||||
clickToCopy,
|
clickToCopy,
|
||||||
} = schema;
|
} = schema;
|
||||||
|
|
||||||
|
@@ -9,7 +9,7 @@ type LayoutProps = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export default function Layout({ children }: LayoutProps) {
|
export default function Layout({ children }: LayoutProps) {
|
||||||
const [isDrawerOpen, setDrawerOpen] = useState(false);
|
const [isDrawerOpen, setDrawerOpen] = useState(true);
|
||||||
const onMenuClick = useCallback(() => { setDrawerOpen(value => !value) }, []);
|
const onMenuClick = useCallback(() => { setDrawerOpen(value => !value) }, []);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@@ -4,6 +4,31 @@ const cache = new InMemoryCache({
|
|||||||
typePolicies: {
|
typePolicies: {
|
||||||
App: {
|
App: {
|
||||||
keyFields: ['key']
|
keyFields: ['key']
|
||||||
|
},
|
||||||
|
Mutation: {
|
||||||
|
mutationType: true,
|
||||||
|
fields: {
|
||||||
|
createConnection: {
|
||||||
|
merge(existing, newConnection, { args, readField, cache }) {
|
||||||
|
const appKey = readField('key', newConnection);
|
||||||
|
const appCacheId = cache.identify({
|
||||||
|
__typename: 'App',
|
||||||
|
key: appKey,
|
||||||
|
});
|
||||||
|
|
||||||
|
cache.modify({
|
||||||
|
id: appCacheId,
|
||||||
|
fields: {
|
||||||
|
connections: (existingConnections) => {
|
||||||
|
return [...existingConnections, newConnection];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return newConnection;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@@ -3,8 +3,15 @@ import { gql } from '@apollo/client';
|
|||||||
export const CREATE_CONNECTION = gql`
|
export const CREATE_CONNECTION = gql`
|
||||||
mutation CreateConnection($key: String!, $data: twitterCredentialInput!) {
|
mutation CreateConnection($key: String!, $data: twitterCredentialInput!) {
|
||||||
createConnection(key: $key, data: $data) {
|
createConnection(key: $key, data: $data) {
|
||||||
key
|
|
||||||
id
|
id
|
||||||
|
key
|
||||||
|
verified
|
||||||
|
data {
|
||||||
|
screenName
|
||||||
|
}
|
||||||
|
app {
|
||||||
|
key
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
7
packages/web/src/graphql/mutations/delete-connection.ts
Normal file
7
packages/web/src/graphql/mutations/delete-connection.ts
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
import { gql } from '@apollo/client';
|
||||||
|
|
||||||
|
export const DELETE_CONNECTION = gql`
|
||||||
|
mutation DeleteConnection($id: String!) {
|
||||||
|
deleteConnection(id: $id)
|
||||||
|
}
|
||||||
|
`;
|
@@ -1,6 +1,7 @@
|
|||||||
import { CREATE_CONNECTION } from './create-connection';
|
import { CREATE_CONNECTION } from './create-connection';
|
||||||
import { CREATE_AUTH_LINK } from './create-auth-link';
|
|
||||||
import { UPDATE_CONNECTION } from './update-connection';
|
import { UPDATE_CONNECTION } from './update-connection';
|
||||||
|
import { DELETE_CONNECTION } from './delete-connection';
|
||||||
|
import { CREATE_AUTH_LINK } from './create-auth-link';
|
||||||
|
|
||||||
type Mutations = {
|
type Mutations = {
|
||||||
[key: string]: any,
|
[key: string]: any,
|
||||||
@@ -9,6 +10,7 @@ type Mutations = {
|
|||||||
const mutations: Mutations = {
|
const mutations: Mutations = {
|
||||||
createConnection: CREATE_CONNECTION,
|
createConnection: CREATE_CONNECTION,
|
||||||
updateConnection: UPDATE_CONNECTION,
|
updateConnection: UPDATE_CONNECTION,
|
||||||
|
deleteConnection: DELETE_CONNECTION,
|
||||||
createAuthLink: CREATE_AUTH_LINK,
|
createAuthLink: CREATE_AUTH_LINK,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@@ -6,6 +6,9 @@ export const UPDATE_CONNECTION = gql`
|
|||||||
id
|
id
|
||||||
key
|
key
|
||||||
verified
|
verified
|
||||||
|
data {
|
||||||
|
screenName
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
17
packages/web/src/graphql/queries/get-app-connections.ts
Normal file
17
packages/web/src/graphql/queries/get-app-connections.ts
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
import { gql } from '@apollo/client';
|
||||||
|
|
||||||
|
export const GET_APP_CONNECTIONS = gql`
|
||||||
|
query GetAppConnections($key: String!) {
|
||||||
|
getApp(key: $key) {
|
||||||
|
key
|
||||||
|
connections {
|
||||||
|
id
|
||||||
|
key
|
||||||
|
verified
|
||||||
|
data {
|
||||||
|
screenName
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
@@ -32,6 +32,9 @@ export const GET_APP = gql`
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
connections {
|
||||||
|
id
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
@@ -19,6 +19,9 @@ export const GET_APPS = gql`
|
|||||||
docUrl
|
docUrl
|
||||||
clickToCopy
|
clickToCopy
|
||||||
}
|
}
|
||||||
|
connections {
|
||||||
|
id
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
`;
|
`;
|
7
packages/web/src/graphql/queries/test-connection.ts
Normal file
7
packages/web/src/graphql/queries/test-connection.ts
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
import { gql } from '@apollo/client';
|
||||||
|
|
||||||
|
export const TEST_CONNECTION = gql`
|
||||||
|
query TestConnection($id: String!) {
|
||||||
|
testConnection(id: $id)
|
||||||
|
}
|
||||||
|
`;
|
@@ -15,5 +15,10 @@
|
|||||||
"apps.title": "Apps",
|
"apps.title": "Apps",
|
||||||
"apps.addConnection": "Add connection",
|
"apps.addConnection": "Add connection",
|
||||||
"apps.addNewAppConnection": "Add a new app connection",
|
"apps.addNewAppConnection": "Add a new app connection",
|
||||||
"apps.searchApp": "Search for app"
|
"apps.searchApp": "Search for app",
|
||||||
|
"connection.flowCount": "{count} flows",
|
||||||
|
"connection.viewFlows": "View flows",
|
||||||
|
"connection.testConnection": "Test connection",
|
||||||
|
"connection.reconnect": "Reconnect",
|
||||||
|
"connection.delete": "Delete"
|
||||||
}
|
}
|
@@ -12,6 +12,7 @@ import useFormatMessage from 'hooks/useFormatMessage';
|
|||||||
import { GET_APP } from 'graphql/queries/get-app';
|
import { GET_APP } from 'graphql/queries/get-app';
|
||||||
import * as URLS from 'config/urls';
|
import * as URLS from 'config/urls';
|
||||||
|
|
||||||
|
import AppConnections from 'components/AppConnections';
|
||||||
import AddAppConnection from 'components/AddAppConnection';
|
import AddAppConnection from 'components/AddAppConnection';
|
||||||
import AppIcon from 'components/AppIcon';
|
import AppIcon from 'components/AppIcon';
|
||||||
import Container from 'components/Container';
|
import Container from 'components/Container';
|
||||||
@@ -78,11 +79,11 @@ export default function Application() {
|
|||||||
|
|
||||||
<Switch>
|
<Switch>
|
||||||
<Route path={URLS.APP_FLOWS_PATTERN}>
|
<Route path={URLS.APP_FLOWS_PATTERN}>
|
||||||
Flows come here.
|
Flows
|
||||||
</Route>
|
</Route>
|
||||||
|
|
||||||
<Route path={URLS.APP_CONNECTIONS_PATTERN}>
|
<Route path={URLS.APP_CONNECTIONS_PATTERN}>
|
||||||
Connections come here.
|
<AppConnections appKey={key} />
|
||||||
</Route>
|
</Route>
|
||||||
|
|
||||||
<Route exact path={URLS.APP_PATTERN}>
|
<Route exact path={URLS.APP_PATTERN}>
|
||||||
|
12
packages/web/src/types/connection.ts
Normal file
12
packages/web/src/types/connection.ts
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
type ConnectionData = {
|
||||||
|
screenName: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
type Connection = {
|
||||||
|
id: string;
|
||||||
|
key: string;
|
||||||
|
data: ConnectionData;
|
||||||
|
verified: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type { Connection, ConnectionData };
|
Reference in New Issue
Block a user