Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Related issues buildout #9

Merged
merged 9 commits into from
Dec 20, 2023
21 changes: 21 additions & 0 deletions .github/workflows/pr_agent.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
name: Codium-AI PR Agent
env:
PR_REVIEWER.REQUIRE_TESTS_REVIEW: "false"
on:
pull_request:
issue_comment:
jobs:
pr_agent_job:
runs-on: ubuntu-latest
permissions:
issues: write
pull-requests: write
contents: write
name: Run pr agent on every pull request, respond to user comments
steps:
- name: PR Agent action step
id: pragent
uses: Codium-ai/pr-agent@main
env:
OPENAI_KEY: ${{ secrets.OPENAI_KEY }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
21 changes: 21 additions & 0 deletions backend/src/db/migrations/20231218000000-add-parent-id-to-issue.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
'use strict';

const TABLE_NAME = 'issues';
const COLUMN_NAME = 'parent_id';

/** @type {import('sequelize-cli').Migration} */
export default {
async up(queryInterface, Sequelize) {
await queryInterface.addColumn(TABLE_NAME, COLUMN_NAME, {
type: Sequelize.INTEGER,
});

await queryInterface.addIndex(TABLE_NAME, {
fields: [COLUMN_NAME],
unique: false,
});
},
async down(queryInterface, Sequelize) {
await queryInterface.column(TABLE_NAME, COLUMN_NAME);
},
};
66 changes: 66 additions & 0 deletions backend/src/db/migrations/20231218000001-create-issue-links.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
'use strict';

const TABLE_NAME = 'issues_links';

export default {
async up(queryInterface, Sequelize) {
await queryInterface.createTable(TABLE_NAME, {
id: {
allowNull: false,
autoIncrement: true,
primaryKey: true,
type: Sequelize.INTEGER,
},
issueId: {
type: Sequelize.INTEGER,
field: 'issue_id',
references: {
model: {
tableName: 'issues',
schema: 'public',
},
key: 'id',
},
},
linkType: {
type: Sequelize.STRING,
field: 'link_type',
},
linkedIssueId: {
type: Sequelize.INTEGER,
field: 'linked_issue_id',
references: {
model: {
tableName: 'issues',
schema: 'public',
},
key: 'id',
},
},
createdAt: {
field: 'created_at',
type: Sequelize.DATE,
},
updatedAt: {
field: 'updated_at',
type: Sequelize.DATE,
},
});

await queryInterface.addIndex(TABLE_NAME, {
fields: ['issue_id'],
unique: false,
});
await queryInterface.addIndex(TABLE_NAME, {
fields: ['link_type'],
unique: false,
});
await queryInterface.addIndex(TABLE_NAME, {
fields: ['linked_issue_id'],
unique: false,
});
},
async down(queryInterface, Sequelize) {
await queryInterface.dropTable(TABLE_NAME);
},
};
2 changes: 2 additions & 0 deletions backend/src/db/models/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import IssueComment from './issue-comment.js';
import ProjectTag from './project-tag.js';
import IssueTag from './issue-tag.js';
import IssueBoard from './issue-board.js';
import IssueLinks from './issue-links.js';

export const db = {};

Expand All @@ -31,6 +32,7 @@ const init = async () => {
db.ProjectTag = ProjectTag(db.sequelize, DataTypes);
db.IssueTag = IssueTag(db.sequelize, DataTypes);
db.IssueBoard = IssueBoard(db.sequelize, DataTypes);
db.IssueLinks = IssueLinks(db.sequelize, DataTypes);

Object.values(db).forEach((model) => {
if (model.associate) {
Expand Down
78 changes: 78 additions & 0 deletions backend/src/db/models/issue-links.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
'use strict';

export default (sequelize, DataTypes) => {
const inverseLinkType = {
blocks: 'blocked_by',
blocked_by: 'blocks',
duplicates: 'duplicated_by',
duplicated_by: 'duplicates',
relates_to: 'relates_to',
clones: 'is_cloned_by',
is_cloned_by: 'clones',
};

const IssueLink = sequelize.define(
'IssueLinks',
{
id: {
allowNull: false,
autoIncrement: true,
primaryKey: true,
type: DataTypes.INTEGER,
},
issueId: {
type: DataTypes.INTEGER,
field: 'issue_id',
references: {
model: 'issues',
key: 'id',
},
},
linkType: {
type: DataTypes.STRING,
field: 'link_type',
validate: {
isIn: [Object.keys(inverseLinkType)],
},
},
linkTypeInverted: {
type: DataTypes.STRING,
get() {
return inverseLinkType?.[this.getDataValue('linkType')];
},
},
linkedIssueId: {
type: DataTypes.INTEGER,
field: 'linked_issue_id',
references: {
model: 'issues',
key: 'id',
},
},
createdAt: {
field: 'created_at',
type: DataTypes.DATE,
default: new Date(),
},
updatedAt: {
field: 'updated_at',
type: DataTypes.DATE,
},
},
{
sequelize,
tableName: 'issues_links',
createdAt: 'created_at',
updatedAt: 'updated_at',
indexes: [
{ unique: false, fields: ['link_type'] },
{ unique: false, fields: ['issue_id'] },
{ unique: false, fields: ['linked_issue_id'] },
],
}
);

IssueLink.inverseLinkType = inverseLinkType;

return IssueLink;
};
20 changes: 17 additions & 3 deletions backend/src/db/models/issue.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
'use strict';

import IssueStatuses from './issue-statuses.js';

export default (sequelize, DataTypes) => {
const Issue = sequelize.define(
'Issue',
Expand Down Expand Up @@ -69,6 +67,10 @@ export default (sequelize, DataTypes) => {
field: 'archived',
defaultValue: false,
},
parentId: {
type: DataTypes.INTEGER,
field: 'parent_id',
},
createdAt: {
field: 'created_at',
type: DataTypes.DATE,
Expand All @@ -93,9 +95,21 @@ export default (sequelize, DataTypes) => {
}
);

Issue.associate = ({ IssueComment, IssueBoard, IssueStatuses }) => {
Issue.associate = ({ IssueComment, IssueBoard, IssueLinks, Issue }) => {
Issue.hasMany(IssueComment, { foreignKey: 'issue_id', onDelete: 'CASCADE' });
Issue.hasMany(IssueBoard, { foreignKey: 'issue_id', onDelete: 'CASCADE' });
Issue.belongsToMany(Issue, {
through: IssueLinks,
foreignKey: 'issue_id',
otherKey: 'linked_issue_id',
as: 'linkedToIssues',
});
Issue.belongsToMany(Issue, {
through: IssueLinks,
foreignKey: 'linked_issue_id',
otherKey: 'issue_id',
as: 'linkedByIssues',
});
};

return Issue;
Expand Down
2 changes: 1 addition & 1 deletion backend/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ await apollo.start();

const myContextFunction = async (request) => {
// get the user token from the headers
const token = request.headers.authorization || '';
const token = request.headers.authorization;
let user = null;

// Allow if introspection query only
Expand Down
24 changes: 24 additions & 0 deletions backend/src/resolvers.js
Original file line number Diff line number Diff line change
Expand Up @@ -485,6 +485,30 @@ const resolvers = {
if (searchOperator === 'or') queryOperator = Op.or;

return db.sequelize.models.Issue.findAll({
include: [
{
model: db.sequelize.models.Issue,
as: 'linkedToIssues',
through: {
attributes: [
['issue_id', 'issueId'],
['linked_issue_id', 'linkedIssueId'],
['link_type', 'linkType'],
],
},
},
{
model: db.sequelize.models.Issue,
as: 'linkedByIssues',
through: {
attributes: [
['issue_id', 'issueId'],
['linked_issue_id', 'linkedIssueId'],
['link_type', 'linkType'],
],
},
},
],
where: {
[queryOperator]: whereOr,
},
Expand Down
14 changes: 14 additions & 0 deletions backend/src/resolvers/issue.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,18 @@
export const issueResolvers = {
links: async (parent, args, { db }) => {
return [
...parent.linkedToIssues.map((issue) => ({
...issue.toJSON(),
linkType: issue.IssueLinks.linkType,
linkedIssueId: parent.id,
})),
...parent.linkedByIssues.map((issue) => ({
...issue.toJSON(),
linkType: issue.IssueLinks.linkTypeInverted,
linkedIssueId: parent.id,
})),
];
},
tags: async (parent, args, { db }) => {
const issueTags = await db.sequelize.models.IssueTag.findAll({ where: { issueId: parent.id } });

Expand Down
6 changes: 6 additions & 0 deletions backend/src/type-defs.js
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,10 @@ const typeDefs = gql`
archived: Boolean
comments: [IssueComment]
tags: [ProjectTag]

links: [Issue]
linkType: String
linkedIssueId: String
}

type Column {
Expand Down Expand Up @@ -175,6 +179,8 @@ const typeDefs = gql`
input QueryIssueInput {
id: String
projectId: String
search: String
searchOperator: String
sortBy: [SortBy]
}

Expand Down
3 changes: 3 additions & 0 deletions frontend/gql/__generated__/graphql.ts

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.