Merge pull request #1802 from automatisch/AUT-688
refactor: rewrite get connected apps with RQ
This commit is contained in:
@@ -1,67 +0,0 @@
|
|||||||
import App from '../../models/app.js';
|
|
||||||
import Flow from '../../models/flow.js';
|
|
||||||
import Connection from '../../models/connection.js';
|
|
||||||
|
|
||||||
const getConnectedApps = async (_parent, params, context) => {
|
|
||||||
const conditions = context.currentUser.can('read', 'Connection');
|
|
||||||
|
|
||||||
const userConnections = context.currentUser.$relatedQuery('connections');
|
|
||||||
const allConnections = Connection.query();
|
|
||||||
const connectionBaseQuery = conditions.isCreator
|
|
||||||
? userConnections
|
|
||||||
: allConnections;
|
|
||||||
|
|
||||||
const userFlows = context.currentUser.$relatedQuery('flows');
|
|
||||||
const allFlows = Flow.query();
|
|
||||||
const flowBaseQuery = conditions.isCreator ? userFlows : allFlows;
|
|
||||||
|
|
||||||
let apps = await App.findAll(params.name);
|
|
||||||
|
|
||||||
const connections = await connectionBaseQuery
|
|
||||||
.clone()
|
|
||||||
.select('connections.key')
|
|
||||||
.where({ draft: false })
|
|
||||||
.count('connections.id as count')
|
|
||||||
.groupBy('connections.key');
|
|
||||||
|
|
||||||
const flows = await flowBaseQuery
|
|
||||||
.clone()
|
|
||||||
.withGraphJoined('steps')
|
|
||||||
.orderBy('created_at', 'desc');
|
|
||||||
|
|
||||||
const duplicatedUsedApps = flows
|
|
||||||
.map((flow) => flow.steps.map((step) => step.appKey))
|
|
||||||
.flat()
|
|
||||||
.filter(Boolean);
|
|
||||||
|
|
||||||
const connectionKeys = connections.map((connection) => connection.key);
|
|
||||||
const usedApps = [...new Set([...duplicatedUsedApps, ...connectionKeys])];
|
|
||||||
|
|
||||||
apps = apps
|
|
||||||
.filter((app) => {
|
|
||||||
return usedApps.includes(app.key);
|
|
||||||
})
|
|
||||||
.map((app) => {
|
|
||||||
const connection = connections.find(
|
|
||||||
(connection) => connection.key === app.key
|
|
||||||
);
|
|
||||||
|
|
||||||
app.connectionCount = connection?.count || 0;
|
|
||||||
app.flowCount = 0;
|
|
||||||
|
|
||||||
flows.forEach((flow) => {
|
|
||||||
const usedFlow = flow.steps.find((step) => step.appKey === app.key);
|
|
||||||
|
|
||||||
if (usedFlow) {
|
|
||||||
app.flowCount += 1;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return app;
|
|
||||||
})
|
|
||||||
.sort((appA, appB) => appA.name.localeCompare(appB.name));
|
|
||||||
|
|
||||||
return apps;
|
|
||||||
};
|
|
||||||
|
|
||||||
export default getConnectedApps;
|
|
@@ -1,7 +0,0 @@
|
|||||||
import getConnectedApps from './queries/get-connected-apps.js';
|
|
||||||
|
|
||||||
const queryResolvers = {
|
|
||||||
getConnectedApps,
|
|
||||||
};
|
|
||||||
|
|
||||||
export default queryResolvers;
|
|
@@ -1,8 +1,6 @@
|
|||||||
import mutationResolvers from './mutation-resolvers.js';
|
import mutationResolvers from './mutation-resolvers.js';
|
||||||
import queryResolvers from './query-resolvers.js';
|
|
||||||
|
|
||||||
const resolvers = {
|
const resolvers = {
|
||||||
Query: queryResolvers,
|
|
||||||
Mutation: mutationResolvers,
|
Mutation: mutationResolvers,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@@ -1,7 +1,6 @@
|
|||||||
type Query {
|
type Query {
|
||||||
getConnectedApps(name: String): [App]
|
placeholderQuery(name: String): Boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
type Mutation {
|
type Mutation {
|
||||||
createAppConfig(input: CreateAppConfigInput): AppConfig
|
createAppConfig(input: CreateAppConfigInput): AppConfig
|
||||||
createAppAuthClient(input: CreateAppAuthClientInput): AppAuthClient
|
createAppAuthClient(input: CreateAppAuthClientInput): AppAuthClient
|
||||||
|
@@ -40,9 +40,6 @@ export const authenticateUser = async (request, response, next) => {
|
|||||||
const isAuthenticatedRule = rule()(isAuthenticated);
|
const isAuthenticatedRule = rule()(isAuthenticated);
|
||||||
|
|
||||||
export const authenticationRules = {
|
export const authenticationRules = {
|
||||||
Query: {
|
|
||||||
'*': isAuthenticatedRule,
|
|
||||||
},
|
|
||||||
Mutation: {
|
Mutation: {
|
||||||
'*': isAuthenticatedRule,
|
'*': isAuthenticatedRule,
|
||||||
forgotPassword: allow,
|
forgotPassword: allow,
|
||||||
|
@@ -42,19 +42,21 @@ describe('authentication rules', () => {
|
|||||||
|
|
||||||
const { queries, mutations } = getQueryAndMutationNames(authenticationRules);
|
const { queries, mutations } = getQueryAndMutationNames(authenticationRules);
|
||||||
|
|
||||||
describe('for queries', () => {
|
if (queries.length) {
|
||||||
queries.forEach((query) => {
|
describe('for queries', () => {
|
||||||
it(`should apply correct rule for query: ${query}`, () => {
|
queries.forEach((query) => {
|
||||||
const ruleApplied = authenticationRules.Query[query];
|
it(`should apply correct rule for query: ${query}`, () => {
|
||||||
|
const ruleApplied = authenticationRules.Query[query];
|
||||||
|
|
||||||
if (query === '*') {
|
if (query === '*') {
|
||||||
expect(ruleApplied.func).toBe(isAuthenticated);
|
expect(ruleApplied.func).toBe(isAuthenticated);
|
||||||
} else {
|
} else {
|
||||||
expect(ruleApplied).toEqual(allow);
|
expect(ruleApplied).toEqual(allow);
|
||||||
}
|
}
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
}
|
||||||
|
|
||||||
describe('for mutations', () => {
|
describe('for mutations', () => {
|
||||||
mutations.forEach((mutation) => {
|
mutations.forEach((mutation) => {
|
||||||
|
@@ -1,15 +0,0 @@
|
|||||||
import { gql } from '@apollo/client';
|
|
||||||
export const GET_CONNECTED_APPS = gql`
|
|
||||||
query GetConnectedApps($name: String) {
|
|
||||||
getConnectedApps(name: $name) {
|
|
||||||
key
|
|
||||||
name
|
|
||||||
iconUrl
|
|
||||||
docUrl
|
|
||||||
primaryColor
|
|
||||||
connectionCount
|
|
||||||
flowCount
|
|
||||||
supportsConnections
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`;
|
|
25
packages/web/src/hooks/useUserApps.js
Normal file
25
packages/web/src/hooks/useUserApps.js
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
import { useQuery } from '@tanstack/react-query';
|
||||||
|
import useCurrentUser from 'hooks/useCurrentUser';
|
||||||
|
import api from 'helpers/api';
|
||||||
|
|
||||||
|
export default function useUserApps(appName) {
|
||||||
|
const { data } = useCurrentUser();
|
||||||
|
const userId = data?.data.id;
|
||||||
|
|
||||||
|
const query = useQuery({
|
||||||
|
queryKey: ['users', userId, 'apps', appName],
|
||||||
|
queryFn: async ({ signal }) => {
|
||||||
|
const { data } = await api.get(`/v1/users/${userId}/apps`, {
|
||||||
|
signal,
|
||||||
|
params: {
|
||||||
|
...(appName && { name: appName }),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
return data;
|
||||||
|
},
|
||||||
|
enabled: !!userId,
|
||||||
|
});
|
||||||
|
|
||||||
|
return query;
|
||||||
|
}
|
@@ -1,6 +1,5 @@
|
|||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import { Link, Routes, Route, useNavigate } from 'react-router-dom';
|
import { Link, Routes, Route, useNavigate } from 'react-router-dom';
|
||||||
import { useQuery } from '@apollo/client';
|
|
||||||
import Box from '@mui/material/Box';
|
import Box from '@mui/material/Box';
|
||||||
import Grid from '@mui/material/Grid';
|
import Grid from '@mui/material/Grid';
|
||||||
import Divider from '@mui/material/Divider';
|
import Divider from '@mui/material/Divider';
|
||||||
@@ -15,23 +14,25 @@ import PageTitle from 'components/PageTitle';
|
|||||||
import AppRow from 'components/AppRow';
|
import AppRow from 'components/AppRow';
|
||||||
import SearchInput from 'components/SearchInput';
|
import SearchInput from 'components/SearchInput';
|
||||||
import useFormatMessage from 'hooks/useFormatMessage';
|
import useFormatMessage from 'hooks/useFormatMessage';
|
||||||
import { GET_CONNECTED_APPS } from 'graphql/queries/get-connected-apps';
|
|
||||||
import * as URLS from 'config/urls';
|
import * as URLS from 'config/urls';
|
||||||
|
import useUserApps from 'hooks/useUserApps';
|
||||||
|
|
||||||
export default function Applications() {
|
export default function Applications() {
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const formatMessage = useFormatMessage();
|
const formatMessage = useFormatMessage();
|
||||||
const [appName, setAppName] = React.useState(null);
|
const [appName, setAppName] = React.useState(null);
|
||||||
const { data, loading } = useQuery(GET_CONNECTED_APPS, {
|
const { data, isLoading } = useUserApps(appName);
|
||||||
variables: { name: appName },
|
const apps = data?.data;
|
||||||
});
|
|
||||||
const apps = data?.getConnectedApps;
|
|
||||||
const hasApps = apps?.length;
|
const hasApps = apps?.length;
|
||||||
|
|
||||||
const onSearchChange = React.useCallback((event) => {
|
const onSearchChange = React.useCallback((event) => {
|
||||||
setAppName(event.target.value);
|
setAppName(event.target.value);
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const goToApps = React.useCallback(() => {
|
const goToApps = React.useCallback(() => {
|
||||||
navigate(URLS.APPS);
|
navigate(URLS.APPS);
|
||||||
}, [navigate]);
|
}, [navigate]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box sx={{ py: 3 }}>
|
<Box sx={{ py: 3 }}>
|
||||||
<Container>
|
<Container>
|
||||||
@@ -75,21 +76,21 @@ export default function Applications() {
|
|||||||
|
|
||||||
<Divider sx={{ mt: [2, 0], mb: 2 }} />
|
<Divider sx={{ mt: [2, 0], mb: 2 }} />
|
||||||
|
|
||||||
{loading && (
|
{isLoading && (
|
||||||
<CircularProgress
|
<CircularProgress
|
||||||
data-test="apps-loader"
|
data-test="apps-loader"
|
||||||
sx={{ display: 'block', margin: '20px auto' }}
|
sx={{ display: 'block', margin: '20px auto' }}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{!loading && !hasApps && (
|
{!isLoading && !hasApps && (
|
||||||
<NoResultFound
|
<NoResultFound
|
||||||
text={formatMessage('apps.noConnections')}
|
text={formatMessage('apps.noConnections')}
|
||||||
to={URLS.NEW_APP_CONNECTION}
|
to={URLS.NEW_APP_CONNECTION}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{!loading &&
|
{!isLoading &&
|
||||||
apps?.map((app) => (
|
apps?.map((app) => (
|
||||||
<AppRow key={app.name} application={app} url={URLS.APP(app.key)} />
|
<AppRow key={app.name} application={app} url={URLS.APP(app.key)} />
|
||||||
))}
|
))}
|
||||||
|
Reference in New Issue
Block a user