Creating a Linear issue from Chatwoot fails with a GraphQL parse error
when the title, description, or search term contains double quotes. For
example, a description like `the sender is "Bot"` produces this broken
query:
```graphql
issueCreate(input: { description: "the sender is "Bot"" })
```
Linear's API rejects this with `Syntax Error: Expected ":", found
String`. This affects issue creation, issue linking, and issue search —
any flow where user-provided text is interpolated into a GraphQL query.
The `graphql_value` helper was only escaping newlines (`\n`) but not
quotes, backslashes, or other characters that are meaningful inside a
GraphQL string literal. On top of that, `issue_link` and `search_issue`
bypassed `graphql_value` entirely, using raw string interpolation
instead.
The fix replaces the manual `gsub` escaping with Ruby's `to_json`, which
produces a properly escaped, double-quoted string that handles all
special characters. This is a minimal, well-understood substitution —
`to_json` on a Ruby string returns a valid JSON string literal, which is
also a valid GraphQL string literal since GraphQL uses the same escaping
rules. The `issue_link` mutation and `search_issue` query are updated to
route their parameters through `graphql_value` instead of raw
interpolation.
The `team_entities_query` and `linked_issues` methods in `queries.rb`
also use raw interpolation, but their inputs are system-generated IDs
and URLs rather than user-provided text, so they're left as-is to keep
this change focused.
105 lines
1.8 KiB
Ruby
105 lines
1.8 KiB
Ruby
module Linear::Queries
|
|
TEAMS_QUERY = <<~GRAPHQL.freeze
|
|
query {
|
|
teams {
|
|
nodes {
|
|
id
|
|
name
|
|
}
|
|
}
|
|
}
|
|
GRAPHQL
|
|
|
|
def self.team_entities_query(team_id)
|
|
<<~GRAPHQL
|
|
query {
|
|
users {
|
|
nodes {
|
|
id
|
|
name
|
|
}
|
|
}
|
|
projects {
|
|
nodes {
|
|
id
|
|
name
|
|
}
|
|
}
|
|
workflowStates(
|
|
filter: { team: { id: { eq: "#{team_id}" } } }
|
|
) {
|
|
nodes {
|
|
id
|
|
name
|
|
}
|
|
}
|
|
issueLabels(
|
|
filter: { team: { id: { eq: "#{team_id}" } } }
|
|
) {
|
|
nodes {
|
|
id
|
|
name
|
|
}
|
|
}
|
|
}
|
|
GRAPHQL
|
|
end
|
|
|
|
def self.search_issue(term)
|
|
<<~GRAPHQL
|
|
query {
|
|
searchIssues(term: #{Linear::Mutations.graphql_value(term)}) {
|
|
nodes {
|
|
id
|
|
title
|
|
description
|
|
identifier
|
|
state {
|
|
name
|
|
color
|
|
}
|
|
}
|
|
}
|
|
}
|
|
GRAPHQL
|
|
end
|
|
|
|
def self.linked_issues(url)
|
|
<<~GRAPHQL
|
|
query {
|
|
attachmentsForURL(url: "#{url}") {
|
|
nodes {
|
|
id
|
|
title
|
|
issue {
|
|
id
|
|
identifier
|
|
title
|
|
description
|
|
priority
|
|
createdAt
|
|
url
|
|
assignee {
|
|
name
|
|
avatarUrl
|
|
}
|
|
state {
|
|
name
|
|
color
|
|
}
|
|
labels {
|
|
nodes{
|
|
id
|
|
name
|
|
color
|
|
description
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
GRAPHQL
|
|
end
|
|
end
|