Skip to content
This repository has been archived by the owner on Sep 16, 2024. It is now read-only.

Commit

Permalink
feat: only create apps that are needed by a developer (#553)
Browse files Browse the repository at this point in the history
* init

* feat(ui): adds loading button on right

* feat(ui): created AddIntegration Modal UI

* feat(ui) show all the created apps

* feat: created createApp endpoint and handled already created cards with ui

* fix: creation endpoint and made ui use float units

* feat: remove unused imports

* fix(ui): border red colour

* fix: remove asana from currently available apps

* fix add asana incase to avoid failure on local data

* fix: navbar and selector on left

* feat: onSelect Creation

* fix: create button on open to not show

* fix: cursor pointers

* fix(ui): naming conventions
  • Loading branch information
Nabhag8848 authored May 6, 2024
1 parent eb7d02b commit 3d33240
Show file tree
Hide file tree
Showing 12 changed files with 462 additions and 190 deletions.
17 changes: 17 additions & 0 deletions fern/definition/internal/account.yml
Original file line number Diff line number Diff line change
Expand Up @@ -50,3 +50,20 @@ service:
- errors.UnAuthorizedError
- errors.InternalServerError
- errors.NotFoundError
createRevertAppForAccount:
docs: create Revert App for Account
method: POST
path: /apps
request:
name: CreateRevertAppForAccount
body:
properties:
userId: string
tpId: types.TPID
environment: string
response: GetAccountDetailsResponse
errors:
- errors.UnAuthorizedError
- errors.InternalServerError


43 changes: 43 additions & 0 deletions packages/backend/oas/openapi.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2745,6 +2745,49 @@ paths:
- tpId
- isRevertApp
- appId
/internal/account/apps:
post:
description: create Revert App for Account
operationId: internal_account_createRevertAppForAccount
tags:
- InternalAccount
parameters: []
responses:
'200':
description: ''
content:
application/json:
schema:
$ref: '#/components/schemas/internalGetAccountDetailsResponse'
'401':
description: ''
content:
application/json:
schema:
$ref: '#/components/schemas/commonBaseError'
'500':
description: ''
content:
application/json:
schema:
$ref: '#/components/schemas/commonBaseError'
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
userId:
type: string
tpId:
$ref: '#/components/schemas/commonTPID'
environment:
type: string
required:
- userId
- tpId
- environment
/internal/analytics:
post:
description: Get Analytics of your revert account
Expand Down
23 changes: 23 additions & 0 deletions packages/backend/services/Internal/account.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { logError } from '../../helpers/logger';
import { AccountService } from '../../generated/typescript/api/resources/internal/resources/account/service/AccountService';
import { InternalServerError, NotFoundError, UnAuthorizedError } from '../../generated/typescript/api/resources/common';
import AuthService from '../auth';
import AppService from '../app';
import prisma from '../../prisma/client';

const accountService = new AccountService({
Expand Down Expand Up @@ -58,6 +59,28 @@ const accountService = new AccountService({
throw new InternalServerError({ error: 'Internal server error' });
}
},
async createRevertAppForAccount(req, res) {
try {
const { userId, tpId, environment } = req.body;
const result = await AuthService.getAccountForUser(userId);

if (result?.error) {
throw new NotFoundError({ error: 'Could not get the account for user' });
}

const isCreated = await AppService.createRevertAppForAccount({ accountId: result.account.id as string, tpId, environment });

if (isCreated?.error) {
throw new InternalServerError({ error: 'Internal Server Error' });
}

const finalResult = await AuthService.getAccountForUser(userId);
res.send({ ...finalResult });
} catch (error: any) {
logError(error);
throw new InternalServerError({ error: 'Internal server error' });
}
},
});

export { accountService };
33 changes: 33 additions & 0 deletions packages/backend/services/app.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { TP_ID } from '@prisma/client';
import prisma from '../prisma/client';

class AppService {
async createRevertAppForAccount({
accountId,
tpId,
environment,
}: {
accountId: string;
tpId: TP_ID;
environment: string;
}): Promise<any> {
const id = `${tpId}_${accountId}_${environment}`;
const environmentId = `${accountId}_${environment}`;
try {
const createdApp = await prisma.apps.create({
data: {
id,
tp_id: tpId,
scope: [],
is_revert_app: true,
environmentId,
},
});
return createdApp;
} catch (error: any) {
return { error: 'Something went wrong while creating app' };
}
}
}

export default new AppService();
26 changes: 0 additions & 26 deletions packages/backend/services/auth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -436,32 +436,6 @@ class AuthService {
},
include: { environments: true },
});
// Create default apps that don't yet exist.
await Promise.all(
Object.keys(ENV).map((env) => {
Object.keys(TP_ID).map(async (tp) => {
try {
const environment = account.environments?.find((e) => e.env === env)!;
await prisma.apps.upsert({
where: {
id: `${tp}_${account.id}_${env}`,
},
update: {},
create: {
id: `${tp}_${account.id}_${env}`,
tp_id: tp as TP_ID,
scope: [],
is_revert_app: true,
environmentId: environment.id,
},
});
// Create apps for both in development & production.
} catch (error: any) {
logError(error);
}
});
})
);
// Create Svix application for this account if it doesn't exist.
await config.svix?.application.getOrCreate({
name: accountId,
Expand Down
103 changes: 103 additions & 0 deletions packages/client/src/features/integration/AddIntegration.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
import { Box } from '@mui/material';
import Modal from '@mui/material/Modal';
import React, { useState } from 'react';
import { appsInfo } from './enums/metadata';
import { LoadingButton } from '@mui/lab';

function AddIntegration({
values,
}: {
values: {
init: boolean;
setInit: React.Dispatch<React.SetStateAction<boolean>>;
handleCreation: (id: string) => Promise<void>;
apps: any;
};
}) {
const [selected, setSelected] = useState<string>('');
const { init, setInit, handleCreation, apps } = values;
const appsId = apps.map((app) => app.tp_id);
return (
<Modal
open={init}
onClose={() => {
setInit(false);
setSelected('');
}}
aria-labelledby="modal-modal-title"
aria-describedby="modal-modal-description"
sx={{ backgroundColor: '#293347', width: '70vw', margin: '10vh 20vw 10vh 20vw', maxHeight: '74vh' }}
className="rounded-xl overflow-hidden"
>
<>
<div className="overflow-hidden">
<Box component="div" sx={{ width: '70vw', padding: '2rem 3rem 0rem 3rem', marginBottom: '1.6rem' }}>
<h1 className="text-3xl font-bold text-[#fff]">Create App</h1>
<span className="text-[#b1b8ba]">
Select and click add to configure a new app for an integration
</span>
</Box>
<div className="pt-4 h-[36rem] overflow-scroll">
<div className="grid grid-cols-4 gap-8 justify-center content-center mx-8 ">
{Object.keys(appsInfo).map((app, index) => {
const isAppExist = appsId.includes(app);
return (
<Box
key={index}
sx={{
display: 'flex',
justifyContent: 'space-between',
pointerEvents: isAppExist ? 'none' : 'initial',
cursor: isAppExist ? 'not-allowed' : 'pointer',
backgroundColor: isAppExist && '#181d28',
}}
>
<div
className="flex w-full justify-around items-center px-8 py-8 rounded-lg"
style={{
boxShadow:
app === selected
? '0 0 0 3px #fff'
: isAppExist
? '0 0 0 1px #ffa8a8'
: '0 0 0 1px #ced4da',
}}
onClick={() => setSelected(app)}
>
<img
width={100}
style={{
height: 40,
objectFit: 'scale-down',
}}
alt={`${appsInfo[app].name} logo`}
src={appsInfo[app].logo}
/>
</div>
</Box>
);
})}
</div>
</div>
<div className="flex justify-end mr-8">
<LoadingButton
variant="contained"
style={{
background: '#293347',
padding: '0.6rem 1.2rem',
}}
disabled={selected === ''}
onClick={() => {
handleCreation(selected);
}}
>
Add
</LoadingButton>
</div>
</div>
</>
</Modal>
);
}

export default AddIntegration;
85 changes: 85 additions & 0 deletions packages/client/src/features/integration/CreatedIntegration.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
import React from 'react';
import { Box, IconButton } from '@mui/material';
import SettingsIcon from '@mui/icons-material/Settings';
import { appsInfo } from './enums/metadata';

function CreatedIntegration({
values,
}: {
values: {
apps: any;
handleOpen: (appId: string) => void;
};
}) {
const { handleOpen, apps } = values;

if (!apps.length) {
return (
<div className="flex flex-col justify-center items-center h-[77vh] w-[80vw]">
<p>No apps have been created; create and configure your first app for an integration</p>
</div>
);
}

return (
<div className="grid grid-cols-4 gap-8">
{apps.map((app, index) => {
const type = appsInfo?.[app.tp_id];
return (
<Box
key={index}
sx={{
display: 'flex',
justifyContent: 'space-between',
alignItems: 'center',
padding: '2rem 0rem',
maxWidth: '22rem',
maxHeight: '12.5rem',
}}
>
<div
style={{
padding: 30,
border: '1px #3E3E3E solid',
borderRadius: 10,
display: 'flex',
flexDirection: 'column',
alignItems: 'flex-start',
height: 200,
justifyContent: 'flex-end',
position: 'relative',
}}
>
<img
width={100}
style={{
maxHeight: 40,
objectFit: 'scale-down',
objectPosition: 'left',
}}
alt={`${type?.name} logo`}
src={type?.logo}
/>
<p className="font-bold mt-4">{type?.name}</p>
<span className="text-[#b1b8ba]">{type?.description}</span>
<IconButton
onClick={() => handleOpen(app.tp_id)}
style={{
color: '#94a3b8',
fontSize: 12,
position: 'absolute',
top: 10,
right: 10,
}}
>
<SettingsIcon />
</IconButton>
</div>
</Box>
);
})}
</div>
);
}

export default CreatedIntegration;
Loading

0 comments on commit 3d33240

Please sign in to comment.