Add fixtures for remaining entities in database initialization script (#36)

* Create fixture data for remaining entities and load it in db

* Rename currProject to currentProject in frontend package

* Handle review changes

* Update readme for loading fixtures

---------

Co-authored-by: neeraj <neeraj.rtly@gmail.com>
This commit is contained in:
Nabarun Gogoi 2024-01-24 16:58:07 +05:30 committed by Ashwin Phatak
parent 890603061f
commit 2d7e56c0e1
11 changed files with 436 additions and 171 deletions

View File

@ -15,7 +15,7 @@
- Build packages
```bash
yarn build
yarn build --ignore frontend
```
- Change directory to `packages/backend`
@ -24,6 +24,12 @@
cd packages/backend
```
- Load fixtures in database
```bash
yarn db:load:fixtures
```
- Start the server
```bash

View File

@ -29,7 +29,7 @@
"lint": "eslint .",
"format": "prettier --write .",
"format:check": "prettier --check .",
"db:load:fixtures": "ts-node ./test/initialize-db.ts"
"db:load:fixtures": "DEBUG=snowball:* ts-node ./test/initialize-db.ts"
},
"devDependencies": {
"@types/fs-extra": "^11.0.4",

View File

@ -0,0 +1,92 @@
[
{
"projectIndex": 0,
"domainIndex":0,
"title": "nextjs-boilerplate-1",
"status": "Building",
"environment": "Production",
"isCurrent": false,
"branch": "prod",
"commitHash": "testXyz"
},
{
"projectIndex": 0,
"domainIndex":1,
"title": "nextjs-boilerplate-2",
"status": "Ready",
"environment": "Preview",
"isCurrent": true,
"branch": "prod",
"commitHash": "testXyz"
},
{
"projectIndex": 0,
"domainIndex":2,
"title": "nextjs-boilerplate-3",
"status": "Error",
"environment": "Development",
"isCurrent": false,
"branch": "prod",
"commitHash": "testXyz"
},
{
"projectIndex": 1,
"domainIndex":3,
"title": "nextjs-boilerplate-1",
"status": "Building",
"environment": "Production",
"isCurrent": false,
"branch": "prod",
"commitHash": "testXyz"
},
{
"projectIndex": 1,
"domainIndex":4,
"title": "nextjs-boilerplate-2",
"status": "Ready",
"environment": "Preview",
"isCurrent": true,
"branch": "prod",
"commitHash": "testXyz"
},
{
"projectIndex": 1,
"domainIndex":5,
"title": "nextjs-boilerplate-3",
"status": "Error",
"environment": "Development",
"isCurrent": false,
"branch": "prod",
"commitHash": "testXyz"
},
{
"projectIndex": 2,
"domainIndex":6,
"title": "nextjs-boilerplate-1",
"status": "Building",
"environment": "Production",
"isCurrent": false,
"branch": "prod",
"commitHash": "testXyz"
},
{
"projectIndex": 2,
"domainIndex":7,
"title": "nextjs-boilerplate-2",
"status": "Ready",
"environment": "Preview",
"isCurrent": true,
"branch": "prod",
"commitHash": "testXyz"
},
{
"projectIndex": 2,
"domainIndex":8,
"title": "nextjs-boilerplate-3",
"status": "Error",
"environment": "Development",
"isCurrent": false,
"branch": "prod",
"commitHash": "testXyz"
}
]

View File

@ -0,0 +1,56 @@
[
{
"name": "randomurl.snowballtools.xyz",
"status": "Live",
"isRedirected": false,
"branch": "test"
},
{
"name": "saugatt.com",
"status": "Pending",
"isRedirected": false,
"branch": "test"
},
{
"name": "www.saugatt.com",
"status": "Pending",
"isRedirected": true,
"branch": "test"
},
{
"name": "randomurl.snowballtools.xyz",
"status": "Live",
"isRedirected": false,
"branch": "test"
},
{
"name": "saugatt.com",
"status": "Pending",
"isRedirected": false,
"branch": "test"
},
{
"name": "www.saugatt.com",
"status": "Pending",
"isRedirected": true,
"branch": "test"
},
{
"name": "randomurl.snowballtools.xyz",
"status": "Live",
"isRedirected": false,
"branch": "test"
},
{
"name": "saugatt.com",
"status": "Pending",
"isRedirected": false,
"branch": "test"
},
{
"name": "www.saugatt.com",
"status": "Pending",
"isRedirected": true,
"branch": "test"
}
]

View File

@ -0,0 +1,62 @@
[
{
"projectIndex": 0,
"key": "ABC",
"value": "ABC",
"environments": ["Production", "Preview"]
},
{
"projectIndex": 0,
"key": "XYZ",
"value": "abc3",
"environments": ["Preview"]
},
{
"projectIndex": 1,
"key": "ABC",
"value": "ABC",
"environments": ["Production", "Preview"]
},
{
"projectIndex": 1,
"key": "XYZ",
"value": "abc3",
"environments": ["Preview"]
},
{
"projectIndex": 2,
"key": "ABC",
"value": "ABC",
"environments": ["Production", "Preview"]
},
{
"projectIndex": 2,
"key": "XYZ",
"value": "abc3",
"environments": ["Preview"]
},
{
"projectIndex": 3,
"key": "ABC",
"value": "ABC",
"environments": ["Production", "Preview"]
},
{
"projectIndex": 3,
"key": "XYZ",
"value": "abc3",
"environments": ["Preview"]
},
{
"projectIndex": 4,
"key": "ABC",
"value": "ABC",
"environments": ["Production", "Preview"]
},
{
"projectIndex": 4,
"key": "XYZ",
"value": "abc3",
"environments": ["Preview"]
}
]

View File

@ -0,0 +1,12 @@
[
{
"memberIndex": 1,
"projectIndex": 0,
"permissions": ["View", "Edit"]
},
{
"memberIndex": 2,
"projectIndex": 0,
"permissions": ["View", "Edit"]
}
]

View File

@ -14,20 +14,37 @@ import { Deployment } from '../src/entity/Deployment';
const log = debug('snowball:initialize-database');
const DB_PATH = '../db/snowball';
const USER_DATA_PATH = './fixtures/users.json';
const PROJECT_DATA_PATH = './fixtures/projects.json';
const ORGANIZATION_DATA_PATH = './fixtures/organizations.json';
const USER_ORGANIZATION_DATA_PATH = './fixtures/user-orgnizations.json';
const PROJECT_MEMBER_DATA_PATH = './fixtures/project-members.json';
const DOMAIN_DATA_PATH = './fixtures/domains.json';
const DEPLOYMENT_DATA_PATH = './fixtures/deployments.json';
const ENVIRONMENT_VARIABLE_DATA_PATH = './fixtures/environment-variables.json';
const loadAndSaveData = async <Entity extends ObjectLiteral>(entityType: EntityTarget<Entity>, dataSource: DataSource, filePath: string) => {
const loadAndSaveData = async <Entity extends ObjectLiteral>(entityType: EntityTarget<Entity>, dataSource: DataSource, filePath: string, relations?: any | undefined) => {
const entitiesData = await fs.readFile(filePath, 'utf-8');
const entities = JSON.parse(entitiesData) as DeepPartial<Entity>[];
const entities = JSON.parse(entitiesData);
const entityRepository = dataSource.getRepository(entityType);
const savedEntity:Entity[] = [];
for (const entityData of entities) {
const entity = entityRepository.create(entityData);
let entity = entityRepository.create(entityData as DeepPartial<Entity>);
if (relations) {
for (const field in relations) {
const valueIndex = String(field + 'Index');
entity = {
...entity,
[field]: relations[field][entityData[valueIndex]]
};
}
}
const dbEntity = await entityRepository.save(entity);
savedEntity.push(dbEntity);
}
@ -38,52 +55,69 @@ const loadAndSaveData = async <Entity extends ObjectLiteral>(entityType: EntityT
const generateTestData = async (dataSource: DataSource) => {
const savedUsers = await loadAndSaveData(User, dataSource, path.resolve(__dirname, USER_DATA_PATH));
const savedOrgs = await loadAndSaveData(Organization, dataSource, path.resolve(__dirname, ORGANIZATION_DATA_PATH));
const savedDomains = await loadAndSaveData(Domain, dataSource, path.resolve(__dirname, DOMAIN_DATA_PATH));
const projectsData = await fs.readFile(path.resolve(__dirname, PROJECT_DATA_PATH), 'utf-8');
const projects = JSON.parse(projectsData);
const projectRepository = dataSource.getRepository(Project);
const projectRelations = {
owner: savedUsers,
organization: savedOrgs
};
for (const projectData of projects) {
const project = projectRepository.create(projectData as DeepPartial<Project>);
project.owner = savedUsers[projectData.ownerIndex];
project.organization = savedOrgs[projectData.organizationIndex];
await projectRepository.save(project);
}
const savedProjects = await loadAndSaveData(Project, dataSource, path.resolve(__dirname, PROJECT_DATA_PATH), projectRelations);
const userOrgData = await fs.readFile(path.resolve(__dirname, USER_ORGANIZATION_DATA_PATH), 'utf-8');
const userOrgs = JSON.parse(userOrgData);
const userOrgRepository = dataSource.getRepository(UserOrganization);
const userOrganizationRelations = {
member: savedUsers,
organization: savedOrgs
};
for (const userOrgData of userOrgs) {
const userOrg = userOrgRepository.create(userOrgData as DeepPartial<UserOrganization>);
userOrg.member = savedUsers[userOrgData.memberIndex];
userOrg.organization = savedOrgs[userOrgData.organizationIndex];
await loadAndSaveData(UserOrganization, dataSource, path.resolve(__dirname, USER_ORGANIZATION_DATA_PATH), userOrganizationRelations);
await userOrgRepository.save(userOrg);
const projectMemberRelations = {
member: savedUsers,
project: savedProjects
};
await loadAndSaveData(ProjectMember, dataSource, path.resolve(__dirname, PROJECT_MEMBER_DATA_PATH), projectMemberRelations);
const deploymentRelations = {
project: savedProjects,
domain: savedDomains
};
await loadAndSaveData(Deployment, dataSource, path.resolve(__dirname, DEPLOYMENT_DATA_PATH), deploymentRelations);
const environmentVariableRelations = {
project: savedProjects
};
await loadAndSaveData(EnvironmentVariable, dataSource, path.resolve(__dirname, ENVIRONMENT_VARIABLE_DATA_PATH), environmentVariableRelations);
};
const checkFileExists = async (filePath: string) => {
try {
await fs.access(filePath, fs.constants.F_OK);
return true;
} catch (err) {
log(err);
return false;
}
};
const main = async () => {
const isDbPresent = await checkFileExists(path.resolve(__dirname, DB_PATH));
if (!isDbPresent) {
const dataSource = new DataSource({
type: 'better-sqlite3',
database: 'db/snowball',
synchronize: true,
logging: true,
entities: [
User,
Organization,
Project,
UserOrganization,
EnvironmentVariable,
Domain,
ProjectMember,
Deployment
]
entities: [path.join(__dirname, '../src/entity/*')]
});
await dataSource.initialize();
await generateTestData(dataSource);
}
};
main().then(() => {

View File

@ -14,19 +14,19 @@ const Domains = () => {
const { projects } = useOutletContext<ProjectsOutletContext>();
const currProject = useMemo(() => {
const currentProject = useMemo(() => {
return projects.find((project) => {
return project.id === id;
});
}, [id, projects]);
const linkedRepo = useMemo(() => {
return currProject?.repositories.find(
(repo: any) => repo.id === Number(currProject?.repositoryId),
return currentProject?.repositories.find(
(repo: any) => repo.id === Number(currentProject?.repositoryId),
);
}, [currProject]);
}, [currentProject]);
const domains = currProject?.deployments
const domains = currentProject?.deployments
.filter((deployment) => {
return deployment.domain != null;
})
@ -49,7 +49,7 @@ const Domains = () => {
domain={domain}
key={domain.id}
repo={linkedRepo!}
project={currProject!}
project={currentProject!}
/>
);
})}

View File

@ -38,7 +38,7 @@ export const EnvironmentVariablesTabPanel = () => {
const { projects } = useOutletContext<ProjectsOutletContext>();
const currProject = useMemo(() => {
const currentProject = useMemo(() => {
return projects.find((project) => {
return project.id === id;
});
@ -77,9 +77,9 @@ export const EnvironmentVariablesTabPanel = () => {
}, [isSubmitSuccessful, reset]);
const getEnvironmentVariable = useCallback((environment: Environments) => {
return (currProject?.environmentVariables as EnvironmentVariable[]).filter(
(item) => item.environments.includes(environment),
);
return (
currentProject?.environmentVariables as EnvironmentVariable[]
).filter((item) => item.environments.includes(environment));
}, []);
const isFieldEmpty = useMemo(() => {

View File

@ -15,7 +15,6 @@ import DeleteProjectDialog from './DeleteProjectDialog';
import ConfirmDialog from '../../../shared/ConfirmDialog';
import { ProjectsOutletContext } from '../../../../types/project';
const PROJECT_ID = '62f87575-7a2b-4951-8156-9f9821j380d';
const TEAMS = ['Airfoil'];
const DEFAULT_SELECT_TEAM = undefined;
@ -37,7 +36,7 @@ const GeneralTabPanel = () => {
const { id } = useParams();
const { projects } = useOutletContext<ProjectsOutletContext>();
const currProject = useMemo(() => {
const currentProject = useMemo(() => {
return projects.find((project: any) => project.id === id);
}, [id]);
@ -61,12 +60,14 @@ const GeneralTabPanel = () => {
const { handleSubmit, register } = useForm({
defaultValues: {
appName: currProject?.name,
description: currProject?.description,
appName: currentProject?.name,
description: currentProject?.description,
},
});
return (
<>
{currentProject && (
<>
<form onSubmit={handleSubmit(() => {})}>
<Typography variant="h6">Project info</Typography>
@ -95,10 +96,10 @@ const GeneralTabPanel = () => {
<Input
crossOrigin={undefined}
variant="outlined"
value={PROJECT_ID}
value={currentProject.id}
size="md"
disabled
icon={<CopyIcon value={PROJECT_ID} />}
icon={<CopyIcon value={currentProject.id} />}
/>
<Button type="submit" variant="gradient" size="sm" className="mt-1">
Save
@ -107,8 +108,8 @@ const GeneralTabPanel = () => {
<div className="mb-1">
<Typography variant="h6">Transfer project</Typography>
<Typography variant="small">
Transfer this app to your personal account or a team you are a member
of.
Transfer this app to your personal account or a team you are a
member of.
<Link to="" className="text-blue-500">
Learn more
</Link>
@ -166,8 +167,8 @@ const GeneralTabPanel = () => {
<div className="mb-1">
<Typography variant="h6">Delete project</Typography>
<Typography variant="small">
The project will be permanently deleted, including its deployments and
domains. This action is irreversible and can not be undone.
The project will be permanently deleted, including its deployments
and domains. This action is irreversible and can not be undone.
</Typography>
<Button
variant="gradient"
@ -184,6 +185,8 @@ const GeneralTabPanel = () => {
/>
</div>
</>
)}
</>
);
};

View File

@ -18,12 +18,12 @@ const MembersTabPanel = () => {
// @ts-expect-error create context type for projects
const { projects } = useOutletContext();
const currProject = useMemo(() => {
const currentProject = useMemo(() => {
return projects.find((project: any) => project.id === id);
}, [id]);
const [updatedMembers, setUpdatedMembers] = useState([
...currProject?.members,
...currentProject?.members,
]);
const addMemberHandler = useCallback((member: Member) => {
@ -59,7 +59,7 @@ const MembersTabPanel = () => {
member={member.member}
key={member.id}
isFirstCard={index === FIRST_MEMBER_CARD}
isOwner={member.member.id === currProject?.owner.id}
isOwner={member.member.id === currentProject?.owner.id}
isPending={member.name === ''}
permissions={member.permissions}
handleDeletePendingMember={(id: number) => {