Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 2 additions & 4 deletions manifests/nodejs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -927,10 +927,8 @@ tests/:
nextjs: bug (APMAPI-939) # the nextjs weblog application changes the sampling priority from 1.0 to 2.0
test_graphql.py:
Test_GraphQLQueryErrorReporting:
'*': *ref_5_34_0
express4-typescript: incomplete_test_app (endpoint not implemented)
express5: missing_feature
nextjs: missing_feature
'*': irrelevant
express4: missing_feature
test_identify.py:
Test_Basic: v2.4.0
Test_Propagate: *ref_3_2_0
Expand Down
5 changes: 4 additions & 1 deletion manifests/ruby.yml
Original file line number Diff line number Diff line change
Expand Up @@ -523,7 +523,10 @@ tests/:
Test_Span_Links_Flags_From_Conflicting_Contexts: missing_feature (implementation specs have not been determined)
Test_Span_Links_From_Conflicting_Contexts: missing_feature
Test_Span_Links_Omit_Tracestate_From_Conflicting_Contexts: missing_feature (implementation specs have not been determined)
test_graphql.py: missing_feature
test_graphql.py:
Test_GraphQLQueryErrorReporting:
"*": irrelevant
graphql23: missing_feature
test_identify.py:
Test_Basic: v1.0.0
Test_Propagate: missing_feature
Expand Down
21 changes: 20 additions & 1 deletion tests/test_graphql.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,9 @@ def setup_execute_error_span_event(self):
)

def test_execute_error_span_event(self):
"""Test if the main GraphQL span contains a span event with the appropriate error information"""
"""Test if the main GraphQL span contains a span event with the appropriate error information.
The error extensions allowed are DD_TRACE_GRAPHQL_ERROR_EXTENSIONS=int,float,str,bool,other.
"""

assert self.request.status_code == 200

Expand Down Expand Up @@ -66,6 +68,23 @@ def test_execute_error_span_event(self):
assert location.split(":")[0].isdigit()
assert location.split(":")[1].isdigit()

assert attributes["extensions.int"] == 1
assert attributes["extensions.float"] == 1.1
assert attributes["extensions.str"] == "1"
assert attributes["extensions.bool"] == True

# A list with two heterogeneous elements: [1, "foo"].
# This test simulates an object that is not a supported scalar above (int,float,string,boolean).
# This object should be serialized as a string, either using the language's default serialization or
# JSON serialization of the object.
# The goal here is to display the original as well as possible in the UI, without supporting arbitrary
# nested levels inside `span_event.attributes`.
# use regex to match the list format
assert "1" in attributes["extensions.other"]
assert "foo" in attributes["extensions.other"]

assert "extensions.not_captured" not in attributes

@staticmethod
def _get_events(span):
if "events" in span["meta"]:
Expand Down
5 changes: 4 additions & 1 deletion utils/_context/_scenarios/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,10 @@ class _Scenarios:
# This GraphQL scenario can be used for any GraphQL testing, not just AppSec
graphql_appsec = EndToEndScenario(
"GRAPHQL_APPSEC",
weblog_env={"DD_APPSEC_RULES": "/appsec_blocking_rule.json"},
weblog_env={
"DD_APPSEC_RULES": "/appsec_blocking_rule.json",
"DD_TRACE_GRAPHQL_ERROR_EXTENSIONS": "int,float,str,bool,other",
},
weblog_volumes={"./tests/appsec/blocking_rule.json": {"bind": "/appsec_blocking_rule.json", "mode": "ro"}},
doc="AppSec tests for GraphQL integrations",
github_workflow="graphql",
Expand Down
43 changes: 17 additions & 26 deletions utils/build/docker/nodejs/express/graphql.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
'use strict'

const { ApolloServer, gql } = require('apollo-server-express')
const { ApolloServer, gql, ApolloError } = require('apollo-server-express')
const { readFileSync } = require('fs')

const users = [
Expand Down Expand Up @@ -32,16 +32,21 @@ const typeDefs = gql`
id: Int
name: String
}
`

type Error {
message: String
extensions: [Extension]
}

type Extension {
key: String
value: String
}`
// Custom GraphQL error class
class CustomGraphQLError extends ApolloError {
constructor (message, code, properties) {
super(message, properties)
this.extensions.code = code
this.extensions.int = 1
this.extensions.float = 1.1
this.extensions.str = '1'
this.extensions.bool = true
this.extensions.other = [1, 'foo']
this.extensions.not_captured = 'nope'
}
}

function getUser (parent, args) {
return users.find((item) => args.id === item.id)
Expand All @@ -61,7 +66,7 @@ function testInjection (parent, args) {
}

function withError (parent, args) {
throw new Error('test error')
throw new CustomGraphQLError('test error', 'CUSTOM_USER_DEFINED_ERROR', 'Some extra context about the error.')
}

const resolvers = {
Expand All @@ -73,22 +78,8 @@ const resolvers = {
}
}

// Custom error formatting
const formatError = (error) => {
return {
message: error.message,
extensions: [
{ key: 'int-1', value: '1' },
{ key: 'str-1', value: '1' },
{ key: 'array-1-2', value: [1, '2'] },
{ key: 'empty', value: 'empty string' },
{ key: 'comma', value: 'comma' }
]
}
}

module.exports = async function (app) {
const server = new ApolloServer({ typeDefs, resolvers, formatError })
const server = new ApolloServer({ typeDefs, resolvers })
await server.start()
server.applyMiddleware({ app })
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,12 @@ class SystemTestSchema < GraphQL::Schema
rescue_from(RuntimeError) do |err, obj, args, ctx, field|
# Custom extension values used for testing.
raise GraphQL::ExecutionError.new(err.message, extensions: {
'int-1': 1,
'str-1': '1',
'array-1-2': [1,'2'],
'': 'empty string',
',': 'comma',
int: 1,
float: 1.1,
str: '1',
bool: true,
other: [1, 'foo'],
not_captured: 'nope',
})
end

Expand Down