diff --git a/lib/linear/mutations.rb b/lib/linear/mutations.rb index 5f34e602b..1935ba478 100644 --- a/lib/linear/mutations.rb +++ b/lib/linear/mutations.rb @@ -2,13 +2,10 @@ module Linear::Mutations def self.graphql_value(value) case value when String - # Strings must be enclosed in double quotes - "\"#{value.gsub("\n", '\\n')}\"" + value.to_json when Array - # Arrays need to be recursively converted "[#{value.map { |v| graphql_value(v) }.join(', ')}]" else - # Other types (numbers, booleans) can be directly converted to strings value.to_s end end @@ -47,7 +44,7 @@ module Linear::Mutations <<~GRAPHQL mutation { - attachmentLinkURL(url: "#{link}", issueId: "#{issue_id}", title: "#{title}"#{user_params_str}) { + attachmentLinkURL(url: #{graphql_value(link)}, issueId: #{graphql_value(issue_id)}, title: #{graphql_value(title)}#{user_params_str}) { success attachment { id diff --git a/lib/linear/queries.rb b/lib/linear/queries.rb index 54daaf30c..cc705625e 100644 --- a/lib/linear/queries.rb +++ b/lib/linear/queries.rb @@ -48,7 +48,7 @@ module Linear::Queries def self.search_issue(term) <<~GRAPHQL query { - searchIssues(term: "#{term}") { + searchIssues(term: #{Linear::Mutations.graphql_value(term)}) { nodes { id title diff --git a/spec/lib/linear_spec.rb b/spec/lib/linear_spec.rb index e15f64382..65c7cb664 100644 --- a/spec/lib/linear_spec.rb +++ b/spec/lib/linear_spec.rb @@ -93,6 +93,30 @@ describe Linear do end let(:user) { instance_double(User, name: 'John Doe', avatar_url: 'https://example.com/avatar.jpg') } + context 'when description contains double quotes' do + it 'produces valid GraphQL by escaping the quotes' do + allow(linear_client).to receive(:post) do |payload| + expect(payload[:query]).to include('description: "the sender is \\"Bot\\"') + instance_double(HTTParty::Response, success?: true, + parsed_response: { 'data' => { 'issueCreate' => { 'id' => 'issue1', 'title' => 'Title' } } }) + end + + linear_client.create_issue(params.merge(description: 'the sender is "Bot"')) + end + end + + context 'when description contains backslashes' do + it 'produces valid GraphQL by escaping the backslashes' do + allow(linear_client).to receive(:post) do |payload| + expect(payload[:query]).to include('description: "path\\\\to\\\\file"') + instance_double(HTTParty::Response, success?: true, + parsed_response: { 'data' => { 'issueCreate' => { 'id' => 'issue1', 'title' => 'Title' } } }) + end + + linear_client.create_issue(params.merge(description: 'path\\to\\file')) + end + end + context 'when the API response is success' do before do stub_request(:post, url) @@ -213,6 +237,18 @@ describe Linear do let(:title) { 'Title' } let(:user) { instance_double(User, name: 'John Doe', avatar_url: 'https://example.com/avatar.jpg') } + context 'when title contains double quotes' do + it 'produces valid GraphQL by escaping the quotes' do + allow(linear_client).to receive(:post) do |payload| + expect(payload[:query]).to include('title: "say \\"hello\\"') + instance_double(HTTParty::Response, success?: true, + parsed_response: { 'data' => { 'attachmentLinkURL' => { 'id' => 'attachment1' } } }) + end + + linear_client.link_issue(link, issue_id, 'say "hello"') + end + end + context 'when the API response is success' do before do stub_request(:post, url) @@ -332,6 +368,18 @@ describe Linear do context 'when querying issues' do let(:term) { 'term' } + context 'when search term contains double quotes' do + it 'produces valid GraphQL by escaping the quotes' do + allow(linear_client).to receive(:post) do |payload| + expect(payload[:query]).to include('term: "find \\"Bot\\"') + instance_double(HTTParty::Response, success?: true, + parsed_response: { 'data' => { 'searchIssues' => { 'nodes' => [] } } }) + end + + linear_client.search_issue('find "Bot"') + end + end + context 'when the API response is success' do before do stub_request(:post, url)