diff --git a/naptime-graphql/src/main/scala/org/coursera/naptime/ari/graphql/schema/FieldBuilder.scala b/naptime-graphql/src/main/scala/org/coursera/naptime/ari/graphql/schema/FieldBuilder.scala index ec3d5c35..bea3426d 100644 --- a/naptime-graphql/src/main/scala/org/coursera/naptime/ari/graphql/schema/FieldBuilder.scala +++ b/naptime-graphql/src/main/scala/org/coursera/naptime/ari/graphql/schema/FieldBuilder.scala @@ -138,8 +138,7 @@ object FieldBuilder extends StrictLogging { Field.apply[SangriaGraphQlContext, DataMapWithParent, Any, Any]( name = FieldBuilder.formatName(fieldName), fieldType = NaptimeTypes.DataMapType, - resolve = - context => context.value.copy(element = context.value.element.getDataMap(fieldName))) + resolve = context => context.value.element.getDataMap(fieldName)) // Complex types case (None, recordDataSchema: RecordDataSchema) => @@ -278,24 +277,23 @@ object FieldBuilder extends StrictLogging { Field.apply[SangriaGraphQlContext, DataMapWithParent, Any, Any]( name = FieldBuilder.formatName(fieldName), fieldType = sangriaScalarType, - resolve = context => { + resolve = context => Option(context.value.element.get(fieldName)) - .map { - rawValue => - val result = - ValidateDataAgainstSchema.validate(rawValue, dataSchema, validationOptions) - - if (result.isValid) { - result.getFixed match { - case value: ParseType => value - case _ => throw ResponseFormatException(s"$fieldName's value is an invalid type") - } - } else { - throw ResponseFormatException(s"$fieldName could not be fixed-up or parsed") + .map { rawValue => + val result = + ValidateDataAgainstSchema.validate(rawValue, dataSchema, validationOptions) + + if (result.isValid) { + result.getFixed match { + case value: ParseType => value + case _ => throw ResponseFormatException(s"$fieldName's value is an invalid type") } + } else { + throw ResponseFormatException(s"$fieldName could not be fixed-up or parsed") + } } .getOrElse(null) - }) + ) } /** diff --git a/naptime-graphql/src/test/scala/org/coursera/naptime/ari/graphql/schema/NaptimeAnyDataFieldTest.scala b/naptime-graphql/src/test/scala/org/coursera/naptime/ari/graphql/schema/NaptimeAnyDataFieldTest.scala new file mode 100644 index 00000000..ab9b958e --- /dev/null +++ b/naptime-graphql/src/test/scala/org/coursera/naptime/ari/graphql/schema/NaptimeAnyDataFieldTest.scala @@ -0,0 +1,157 @@ +package org.coursera.naptime.ari.graphql.schema + +import com.linkedin.data.DataMap +import com.linkedin.data.schema.RecordDataSchema +import com.linkedin.data.schema.RecordDataSchema.{Field => RecordDataSchemaField} +import com.linkedin.data.template.DataTemplateUtil +import org.coursera.naptime.ResourceName +import org.coursera.naptime.ResponsePagination +import org.coursera.naptime.ari.FetcherApi +import org.coursera.naptime.ari.Response +import org.coursera.naptime.ari.graphql.Models +import org.coursera.naptime.ari.graphql.SangriaGraphQlContext +import org.coursera.naptime.ari.graphql.SangriaGraphQlSchemaBuilder +import org.coursera.naptime.ari.graphql.helpers.ArgumentBuilder +import org.coursera.naptime.ari.graphql.marshaller.NaptimeMarshaller._ +import org.coursera.naptime.ari.graphql.models.MergedCourse +import org.coursera.naptime.ari.graphql.models.MergedInstructor +import org.coursera.naptime.ari.graphql.models.MergedPartner +import org.coursera.naptime.ari.graphql.resolvers.NaptimeResolver +import org.coursera.naptime.ari.graphql.types.NaptimeTypes +import org.junit.Test +import org.scalatest.junit.AssertionsForJUnit +import org.scalatest.mockito.MockitoSugar +import org.mockito.Matchers.any +import org.mockito.Mockito.when +import play.api.libs.json.JsObject +import sangria.ast.Document +import sangria.execution.DeprecationTracker +import sangria.execution.ExecutionPath +import sangria.execution.Executor +import sangria.marshalling.ResultMarshaller +import sangria.parser.QueryParser +import sangria.schema.Context +import sangria.schema.Field +import sangria.schema.ObjectType +import sangria.schema.Schema +import sangria.schema.Value + +import scala.collection.JavaConverters._ +import scala.concurrent.Await +import scala.concurrent.ExecutionContext +import scala.concurrent.ExecutionContext.Implicits.global +import scala.concurrent.Future +import scala.concurrent.duration.Duration + +class NaptimeAnyDataFieldTest extends AssertionsForJUnit with MockitoSugar { + + private[this] val fieldName = "anyDataField" + + private[this] def createContext[Val](value: Val)( + implicit ctxManifest: Manifest[SangriaGraphQlContext], + valManifest: Manifest[Val]): Context[SangriaGraphQlContext, Val] = { + Context[SangriaGraphQlContext, Val]( + value = value, + ctx = SangriaGraphQlContext(null, null, ExecutionContext.global, debugMode = true), + args = + ArgumentBuilder.buildArgs(NaptimePaginationField.paginationArguments, Map("limit" -> 100)), + schema = mock[Schema[SangriaGraphQlContext, Val]], + field = mock[Field[SangriaGraphQlContext, Val]], + parentType = mock[ObjectType[SangriaGraphQlContext, Any]], + marshaller = mock[ResultMarshaller], + query = Document.emptyStub, + sourceMapper = None, + deprecationTracker = DeprecationTracker.empty, + astFields = Vector.empty, + path = ExecutionPath.empty, + deferredResolverState = None) + } + + @Test + def basic(): Unit = { + val passthroughExemptJson = + """ + |{ + | "name": "PassthroughExempt", + | "type": "record", + | "passthroughExempt": true, + | "fields": [ + | ] + |} + """.stripMargin + val recordDataSchema = + DataTemplateUtil.parseSchema(passthroughExemptJson).asInstanceOf[RecordDataSchema] + val field = FieldBuilder.buildField( + mock[SchemaMetadata], + new RecordDataSchemaField(recordDataSchema), + namespace = None, + fieldNameOverride = Some(fieldName), + resourceName = ResourceName("courses", 1)) + assert(field.fieldType === NaptimeTypes.DataMapType) + + val dataMap = new DataMap(Map("foo" -> 1).asJava) + val context = createContext( + DataMapWithParent(new DataMap(Map(fieldName -> dataMap).asJava), null)) + val result = + field.resolve(context).asInstanceOf[Value[SangriaGraphQlContext, DataMap]] + assert(result.value === dataMap) + } + + @Test + def parseDataMapTypes(): Unit = { + val schemaTypes = Map( + "org.coursera.naptime.ari.graphql.models.MergedCourse" -> MergedCourse.SCHEMA, + "org.coursera.naptime.ari.graphql.models.MergedPartner" -> MergedPartner.SCHEMA, + "org.coursera.naptime.ari.graphql.models.MergedInstructor" -> MergedInstructor.SCHEMA) + val allResources = + Set(Models.courseResource, Models.instructorResource, Models.partnersResource) + val builder = new SangriaGraphQlSchemaBuilder(allResources, schemaTypes) + val schema = builder.generateSchema().data.asInstanceOf[Schema[SangriaGraphQlContext, Any]] + + val query = + """ + |query { + | CoursesV1Resource { + | get(id: "courseAId") { + | id + | arbitraryData + | } + | } + |} + """.stripMargin + val queryAst = QueryParser.parse(query).get + + val fetcher = mock[FetcherApi] + val data = List( + new DataMap( + Map( + "arbitraryData" -> + new DataMap(Map("moduleOne" -> "abc", "moduleTwo" -> "defg").asJava), + "id" -> "courseAId").asJava)) + when(fetcher.data(any(), any())(any())) + .thenReturn( + Future.successful(Right(Response(data, ResponsePagination(None, None, None), url = None)))) + + val context = SangriaGraphQlContext(fetcher, null, ExecutionContext.global, debugMode = true) + val responseData = Await + .result( + Executor + .execute( + schema, + queryAst, + context, + variables = JsObject(Map.empty[String, JsObject]), + deferredResolver = new NaptimeResolver()), + Duration.Inf) + .asInstanceOf[JsObject] + + assert( + (responseData \ "data" \ "CoursesV1Resource" \ "get" \ "arbitraryData").get + .as[Map[String, String]] === + Map("moduleOne" -> "abc", "moduleTwo" -> "defg")) + assert( + (responseData \ "data" \ "CoursesV1Resource" \ "get" \ "id").get + .as[String] === "courseAId") + } + +} diff --git a/naptime-graphql/src/test/scala/org/coursera/naptime/ari/graphql/schema/NaptimePaginatedResourceFieldTest.scala b/naptime-graphql/src/test/scala/org/coursera/naptime/ari/graphql/schema/NaptimePaginatedResourceFieldTest.scala index 4f7fb2ae..f9b1a888 100644 --- a/naptime-graphql/src/test/scala/org/coursera/naptime/ari/graphql/schema/NaptimePaginatedResourceFieldTest.scala +++ b/naptime-graphql/src/test/scala/org/coursera/naptime/ari/graphql/schema/NaptimePaginatedResourceFieldTest.scala @@ -17,7 +17,6 @@ package org.coursera.naptime.ari.graphql.schema import org.coursera.naptime.ResourceName -import org.coursera.naptime.ari.Response import org.coursera.naptime.ari.graphql.Models import org.coursera.naptime.ari.graphql.SangriaGraphQlContext import org.coursera.naptime.ari.graphql.helpers.ArgumentBuilder diff --git a/version.sbt b/version.sbt index 0394a72b..1d114b7d 100644 --- a/version.sbt +++ b/version.sbt @@ -1 +1 @@ -version in ThisBuild := "0.9.2-alpha17" +version in ThisBuild := "0.9.2-alpha18"