feat(linear): add project_id filter for sync (#938)
Add project_id filter for Linear sync When linear.project_id is configured, bd linear sync will only fetch issues belonging to that project instead of all team issues. Closes #937
This commit is contained in:
@@ -26,6 +26,7 @@ var linearCmd = &cobra.Command{
|
|||||||
Configuration:
|
Configuration:
|
||||||
bd config set linear.api_key "YOUR_API_KEY"
|
bd config set linear.api_key "YOUR_API_KEY"
|
||||||
bd config set linear.team_id "TEAM_ID"
|
bd config set linear.team_id "TEAM_ID"
|
||||||
|
bd config set linear.project_id "PROJECT_ID" # Optional: sync only this project
|
||||||
|
|
||||||
Environment variables (alternative to config):
|
Environment variables (alternative to config):
|
||||||
LINEAR_API_KEY - Linear API key
|
LINEAR_API_KEY - Linear API key
|
||||||
@@ -586,6 +587,10 @@ func getLinearClient(ctx context.Context) (*linear.Client, error) {
|
|||||||
if endpoint, _ := store.GetConfig(ctx, "linear.api_endpoint"); endpoint != "" {
|
if endpoint, _ := store.GetConfig(ctx, "linear.api_endpoint"); endpoint != "" {
|
||||||
client = client.WithEndpoint(endpoint)
|
client = client.WithEndpoint(endpoint)
|
||||||
}
|
}
|
||||||
|
// Filter to specific project if configured
|
||||||
|
if projectID, _ := store.GetConfig(ctx, "linear.project_id"); projectID != "" {
|
||||||
|
client = client.WithProjectID(projectID)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return client, nil
|
return client, nil
|
||||||
|
|||||||
@@ -92,6 +92,7 @@ func (c *Client) WithEndpoint(endpoint string) *Client {
|
|||||||
return &Client{
|
return &Client{
|
||||||
APIKey: c.APIKey,
|
APIKey: c.APIKey,
|
||||||
TeamID: c.TeamID,
|
TeamID: c.TeamID,
|
||||||
|
ProjectID: c.ProjectID,
|
||||||
Endpoint: endpoint,
|
Endpoint: endpoint,
|
||||||
HTTPClient: c.HTTPClient,
|
HTTPClient: c.HTTPClient,
|
||||||
}
|
}
|
||||||
@@ -103,11 +104,24 @@ func (c *Client) WithHTTPClient(httpClient *http.Client) *Client {
|
|||||||
return &Client{
|
return &Client{
|
||||||
APIKey: c.APIKey,
|
APIKey: c.APIKey,
|
||||||
TeamID: c.TeamID,
|
TeamID: c.TeamID,
|
||||||
|
ProjectID: c.ProjectID,
|
||||||
Endpoint: c.Endpoint,
|
Endpoint: c.Endpoint,
|
||||||
HTTPClient: httpClient,
|
HTTPClient: httpClient,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// WithProjectID returns a new client configured to filter issues by the specified project.
|
||||||
|
// When set, FetchIssues and FetchIssuesSince will only return issues belonging to this project.
|
||||||
|
func (c *Client) WithProjectID(projectID string) *Client {
|
||||||
|
return &Client{
|
||||||
|
APIKey: c.APIKey,
|
||||||
|
TeamID: c.TeamID,
|
||||||
|
ProjectID: projectID,
|
||||||
|
Endpoint: c.Endpoint,
|
||||||
|
HTTPClient: c.HTTPClient,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Execute sends a GraphQL request to the Linear API.
|
// Execute sends a GraphQL request to the Linear API.
|
||||||
// Handles rate limiting with exponential backoff.
|
// Handles rate limiting with exponential backoff.
|
||||||
func (c *Client) Execute(ctx context.Context, req *GraphQLRequest) (json.RawMessage, error) {
|
func (c *Client) Execute(ctx context.Context, req *GraphQLRequest) (json.RawMessage, error) {
|
||||||
@@ -178,6 +192,7 @@ func (c *Client) Execute(ctx context.Context, req *GraphQLRequest) (json.RawMess
|
|||||||
|
|
||||||
// FetchIssues retrieves issues from Linear with optional filtering by state.
|
// FetchIssues retrieves issues from Linear with optional filtering by state.
|
||||||
// state can be: "open" (unstarted/started), "closed" (completed/canceled), or "all".
|
// state can be: "open" (unstarted/started), "closed" (completed/canceled), or "all".
|
||||||
|
// If ProjectID is set on the client, only issues from that project are returned.
|
||||||
func (c *Client) FetchIssues(ctx context.Context, state string) ([]Issue, error) {
|
func (c *Client) FetchIssues(ctx context.Context, state string) ([]Issue, error) {
|
||||||
var allIssues []Issue
|
var allIssues []Issue
|
||||||
var cursor string
|
var cursor string
|
||||||
@@ -189,6 +204,16 @@ func (c *Client) FetchIssues(ctx context.Context, state string) ([]Issue, error)
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Add project filter if configured
|
||||||
|
if c.ProjectID != "" {
|
||||||
|
filter["project"] = map[string]interface{}{
|
||||||
|
"id": map[string]interface{}{
|
||||||
|
"eq": c.ProjectID,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
switch state {
|
switch state {
|
||||||
case "open":
|
case "open":
|
||||||
filter["state"] = map[string]interface{}{
|
filter["state"] = map[string]interface{}{
|
||||||
@@ -242,6 +267,7 @@ func (c *Client) FetchIssues(ctx context.Context, state string) ([]Issue, error)
|
|||||||
// FetchIssuesSince retrieves issues from Linear that have been updated since the given time.
|
// FetchIssuesSince retrieves issues from Linear that have been updated since the given time.
|
||||||
// This enables incremental sync by only fetching issues modified after the last sync.
|
// This enables incremental sync by only fetching issues modified after the last sync.
|
||||||
// The state parameter can be: "open", "closed", or "all".
|
// The state parameter can be: "open", "closed", or "all".
|
||||||
|
// If ProjectID is set on the client, only issues from that project are returned.
|
||||||
func (c *Client) FetchIssuesSince(ctx context.Context, state string, since time.Time) ([]Issue, error) {
|
func (c *Client) FetchIssuesSince(ctx context.Context, state string, since time.Time) ([]Issue, error) {
|
||||||
var allIssues []Issue
|
var allIssues []Issue
|
||||||
var cursor string
|
var cursor string
|
||||||
@@ -260,6 +286,15 @@ func (c *Client) FetchIssuesSince(ctx context.Context, state string, since time.
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Add project filter if configured
|
||||||
|
if c.ProjectID != "" {
|
||||||
|
filter["project"] = map[string]interface{}{
|
||||||
|
"id": map[string]interface{}{
|
||||||
|
"eq": c.ProjectID,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Add state filter if specified
|
// Add state filter if specified
|
||||||
switch state {
|
switch state {
|
||||||
case "open":
|
case "open":
|
||||||
|
|||||||
@@ -32,6 +32,7 @@ const (
|
|||||||
type Client struct {
|
type Client struct {
|
||||||
APIKey string
|
APIKey string
|
||||||
TeamID string
|
TeamID string
|
||||||
|
ProjectID string // Optional: filter issues to a specific project
|
||||||
Endpoint string // GraphQL endpoint URL (defaults to DefaultAPIEndpoint)
|
Endpoint string // GraphQL endpoint URL (defaults to DefaultAPIEndpoint)
|
||||||
HTTPClient *http.Client
|
HTTPClient *http.Client
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user