deepsource-mcp-server
Version:
Model Context Protocol server for DeepSource
295 lines • 9.27 kB
JavaScript
/**
* @fileoverview GraphQL Query Builder for constructing type-safe queries
*
* This module provides a fluent API for building GraphQL queries with:
* - Type safety for fields and arguments
* - Automatic fragment generation
* - Query optimization
* - Reusable field selections
*/
/**
* GraphQL query builder for constructing type-safe queries
*/
export class GraphQLQueryBuilder {
operation = 'query';
operationName;
variables = new Map();
selections = [];
fragments = new Map();
/**
* Creates a new query builder
* @param operation - The operation type (default: 'query')
*/
constructor(operation = 'query') {
this.operation = operation;
}
/**
* Sets the operation name
*/
withName(name) {
this.operationName = name;
return this;
}
/**
* Adds a variable to the query
*/
withVariable(name, type, value) {
this.variables.set(name, { type, value });
return this;
}
/**
* Adds a field selection
*/
select(field) {
if (typeof field === 'string') {
this.selections.push({ name: field });
}
else {
this.selections.push(field);
}
return this;
}
/**
* Adds multiple field selections
*/
selectMany(...fields) {
fields.forEach((field) => this.select(field));
return this;
}
/**
* Adds a nested field selection
*/
selectNested(name, fields, args) {
const nestedFields = fields.map((f) => (typeof f === 'string' ? { name: f } : f));
const selection = { name, fields: nestedFields };
if (args !== undefined) {
selection.args = args;
}
this.selections.push(selection);
return this;
}
/**
* Adds a fragment
*/
withFragment(fragment) {
this.fragments.set(fragment.name, fragment);
return this;
}
/**
* Uses a fragment in the selection
*/
useFragment(fragmentName) {
this.selections.push({ name: `...${fragmentName}` });
return this;
}
/**
* Builds the query string
*/
build() {
const variableDeclarations = this.buildVariableDeclarations();
const selectionSet = this.buildSelectionSet(this.selections);
const fragmentDefinitions = this.buildFragments();
const operationDef = this.operationName
? `${this.operation} ${this.operationName}`
: this.operation;
const queryParts = [
variableDeclarations ? `${operationDef}(${variableDeclarations})` : operationDef,
`{${selectionSet}\n}`,
];
if (fragmentDefinitions) {
queryParts.push(fragmentDefinitions);
}
const query = queryParts.join(' ');
const variables = {};
this.variables.forEach((varDef, name) => {
variables[name] = varDef.value;
});
return { query, variables };
}
/**
* Builds variable declarations
*/
buildVariableDeclarations() {
if (this.variables.size === 0)
return '';
const declarations = [];
this.variables.forEach((varDef, name) => {
declarations.push(`$${name}: ${varDef.type}`);
});
return declarations.join(', ');
}
/**
* Builds a selection set
*/
buildSelectionSet(selections, indent = 1) {
const indentStr = ' '.repeat(indent);
const fields = selections.map((sel) => {
// Handle fragment spreads
if (sel.name?.startsWith('...')) {
return `${indentStr}${sel.name}`;
}
// Handle string selections that get converted to FieldSelection
if (!sel.name) {
return `${indentStr}${sel}`;
}
let field = sel.alias ? `${sel.alias}: ${sel.name}` : sel.name;
if (sel.args && Object.keys(sel.args).length > 0) {
const args = GraphQLQueryBuilder.buildArguments(sel.args);
field += `(${args})`;
}
if (sel.fields && sel.fields.length > 0) {
const nestedFields = this.buildSelectionSet(sel.fields, indent + 1);
field += ` {${nestedFields}\n${indentStr}}`;
}
return `${indentStr}${field}`;
});
return fields.length > 0 ? `\n${fields.join('\n')}` : '';
}
/**
* Builds argument string
*/
static buildArguments(args) {
return Object.entries(args)
.map(([key, value]) => {
if (typeof value === 'string' && value.startsWith('$')) {
// Variable reference
return `${key}: ${value}`;
}
return `${key}: ${JSON.stringify(value)}`;
})
.join(', ');
}
/**
* Builds fragment definitions
*/
buildFragments() {
if (this.fragments.size === 0)
return '';
const fragmentDefs = [];
this.fragments.forEach((fragment) => {
const fields = this.buildSelectionSet(fragment.fields);
fragmentDefs.push(`fragment ${fragment.name} on ${fragment.type} {${fields}\n}`);
});
return `\n${fragmentDefs.join('\n')}`;
}
/**
* Static factory methods for common queries
*/
/**
* Creates a query builder for fetching projects
*/
static projects() {
return new GraphQLQueryBuilder().withName('GetProjects').selectNested('viewer', [
{
name: 'accounts',
fields: [
{ name: 'id' },
{ name: 'name' },
{
name: 'repositories',
fields: [{ name: 'edges' }],
},
],
},
]);
}
/**
* Creates a query builder for fetching a specific run
*/
static run(projectKey, runId) {
return new GraphQLQueryBuilder()
.withName('GetRun')
.withVariable('projectKey', 'String!', projectKey)
.withVariable('runId', 'ID!', runId)
.selectNested('run', [
{ name: 'id' },
{ name: 'commitOid' },
{ name: 'createdAt' },
{ name: 'updatedAt' },
{ name: 'status' },
{
name: 'checks',
fields: [{ name: 'analyzer' }, { name: 'status' }, { name: 'summary' }],
},
], { projectKey: '$projectKey', runUid: '$runId' });
}
/**
* Creates a query builder for fetching quality metrics
*/
static qualityMetrics(projectKey) {
return new GraphQLQueryBuilder()
.withName('GetQualityMetrics')
.withVariable('projectKey', 'String!', projectKey)
.selectNested('repository', [
{
name: 'metrics',
fields: [
{ name: 'name' },
{ name: 'shortcode' },
{ name: 'description' },
{ name: 'positiveDirection' },
{ name: 'unit' },
{ name: 'minValueAllowed' },
{ name: 'maxValueAllowed' },
{ name: 'isReported' },
{ name: 'isThresholdEnforced' },
{
name: 'items',
fields: [
{ name: 'id' },
{ name: 'key' },
{ name: 'threshold' },
{ name: 'latestValue' },
{ name: 'latestValueDisplay' },
{ name: 'thresholdStatus' },
],
},
],
},
], { key: '$projectKey' });
}
/**
* Creates a reusable fragment for issue fields
*/
static issueFieldsFragment() {
return {
name: 'IssueFields',
type: 'Issue',
fields: [
{ name: 'id' },
{ name: 'title' },
{ name: 'category' },
{ name: 'issueCode' },
{ name: 'issuePriority' },
{ name: 'shortDescription' },
{ name: 'beginLine' },
{ name: 'endLine' },
{ name: 'beginColumn' },
{ name: 'endColumn' },
{ name: 'path' },
{ name: 'analyzer' },
{ name: 'tags' },
],
};
}
/**
* Creates a reusable fragment for run fields
*/
static runFieldsFragment() {
return {
name: 'RunFields',
type: 'Run',
fields: [
{ name: 'runUid' },
{ name: 'commitOid' },
{ name: 'createdAt' },
{ name: 'updatedAt' },
{ name: 'status' },
{ name: 'summary' },
{ name: 'triggeredVia' },
{ name: 'branch' },
],
};
}
}
//# sourceMappingURL=query-builder.js.map