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 queryResolvers from './query-resolvers.js';
|
||||
|
||||
const resolvers = {
|
||||
Query: queryResolvers,
|
||||
Mutation: mutationResolvers,
|
||||
};
|
||||
|
||||
|
@@ -1,7 +1,6 @@
|
||||
type Query {
|
||||
getConnectedApps(name: String): [App]
|
||||
placeholderQuery(name: String): Boolean
|
||||
}
|
||||
|
||||
type Mutation {
|
||||
createAppConfig(input: CreateAppConfigInput): AppConfig
|
||||
createAppAuthClient(input: CreateAppAuthClientInput): AppAuthClient
|
||||
|
@@ -40,9 +40,6 @@ export const authenticateUser = async (request, response, next) => {
|
||||
const isAuthenticatedRule = rule()(isAuthenticated);
|
||||
|
||||
export const authenticationRules = {
|
||||
Query: {
|
||||
'*': isAuthenticatedRule,
|
||||
},
|
||||
Mutation: {
|
||||
'*': isAuthenticatedRule,
|
||||
forgotPassword: allow,
|
||||
|
@@ -42,19 +42,21 @@ describe('authentication rules', () => {
|
||||
|
||||
const { queries, mutations } = getQueryAndMutationNames(authenticationRules);
|
||||
|
||||
describe('for queries', () => {
|
||||
queries.forEach((query) => {
|
||||
it(`should apply correct rule for query: ${query}`, () => {
|
||||
const ruleApplied = authenticationRules.Query[query];
|
||||
if (queries.length) {
|
||||
describe('for queries', () => {
|
||||
queries.forEach((query) => {
|
||||
it(`should apply correct rule for query: ${query}`, () => {
|
||||
const ruleApplied = authenticationRules.Query[query];
|
||||
|
||||
if (query === '*') {
|
||||
expect(ruleApplied.func).toBe(isAuthenticated);
|
||||
} else {
|
||||
expect(ruleApplied).toEqual(allow);
|
||||
}
|
||||
if (query === '*') {
|
||||
expect(ruleApplied.func).toBe(isAuthenticated);
|
||||
} else {
|
||||
expect(ruleApplied).toEqual(allow);
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
describe('for mutations', () => {
|
||||
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 { Link, Routes, Route, useNavigate } from 'react-router-dom';
|
||||
import { useQuery } from '@apollo/client';
|
||||
import Box from '@mui/material/Box';
|
||||
import Grid from '@mui/material/Grid';
|
||||
import Divider from '@mui/material/Divider';
|
||||
@@ -15,23 +14,25 @@ import PageTitle from 'components/PageTitle';
|
||||
import AppRow from 'components/AppRow';
|
||||
import SearchInput from 'components/SearchInput';
|
||||
import useFormatMessage from 'hooks/useFormatMessage';
|
||||
import { GET_CONNECTED_APPS } from 'graphql/queries/get-connected-apps';
|
||||
import * as URLS from 'config/urls';
|
||||
import useUserApps from 'hooks/useUserApps';
|
||||
|
||||
export default function Applications() {
|
||||
const navigate = useNavigate();
|
||||
const formatMessage = useFormatMessage();
|
||||
const [appName, setAppName] = React.useState(null);
|
||||
const { data, loading } = useQuery(GET_CONNECTED_APPS, {
|
||||
variables: { name: appName },
|
||||
});
|
||||
const apps = data?.getConnectedApps;
|
||||
const { data, isLoading } = useUserApps(appName);
|
||||
const apps = data?.data;
|
||||
const hasApps = apps?.length;
|
||||
|
||||
const onSearchChange = React.useCallback((event) => {
|
||||
setAppName(event.target.value);
|
||||
}, []);
|
||||
|
||||
const goToApps = React.useCallback(() => {
|
||||
navigate(URLS.APPS);
|
||||
}, [navigate]);
|
||||
|
||||
return (
|
||||
<Box sx={{ py: 3 }}>
|
||||
<Container>
|
||||
@@ -75,21 +76,21 @@ export default function Applications() {
|
||||
|
||||
<Divider sx={{ mt: [2, 0], mb: 2 }} />
|
||||
|
||||
{loading && (
|
||||
{isLoading && (
|
||||
<CircularProgress
|
||||
data-test="apps-loader"
|
||||
sx={{ display: 'block', margin: '20px auto' }}
|
||||
/>
|
||||
)}
|
||||
|
||||
{!loading && !hasApps && (
|
||||
{!isLoading && !hasApps && (
|
||||
<NoResultFound
|
||||
text={formatMessage('apps.noConnections')}
|
||||
to={URLS.NEW_APP_CONNECTION}
|
||||
/>
|
||||
)}
|
||||
|
||||
{!loading &&
|
||||
{!isLoading &&
|
||||
apps?.map((app) => (
|
||||
<AppRow key={app.name} application={app} url={URLS.APP(app.key)} />
|
||||
))}
|
||||
|
Reference in New Issue
Block a user