Skip to content

Commit

Permalink
created just one function to save setup repos in db, handled cases wh…
Browse files Browse the repository at this point in the history
…ere to remove repo_config from db as well, made the code efficient
  • Loading branch information
MuskanPaliwal committed Apr 4, 2024
1 parent efaaa0c commit 7add7ca
Show file tree
Hide file tree
Showing 3 changed files with 78 additions and 196 deletions.
37 changes: 12 additions & 25 deletions pages/api/dpu/setup.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { NextApiRequest, NextApiResponse } from 'next';
import { SetupReposArgs, removePreviousInstallations, saveSetupReposInDb } from '../../../utils/db/setupRepos';
import { getUserIdByTopicName } from '../../../utils/db/users';
import { insertRepoConfig } from '../../../utils/db/repos';
import { insertRepoConfig, removeRepoconfigForInstallId } from '../../../utils/db/repos';

const setupHandler = async (req: NextApiRequest, res: NextApiResponse) => {
console.info("[setupHandler]Saving setup info in db...");
Expand All @@ -11,13 +11,6 @@ const setupHandler = async (req: NextApiRequest, res: NextApiResponse) => {
res.status(400).json({ "error": "Invalid request body" });
return;
}
try {
await removePreviousInstallations(jsonBody.installationId)
} catch (err) {
console.error(`[setupHandler] Unable to remove previous installations for ${jsonBody.installationId}`, err);
res.status(500).json({ "error": "Internal Server Error" });
return;
}
// get user_id for the given install_id
const userId = await getUserIdByTopicName(jsonBody.installationId).catch((error: any) => {
console.error("[setupHandler/getUserIdByTopicName] Failed to fetch userId from the database.", error);
Expand All @@ -35,29 +28,23 @@ const setupHandler = async (req: NextApiRequest, res: NextApiResponse) => {
repo_names: ownerInfo.repos,
install_id: jsonBody.installationId
}
const saveSetupReposPromises = saveSetupReposInDb(setupReposArgs)
try {
await removePreviousInstallations(jsonBody.installationId, ownerInfo.provider);
await removeRepoconfigForInstallId(jsonBody.installationId, ownerInfo.provider, ownerInfo.repos, userId);
} catch (err) {
console.error(`[setupHandler] Unable to remove previous installations for ${jsonBody.installationId}`, err);
res.status(500).json({ "error": "Internal Server Error" });
return;
}

const saveSetupReposPromises = saveSetupReposInDb(setupReposArgs, userId)
.catch((err) => {
console.error("[setupHandler] Unable to save setup info, ", err);
});
allSetupReposPromises.push(saveSetupReposPromises);
}
await Promise.all(allSetupReposPromises).then(async (values) => {
await Promise.all(allSetupReposPromises).then(() => {
console.info("[setupHandler] All setup info saved succesfully...")
// for repos in ownerInfo, save default repo_config value in repo_config table
for (const ownerInfo of jsonBody.info) {
await insertRepoConfig(ownerInfo.owner, ownerInfo.repos, userId, ownerInfo.provider)
.then((queryResponse: any) => {
if (queryResponse) {
console.info(`[setupHandler/insertRepoConfigOnSetup] repo config info saved succesfully for repos: ${ownerInfo.repos} for owner: ${ownerInfo.owner}`);
}
else {
console.error(`[setupHandler/insertRepoConfigOnSetup] Failed to save repo config info for repos: ${ownerInfo.repos} for owner: ${ownerInfo.owner}`);
}
})
.catch((error: Error) => {
console.error(`[setupHandler/insertRepoConfigOnSetup] Failed to save repo config for repos: ${ownerInfo.repos} for owner: ${ownerInfo.owner}`, error);
})
}
res.status(200).send("Ok");
return;
}).catch((error) => {
Expand Down
37 changes: 29 additions & 8 deletions utils/db/repos.ts
Original file line number Diff line number Diff line change
Expand Up @@ -170,17 +170,15 @@ export const getRepoConfigByUserAndRepo = async (provider: string, repoName: str
}
return userRows[0].config;
}
export const insertRepoConfig = async (repoOwner: string, repoNames: string[], userId: string, provider: string) => {
// Construct the VALUES clause dynamically
const valuesClause = repoNames.map(repoName => `('${repoName}', '${repoOwner}', '${provider}')`).join(',');

export const insertRepoConfig = async (userId: string, repoIds: number[]) => {
const query = `
INSERT INTO repo_config (repo_id, user_id, auto_assign, comment_setting)
SELECT id, $1, true, true
FROM public.repos
WHERE (repo_name, repo_owner, repo_provider) IN (${valuesClause})
FROM unnest($2::INT[]) AS t(id)
ON CONFLICT DO NOTHING
`;
const params = [userId];
const params = [userId, repoIds];

const queryIsSuccessful = await conn.query(query, params)
.then((dbResponse) => {
Expand All @@ -190,9 +188,32 @@ export const insertRepoConfig = async (repoOwner: string, repoNames: string[], u
return true;
})
.catch((err: Error) => {
console.error(`[db/insertRepoConfigOnSetup] Could not insert repo config for the repos: ${repoNames}`, { pg_query: query }, err);
console.error(`[db/insertRepoConfigOnSetup] Could not insert repo config for the repos: ${repoIds}`, { pg_query: query }, err);
return false;
})
return queryIsSuccessful;
}
}

export const removeRepoconfigForInstallId = async (install_id: string, repo_names: string[], provider: string, user_id: string) => {
const deleteRepoConfigQuery = `
DELETE FROM repo_config
WHERE repo_id IN (
SELECT id
FROM repos
WHERE install_ids && ARRAY[$1]
AND repo_name NOT IN (SELECT unnest($2::TEXT[]))
AND repo_provider = $3
)
AND user_id = $4;
`;

const result = await conn.query(deleteRepoConfigQuery, [install_id, repo_names, provider, user_id])
.catch((err) => {
console.error(`[removeRepoConfig] Could not remove repo config for: ${user_id}, ${repo_names}`, { pg_query: deleteRepoConfigQuery }, err);
throw new Error("Error in running the query on the database", err);
});
if (result.rowCount === 0) {
throw new Error('No repo config found to remove');
}
console.debug(`[removeRepoConfig] Previous repoConfig removed for ${install_id} and ${user_id}`);
}
200 changes: 37 additions & 163 deletions utils/db/setupRepos.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import conn from '.';
import { insertRepoConfig } from './repos';

export interface SetupReposArgs {
repo_owner: string;
Expand All @@ -7,149 +8,46 @@ export interface SetupReposArgs {
install_id: string;
}

interface SetupResponse {
message: string;
}

export const saveSetupReposInDb = async (args: SetupReposArgs): Promise<SetupResponse> => {
export const saveSetupReposInDb = async (args: SetupReposArgs, userId: string): Promise<boolean> => {
const { repo_owner, repo_provider, repo_names, install_id } = args;
const query = `SELECT EXISTS(SELECT 1 FROM repos WHERE repo_owner = $1 AND repo_provider = $2)`

return conn.query(query, [repo_owner, repo_provider])
.then(async (orgExistsResult) => {
const orgExists = orgExistsResult.rows[0].exists;

if (!orgExists) {
await addOrganization(repo_owner, repo_provider, repo_names, install_id).catch(error => {
console.error(`[saveSetupReposInDb] Could not add organisation in repos table for: ${repo_provider}/${repo_owner} for install_id ${install_id}`, error );
throw new Error('Failed to add organization', error);
});
} else {
await handleRepoSetupAndRemoveExtra(repo_names, repo_owner, repo_provider, install_id).catch(error => {
console.error(`[saveSetupreposInDb] error in handleReposetupAndRemoveExtra function: ${repo_provider}/${repo_owner} for install_id ${install_id}`, error);
throw new Error('Failed to handle repo setup and remove extra repositories', error);
});
const insertReposQuery = `
INSERT INTO repos (repo_name, install_ids, repo_owner, repo_provider)
SELECT repo_name, ARRAY[$4]::INT[], $1, $2
FROM unnest($3::TEXT[]) AS t(repo_name)
ON CONFLICT (repo_name) DO UPDATE
SET install_ids = CASE
WHEN NOT (repos.install_ids && ARRAY[$4]::INT[]) -- user's install_id not present
THEN repos.install_ids || $4 -- append user's install_id
ELSE repos.install_ids -- no update needed
END,
repo_owner = $1,
repo_provider = $2
RETURNING id AS repo_id;
`;
try {
await conn.query('BEGIN');
const { rowCount: reposRowCount, rows } = await conn.query(insertReposQuery, [repo_owner, repo_provider, repo_names, install_id]);
if (reposRowCount === 0) {
await conn.query('ROLLBACK');
console.error(`[saveSetupRepos] No repositories were inserted or updated for: ${install_id}, ${repo_names}`);
return false;
}
return { message: 'setup repos are saved in db successfully' };
})
.catch(error => {
console.error(`[saveSetupReposInDb] Could not save setup repos in db for: ${repo_provider}/${repo_owner} for install_id ${install_id}`, { pg_query: query }, error );
throw new Error('Failed to save set up repos in db', error);
});
};


const addOrganization = async (repo_owner: string, repo_provider: string, repo_names: string[], install_id: string) => {
await conn.query('BEGIN').catch(err => {
console.error(`[addOrganisation] Could not start db transaction: ${repo_provider}/${repo_owner} for install_id ${install_id}`, { pg_query: query }, err);
throw new Error('Failed to begin transaction');
})
const query = `INSERT INTO repos (repo_name, repo_owner, repo_provider, install_id) VALUES ($1, $2, $3, $4)`
await Promise.all(repo_names.map(repo_name => conn.query(query, [repo_name, repo_owner, repo_provider, [install_id]])
.catch(error => {
console.error(`[addOrganisation] Could not add repos data in repos table for: ${repo_provider}/${repo_owner} for install_id ${install_id}`, { pg_query: query }, error);
throw new Error('Failed to insert repos data');
})));
await conn.query('COMMIT').catch(err => {
console.error(`[addOrganisation] Could not commit db transaction: ${repo_provider}/${repo_owner} `, { pg_query: query }, err);
throw new Error('Failed to commit transaction');
})
};

// Function to handle setup for a single repository and remove extra repositories
const handleRepoSetupAndRemoveExtra = async (repo_names: string[], repo_owner: string, repo_provider: string, install_id: string) => {
await Promise.all(repo_names.map(repo_name => handleRepoSetup(repo_name, repo_owner, repo_provider, install_id)
.catch(error => {
console.error(`[handleReposetupAndRemoveExtra] Error in handleRepoSetup: ${repo_provider}/${repo_owner} for install_id ${install_id}`, error);
throw new Error('Failed to handle repo setup');
})));
await removeExtraRepositories(repo_names, repo_owner, repo_provider, install_id).catch(error => {
console.error(`[handleReposetupAndRemoveExtra] Error in removeExtraRepositories: ${repo_provider}/${repo_owner} for install_id ${install_id}`, error);
throw new Error('Failed to remove extra repositories');
});
};

// Function to handle setup for a single repository
const handleRepoSetup = async (repo_name: string, repo_owner: string, repo_provider: string, install_id: string) => {
await conn.query('BEGIN').catch(err => {
console.error(`[handleRepoSetup] Could not start db transaction: ${repo_provider}/${repo_owner} for install_id ${install_id}`, { pg_query: query }, err);
throw new Error('Failed to begin transaction');
});

const query = `SELECT install_id FROM repos WHERE repo_name = $1 AND repo_owner = $2 AND repo_provider = $3`;
const existingInstallationsResult = await conn.query(query, [repo_name, repo_owner, repo_provider]).catch(err => {
console.error(`[handleRepoSetup] Could not get the install-id for repository: ${repo_provider}/${repo_owner}/${repo_name} for install_id ${install_id}`, { pg_query: query }, err);
throw new Error("Error in getting install-id from db");
});

const existingInstallations = existingInstallationsResult.rows.map(row => row.install_id);

// Iterate over each row
if (existingInstallations && existingInstallations.length > 0) {
for (const installations of existingInstallations) {
// Check if the install_id is already present in the array
if (!installations.includes(install_id)) {
installations.push(install_id);
const updateQuery = 'UPDATE repos SET install_id = $1 WHERE repo_name = $2 AND repo_owner = $3 AND repo_provider = $4';
await conn.query(updateQuery, [installations, repo_name, repo_owner, repo_provider])
.catch(err => {
console.error(`[handleRepoSetup] Could not update the install-id array for repository: ${repo_provider}/${repo_owner}/${repo_name} for install_id ${install_id}`, { pg_query: query }, err);
throw new Error('Failed to update install-id array for repo');
});
}

const repoIds = rows.map((row) => row.repo_id);
const insertRepoConfigSuccess = await insertRepoConfig(userId, repoIds);

if (!insertRepoConfigSuccess) {
await conn.query('ROLLBACK');
return false;
}
} else {
//If no installation exist that means add that repo into repos table with the upcoming install_id
const query = `INSERT INTO repos (repo_name, repo_owner, repo_provider, install_id) VALUES ($1, $2, $3, $4)`
await conn.query(query, [repo_name, repo_owner, repo_provider, [install_id]]).catch(err => {
console.error(`[handleRepoSetup] could not insert repo into repos table: ${repo_provider}/${repo_owner}/${repo_name} for install_id ${install_id}`, { pg_query: query }, err);
throw new Error("Error in inserting repo in db");
})
await conn.query('COMMIT');
console.debug(`[saveSetupRepos] setup repos info saved successfully in db`)
return true;
} catch (err) {
await conn.query('ROLLBACK');
console.error(`[saveSetupRepos] Could not save setup repos for: ${install_id}, ${repo_names}`, { pg_query: insertReposQuery }, err);
return false;
}

await conn.query('COMMIT').catch(err => {
console.error(`[handleRepoSetup] Could not commit DB transaction: ${repo_provider}/${repo_owner} for install_id ${install_id}`, { pg_query: query }, err);
throw new Error('Failed to commit db transaction');
});
};

const removeExtraRepositories = async (incomingRepoNames: string[], repo_owner: string, repo_provider: string, install_id: string) => {
await conn.query('BEGIN').catch(err => {
console.error(`[removeExtraRepositories] Could not start db transaction: ${repo_provider}/${repo_owner} for install_id ${install_id}`, { pg_query: query }, err);
throw new Error('Failed to begin db transaction');
});

const query = `SELECT repo_name FROM repos WHERE repo_owner = $1 AND repo_provider = $2 AND $3 = ANY(install_id)`;

const existingReposResult = await conn.query(query, [repo_owner, repo_provider, install_id]).catch(err => {
console.error(`[removeExtraRepositories] Could not get existing repositories from db for: ${repo_provider}/${repo_owner} for install_id: ${install_id}`, { pg_query: query }, err);
throw new Error('Failed to get existing repos from db');
});

const existingRepos = existingReposResult.rows.map(row => row.repo_name);
const extraRepos = existingRepos.filter(repo => !incomingRepoNames.includes(repo));

for (const repo_name of extraRepos) {
// Check if the current extra repo is the only one associated with the install_id
const singleRepoQuery = `SELECT install_id FROM repos WHERE repo_name = $1 AND repo_owner = $2 AND repo_provider = $3`;
const singleRepoResult = await conn.query(singleRepoQuery, [repo_name, repo_owner, repo_provider]).catch(err => {
console.error(`[removeExtraRepositories] Error checking if repo is the only one: ${repo_provider}/${repo_owner}/${repo_name} for install_id ${install_id}`, { pg_query: singleRepoQuery }, err);
throw new Error('Failed to check if repo is the only one associated with install_id');
});

const install_ids = singleRepoResult.rows[0]?.install_id || [];
const updatedInstallIds = install_ids.filter((id: String) => id !== install_id);
await conn.query('UPDATE repos SET install_id = $1 WHERE repo_name = $2 AND repo_owner = $3 AND repo_provider = $4', [updatedInstallIds, repo_name, repo_owner, repo_provider])
.catch(err => {
console.error(`[removeExtraRepositories] Could not remove install_id from extra repo: ${repo_provider}/${repo_owner}/${repo_name} for install_id ${install_id}`, { pg_query: query }, err);
throw new Error('Failed to remove install_id from extra repo');
});
}

await conn.query('COMMIT').catch(err => {
console.error(`[removeExtraRepositories] Could not commit DB transaction: ${repo_provider}/${repo_owner} for install_id ${install_id}`, { pg_query: query }, err);
throw new Error('Failed to commit db transaction');
});
};

export const removePreviousInstallations = async (install_id: string, provider: string) => {
Expand All @@ -163,27 +61,3 @@ export const removePreviousInstallations = async (install_id: string, provider:
});
console.debug(`[removePreviousInstallations] Previous installations removed for ${install_id}`);
}

export const removeRepoConfig = async (install_id: string, repo_names: string[], provider: string, user_id: string) => {
const deleteRepoConfigQuery = `
DELETE FROM repo_config
WHERE repo_id IN (
SELECT id
FROM repos
WHERE install_ids && ARRAY[$1]
AND repo_name NOT IN (SELECT unnest($2::TEXT[]))
AND repo_provider = $3
)
AND user_id = $4;
`;

const result = await conn.query(deleteRepoConfigQuery, [install_id, repo_names, provider, user_id])
.catch((err) => {
console.error(`[removeRepoConfig] Could not remove repo config for: ${user_id}, ${repo_names}`, { pg_query: deleteRepoConfigQuery }, err);
throw new Error("Error in running the query on the database", err);
});
if (result.rowCount === 0) {
throw new Error('No repo config found to remove');
}
console.debug(`[removeRepoConfig] Previous repoConfig removed for ${install_id} and ${user_id}`);
}

0 comments on commit 7add7ca

Please sign in to comment.