import { filter_nil } from "std/collections"
import {
graphql_assert_ok,
graphql_extract,
graphql_operation,
graphql_page_info,
} from "std/graphql"
import { merge } from "std/json"
import { wait_for } from "std/monitors"
var linear_connector_config = {}
pub fn configure(config) {
linear_connector_config = filter_nil(config ?? {})
return linear_connector_config
}
pub fn reset() {
linear_connector_config = {}
}
fn __call(method, params = {}) {
return connector_call("linear", method, filter_nil(merge(linear_connector_config, params ?? {})))
}
pub fn operation(name, document, options = nil) {
return graphql_operation(name, document, options)
}
pub fn execute(operation, variables = nil, options = nil) {
let raw = graphql(
operation.document,
variables,
merge(options ?? {}, {operation_name: operation.operation_name ?? operation.name}),
)
let envelope = graphql_assert_ok(raw)
let result = if operation.root_field != nil {
graphql_extract(envelope, operation.root_field, operation.result_schema)
} else {
envelope.data
}
let shaped_result = if type_of(result) == "dict" {
merge(result, {meta: envelope.meta})
} else {
result
}
return merge(envelope, {result: shaped_result})
}
fn __list_issues_operation() {
return operation(
"ListIssues",
"query ListIssues($filter: IssueFilter, $first: Int, $after: String, $includeArchived: Boolean) { issues(filter: $filter, first: $first, after: $after, includeArchived: $includeArchived) { nodes { id identifier title priority estimate dueDate url createdAt updatedAt state { id name type } team { id key name } assignee { id name } project { id name } cycle { id name } labels { nodes { id name } } } pageInfo { hasNextPage endCursor } } }",
{root_field: "issues"},
)
}
fn __update_issue_operation() {
return operation(
"UpdateIssue",
"mutation UpdateIssue($id: String!, $input: IssueUpdateInput!) { issueUpdate(id: $id, input: $input) { success issue { id identifier title priority estimate dueDate url updatedAt state { id name type } assignee { id name } project { id name } cycle { id name } labels { nodes { id name } } } } }",
{root_field: "issueUpdate"},
)
}
fn __create_comment_operation() {
return operation(
"CreateComment",
"mutation CreateComment($input: CommentCreateInput!) { commentCreate(input: $input) { success comment { id body url createdAt user { id name } issue { id identifier title } } } }",
{root_field: "commentCreate"},
)
}
fn __search_issues_operation() {
return operation(
"SearchIssues",
"query SearchIssues($query: String!, $first: Int) { searchIssues(query: $query, first: $first) { nodes { id identifier title url priority state { id name type } team { id key name } } pageInfo { hasNextPage endCursor } } }",
{root_field: "searchIssues"},
)
}
pub fn list_issues(filter = nil, options = nil) {
let variables = filter_nil(
{
filter: filter,
first: options?.first,
after: options?.after,
includeArchived: options?.include_archived ?? options?.includeArchived,
},
)
let connection = execute(__list_issues_operation(), variables, options).result
return merge(connection, {page: graphql_page_info(connection)})
}
pub fn update_issue(id, changes, options = nil) {
return execute(__update_issue_operation(), {id: id, input: changes}, options).result
}
pub fn create_comment(issue_id, body, options = nil) {
return execute(__create_comment_operation(), {input: {issueId: issue_id, body: body}}, options).result
}
pub fn search(query, options = nil) {
return execute(__search_issues_operation(), {query: query, first: options?.first}, options).result
}
pub fn graphql(query, variables = nil, options = nil) {
return __call("graphql", filter_nil(options ?? {} + {query: query, variables: variables}))
}
fn __linear_event(log_event) {
return log_event?.payload?.event
}
fn __linear_payload(log_event) {
return __linear_event(log_event)?.provider_payload
}
fn __linear_issue_matches(payload, issue_id) {
return issue_id == nil
|| payload?.issue?.id == issue_id
|| payload?.issue?.identifier == issue_id
|| payload?.issue?.number == issue_id
}
fn __linear_state_matches(issue, target_state) {
return target_state == nil
|| issue?.state?.id == target_state
|| issue?.state?.name == target_state
|| issue?.state?.type == target_state
}
/** issue_state_source. */
pub fn issue_state_source(issue_id, target_state = nil, options = nil) {
return {
label: "linear.issue_state:" + to_string(issue_id) + ":" + to_string(target_state ?? "*"),
prefers_push: true,
poll: { ctx ->
let payload = __linear_payload(ctx?.last_push_event)
if payload != nil && __linear_issue_matches(payload, issue_id) {
return {
matched: __linear_state_matches(payload.issue, target_state),
issue: payload.issue,
state: payload?.issue?.state,
event: payload,
}
}
let response = graphql(
"query HarnMonitorIssue($id: String!) { issue(id: $id) { id identifier title state { id name type } } }",
{id: issue_id},
options,
)
let issue = response?.data?.issue
return {
matched: __linear_state_matches(issue, target_state),
issue: issue,
state: issue?.state,
event: nil,
}
},
push_filter: { log_event ->
let event = __linear_event(log_event)
let payload = __linear_payload(log_event)
return event?.provider == "linear"
&& event?.kind == "issue"
&& __linear_issue_matches(payload, issue_id)
&& __linear_state_matches(payload?.issue, target_state)
},
}
}
/** wait_until_issue_state. */
pub fn wait_until_issue_state(issue_id, target_state, options = nil) {
return wait_for(
merge(
options ?? {},
{source: issue_state_source(issue_id, target_state, options), condition: { state -> state.matched }},
),
)
}