feat: Introduce generic auth steps
This commit is contained in:
@@ -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"
|
||||||
},
|
},
|
||||||
|
@@ -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}"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
@@ -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)
|
||||||
};
|
};
|
||||||
|
@@ -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"
|
||||||
|
@@ -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 (
|
||||||
|
9
packages/web/src/graphql/mutations/create-auth-link.ts
Normal file
9
packages/web/src/graphql/mutations/create-auth-link.ts
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
import { gql } from '@apollo/client';
|
||||||
|
|
||||||
|
export const CREATE_AUTH_LINK = gql`
|
||||||
|
mutation CreateAuthLink($id: String!) {
|
||||||
|
createAuthLink(id: $id) {
|
||||||
|
url
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
@@ -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
|
||||||
|
13
packages/web/src/graphql/mutations/index.ts
Normal file
13
packages/web/src/graphql/mutations/index.ts
Normal 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;
|
@@ -19,6 +19,19 @@ export const GET_APP = gql`
|
|||||||
docUrl
|
docUrl
|
||||||
clickToCopy
|
clickToCopy
|
||||||
}
|
}
|
||||||
|
authenticationSteps {
|
||||||
|
step
|
||||||
|
type
|
||||||
|
name
|
||||||
|
fields {
|
||||||
|
name
|
||||||
|
value
|
||||||
|
fields {
|
||||||
|
name
|
||||||
|
value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
25
packages/web/src/helpers/computeAuthStepVariables.ts
Normal file
25
packages/web/src/helpers/computeAuthStepVariables.ts
Normal 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;
|
@@ -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 };
|
||||||
|
Reference in New Issue
Block a user