feat: Introduce generic auth steps

This commit is contained in:
Ali BARIN
2021-10-14 18:34:30 +02:00
parent 1fbc58e7e1
commit 9c529b6c6d
12 changed files with 399 additions and 313 deletions

View File

@@ -2,6 +2,7 @@
"name": "@automatisch/root", "name": "@automatisch/root",
"private": true, "private": true,
"scripts": { "scripts": {
"start": "lerna run --stream --scope @*/{web,backend} dev",
"start:web": "lerna run --stream --scope @*/web start", "start:web": "lerna run --stream --scope @*/web start",
"start:backend": "lerna run --stream --scope @*/backend dev" "start:backend": "lerna run --stream --scope @*/backend dev"
}, },

View File

@@ -55,7 +55,7 @@
}, },
{ {
"name": "data", "name": "data",
"value": "data", "value": null,
"fields": [ "fields": [
{ {
"name": "consumerKey", "name": "consumerKey",
@@ -76,7 +76,7 @@
"fields": [ "fields": [
{ {
"name": "id", "name": "id",
"value": "{response.credentialId}" "value": "{createCredential.id}"
} }
] ]
} }

View File

@@ -1,15 +1,15 @@
import { GraphQLNonNull, GraphQLInt } from 'graphql'; import { GraphQLNonNull, GraphQLString } from 'graphql';
import Credential from '../../models/credential'; import Credential from '../../models/credential';
import authLinkType from '../types/auth-link'; import authLinkType from '../types/auth-link';
import RequestWithCurrentUser from '../../types/express/request-with-current-user'; import RequestWithCurrentUser from '../../types/express/request-with-current-user';
type Params = { type Params = {
credentialId: number, id: number,
} }
const createAuthLinkResolver = async (params: Params, req: RequestWithCurrentUser) => { const createAuthLinkResolver = async (params: Params, req: RequestWithCurrentUser) => {
const credential = await Credential.query().findOne({ const credential = await Credential.query().findOne({
user_id: req.currentUser.id, user_id: req.currentUser.id,
id: params.credentialId id: params.id
}) })
const appClass = (await import(`../../apps/${credential.key}`)).default; const appClass = (await import(`../../apps/${credential.key}`)).default;
@@ -23,7 +23,7 @@ const createAuthLinkResolver = async (params: Params, req: RequestWithCurrentUse
const createAuthLink = { const createAuthLink = {
type: authLinkType, type: authLinkType,
args: { args: {
credentialId: { type: GraphQLNonNull(GraphQLInt) }, id: { type: GraphQLNonNull(GraphQLString) },
}, },
resolve: (_: any, params: Params, req: RequestWithCurrentUser) => createAuthLinkResolver(params, req) resolve: (_: any, params: Params, req: RequestWithCurrentUser) => createAuthLinkResolver(params, req)
}; };

View File

@@ -12,12 +12,14 @@
"@testing-library/react": "^11.1.0", "@testing-library/react": "^11.1.0",
"@testing-library/user-event": "^12.1.10", "@testing-library/user-event": "^12.1.10",
"@types/jest": "^26.0.15", "@types/jest": "^26.0.15",
"@types/lodash.template": "^4.5.0",
"@types/node": "^12.0.0", "@types/node": "^12.0.0",
"@types/react": "^17.0.0", "@types/react": "^17.0.0",
"@types/react-dom": "^17.0.0", "@types/react-dom": "^17.0.0",
"@types/react-router-dom": "^5.3.0", "@types/react-router-dom": "^5.3.0",
"clipboard-copy": "^4.0.1", "clipboard-copy": "^4.0.1",
"graphql": "^15.6.0", "graphql": "^15.6.0",
"lodash.template": "^4.5.0",
"react": "^17.0.2", "react": "^17.0.2",
"react-dom": "^17.0.2", "react-dom": "^17.0.2",
"react-hook-form": "^7.17.2", "react-hook-form": "^7.17.2",
@@ -28,7 +30,7 @@
"web-vitals": "^1.0.1" "web-vitals": "^1.0.1"
}, },
"scripts": { "scripts": {
"start": "react-scripts start", "dev": "react-scripts start",
"build": "react-scripts build", "build": "react-scripts build",
"test": "react-scripts test", "test": "react-scripts test",
"eject": "react-scripts eject" "eject": "react-scripts eject"

View File

@@ -1,4 +1,4 @@
import { useMutation } from '@apollo/client'; import { useApolloClient, useMutation } from '@apollo/client';
import DialogTitle from '@mui/material/DialogTitle'; import DialogTitle from '@mui/material/DialogTitle';
import DialogContent from '@mui/material/DialogContent'; import DialogContent from '@mui/material/DialogContent';
import DialogContentText from '@mui/material/DialogContentText'; import DialogContentText from '@mui/material/DialogContentText';
@@ -6,8 +6,9 @@ import Dialog from '@mui/material/Dialog';
import Button from '@mui/material/Button'; import Button from '@mui/material/Button';
import { FieldValues, SubmitHandler } from 'react-hook-form'; import { FieldValues, SubmitHandler } from 'react-hook-form';
import computeAuthStepVariables from 'helpers/computeAuthStepVariables';
import InputCreator from 'components/InputCreator'; import InputCreator from 'components/InputCreator';
import { CREATE_CREDENTIALS } from 'graphql/mutations/create-credentials'; import MUTATIONS from 'graphql/mutations';
import type { App } from 'types/app'; import type { App } from 'types/app';
import { Form } from './style'; import { Form } from './style';
@@ -16,26 +17,33 @@ type AddAppConnectionProps = {
application: App; application: App;
}; };
type Response = {
[key: string]: any;
}
export default function AddAppConnection(props: AddAppConnectionProps){ export default function AddAppConnection(props: AddAppConnectionProps){
const { application, onClose } = props; const { application, onClose } = props;
const { name, fields } = application; const { key, fields, authenticationSteps } = application;
const [createCredentials, { data: newCredentials }] = useMutation(CREATE_CREDENTIALS); const apollo = useApolloClient();
console.log('newCredentials', newCredentials)
const submitHandler: SubmitHandler<FieldValues> = (data) => { const submitHandler: SubmitHandler<FieldValues> = async (data) => {
const variables = { const response: Response = {
key: application.key, key,
displayName: data.displayName, fields: data,
data: {
consumerKey: data.consumerKey,
consumerSecret: data.consumerSecret
}
}; };
createCredentials({ variables }); for await (const authenticationStep of authenticationSteps) {
const mutation = MUTATIONS[authenticationStep.name as string];
const variables = computeAuthStepVariables(authenticationStep, response);
onClose?.(); const mutationResponse: any = await apollo.mutate({
mutation,
variables,
});
response[authenticationStep.name] = mutationResponse.data[authenticationStep.name];
}
}; };
return ( return (

View File

@@ -0,0 +1,9 @@
import { gql } from '@apollo/client';
export const CREATE_AUTH_LINK = gql`
mutation CreateAuthLink($id: String!) {
createAuthLink(id: $id) {
url
}
}
`;

View File

@@ -1,10 +1,10 @@
import { gql } from '@apollo/client'; import { gql } from '@apollo/client';
export const CREATE_CREDENTIALS = gql` export const CREATE_CREDENTIAL = gql`
mutation CreateCredentials($displayName: String!, $key: String!, $data: twitterCredentialInput!) { mutation CreateCredential($key: String!, $data: twitterCredentialInput!) {
createCredential(displayName: $displayName, key: $key, data: $data) { createCredential(key: $key, data: $data) {
key key
displayName id
data { data {
consumerKey consumerKey
consumerSecret consumerSecret

View File

@@ -0,0 +1,13 @@
import { CREATE_CREDENTIAL } from './create-credentials';
import { CREATE_AUTH_LINK } from './create-auth-link';
type Mutations = {
[key: string]: any,
}
const mutations: Mutations = {
createCredential: CREATE_CREDENTIAL,
createAuthLink: CREATE_AUTH_LINK,
};
export default mutations;

View File

@@ -19,6 +19,19 @@ export const GET_APP = gql`
docUrl docUrl
clickToCopy clickToCopy
} }
authenticationSteps {
step
type
name
fields {
name
value
fields {
name
value
}
}
}
} }
} }
`; `;

View File

@@ -0,0 +1,25 @@
import template from 'lodash.template';
const interpolate = /{([\s\S]+?)}/g;
type VARIABLES = {
[key: string]: any
}
const computeAuthStepVariables = (authStep: any, aggregatedData: any) => {
const variables: VARIABLES = {};
for (const field of authStep.fields) {
if (field.fields) {
variables[field.name] = computeAuthStepVariables(field, aggregatedData);
continue;
}
variables[field.name] = template(field.value, { interpolate })(aggregatedData);
}
return variables;
};
export default computeAuthStepVariables;

View File

@@ -17,6 +17,7 @@ type App = {
docUrl: string; docUrl: string;
primaryColor: string; primaryColor: string;
fields: AppFields[]; fields: AppFields[];
authenticationSteps: any[];
}; };
export type { App, AppFields }; export type { App, AppFields };

588
yarn.lock

File diff suppressed because it is too large Load Diff