Skip to content

fix(datastore): keep swift graphql decoding with non optional modelName #4955

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
May 31, 2024
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
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ class GraphQLResponseDecodeTests: XCTestCase {
"a": ["b"]
]
let expectedJson = "{\"a\":[\"b\"]}"
let result: Result<String, APIError> = GraphQLResponse<String>.decodeDataPayload(json, modelName: nil)
let result: Result<String, APIError> = GraphQLResponse<String>.decodeDataPayload(json, modelName: "Post")
XCTAssertNoThrow(try result.get())
XCTAssertEqual(expectedJson, try result.get())
}
Expand All @@ -137,7 +137,7 @@ class GraphQLResponseDecodeTests: XCTestCase {
"author": "authorId",
], modelName: "Post"))

let result: Result<AnyModel, APIError> = GraphQLResponse<AnyModel>.decodeDataPayload(json, modelName: nil)
let result: Result<AnyModel, APIError> = GraphQLResponse<AnyModel>.decodeDataPayload(json, modelName: "Post")
XCTAssertNoThrow(try result.get())
XCTAssertEqual(expectedModel.modelName, try result.get().modelName)
XCTAssertEqual(expectedModel.id, try result.get().id)
Expand Down Expand Up @@ -227,7 +227,11 @@ class GraphQLResponseDecodeTests: XCTestCase {
"author": "authorId",
], modelName: "Post"))

let result: Result<GraphQLResponse<MutationSync<AnyModel>>, APIError> = .fromAppSyncResponse(json: json, decodePath: "onCreatePost", modelName: nil)
let result: Result<GraphQLResponse<MutationSync<AnyModel>>, APIError> = .fromAppSyncResponse(
json: json,
decodePath: "onCreatePost",
modelName: "Post"
)
XCTAssertNoThrow(try result.get())
let mutationSync = try! result.get()
XCTAssertNoThrow(try mutationSync.get())
Expand All @@ -244,7 +248,11 @@ class GraphQLResponseDecodeTests: XCTestCase {
]
]

let result: Result<GraphQLResponse<MutationSync<AnyModel>>, APIError> = .fromAppSyncResponse(json: json, decodePath: "onCreatePost", modelName: nil)
let result: Result<GraphQLResponse<MutationSync<AnyModel>>, APIError> = .fromAppSyncResponse(
json: json,
decodePath: "onCreatePost",
modelName: "Post"
)
XCTAssertNoThrow(try result.get())
let mutationSync = try! result.get()
XCTAssertThrowsError(try mutationSync.get()) { error in
Expand Down Expand Up @@ -283,7 +291,11 @@ class GraphQLResponseDecodeTests: XCTestCase {
"author": "authorId",
], modelName: "Post"))

let result: Result<GraphQLResponse<MutationSync<AnyModel>>, APIError> = .fromAppSyncResponse(json: json, decodePath: "onCreatePost", modelName: nil)
let result: Result<GraphQLResponse<MutationSync<AnyModel>>, APIError> = .fromAppSyncResponse(
json: json,
decodePath: "onCreatePost",
modelName: "Post"
)
XCTAssertNoThrow(try result.get())
let mutationSync = try! result.get()
XCTAssertThrowsError(try mutationSync.get()) { error in
Expand All @@ -305,7 +317,11 @@ class GraphQLResponseDecodeTests: XCTestCase {
"a": "b"
]

let result: Result<GraphQLResponse<MutationSync<AnyModel>>, APIError> = .fromAppSyncResponse(json: json, decodePath: "onCreatePost", modelName: nil)
let result: Result<GraphQLResponse<MutationSync<AnyModel>>, APIError> = .fromAppSyncResponse(
json: json,
decodePath: "onCreatePost",
modelName: "Post"
)
XCTAssertThrowsError(try result.get()) { error in
if case .unknown(let description, _, _) = (error as! APIError) {
XCTAssertEqual("Failed to get data object or errors from GraphQL response", description)
Expand All @@ -325,7 +341,7 @@ class GraphQLResponseDecodeTests: XCTestCase {
]

let jsonString = String(data: try! JSONEncoder().encode(json), encoding: .utf8)!
let response: GraphQLResponse<AnyModel> = .fromAppSyncResponse(string: jsonString, decodePath: nil)
let response: GraphQLResponse<AnyModel> = .fromAppSyncResponse(string: jsonString, decodePath: nil, modelName: "Post")
XCTAssertNoThrow(try response.get())
XCTAssertEqual(json.id?.stringValue, try response.get().identifier)
XCTAssertEqual(json.__typename?.stringValue, try response.get().modelName)
Expand All @@ -334,7 +350,7 @@ class GraphQLResponseDecodeTests: XCTestCase {
func testFromAppSyncResponse_withBrokenJsonString_failWithTransformationError() {
SchemaData.modelSchemaRegistry.registerModels(registry: ModelRegistry.self)
let jsonString = "{"
let response: GraphQLResponse<AnyModel> = .fromAppSyncResponse(string: jsonString, decodePath: nil)
let response: GraphQLResponse<AnyModel> = .fromAppSyncResponse(string: jsonString, decodePath: nil, modelName: "Post")
XCTAssertThrowsError(try response.get()) { error in
guard case .transformationError = error as! GraphQLResponseError<AnyModel> else {
XCTFail("Should failed with transformationError")
Expand All @@ -358,7 +374,11 @@ class GraphQLResponseDecodeTests: XCTestCase {
]

let jsonString = String(data: try! JSONEncoder().encode(payloadJson), encoding: .utf8)!
let response: GraphQLResponse<MutationSync<AnyModel>> = .fromAppSyncResponse(string: jsonString, decodePath: "onCreatePost")
let response: GraphQLResponse<MutationSync<AnyModel>> = .fromAppSyncResponse(
string: jsonString,
decodePath: "onCreatePost",
modelName: "Post"
)
XCTAssertNoThrow(try response.get())
let mutationSync = try! response.get()
XCTAssertEqual(payloadJson.onCreatePost?.id?.stringValue, mutationSync.model.identifier)
Expand All @@ -368,7 +388,11 @@ class GraphQLResponseDecodeTests: XCTestCase {
func testFromAppSyncSubscriptionResponse_withWrongJsonString_failWithTransformationError() {
SchemaData.modelSchemaRegistry.registerModels(registry: ModelRegistry.self)
let jsonString = "{"
let response: GraphQLResponse<MutationSync<AnyModel>> = .fromAppSyncSubscriptionResponse(string: jsonString, decodePath: nil)
let response: GraphQLResponse<MutationSync<AnyModel>> = .fromAppSyncSubscriptionResponse(
string: jsonString,
decodePath: nil,
modelName: "Post"
)
XCTAssertThrowsError(try response.get()) { error in
guard case .transformationError = error as! GraphQLResponseError<MutationSync<AnyModel>> else {
XCTFail("Should failed with transformationError")
Expand Down
14 changes: 9 additions & 5 deletions packages/amplify_datastore/ios/Classes/FlutterApiPlugin.swift
Original file line number Diff line number Diff line change
Expand Up @@ -100,12 +100,14 @@ public class FlutterApiPlugin: APICategoryPlugin
throw DataStoreError.decodingError("Request payload could not be empty", "")
}

let datastoreOptions = request.options?.pluginOptions as? AWSAPIPluginDataStoreOptions

guard let datastoreOptions = request.options?.pluginOptions as? AWSAPIPluginDataStoreOptions else {
throw DataStoreError.decodingError("Failed to decode the GraphQLRequest due to a missing options field.", "")
}

return GraphQLResponse<R>.fromAppSyncResponse(
string: payload,
decodePath: request.decodePath,
modelName: datastoreOptions?.modelName
modelName: datastoreOptions.modelName
)
}

Expand All @@ -117,12 +119,14 @@ public class FlutterApiPlugin: APICategoryPlugin
throw DataStoreError.decodingError("Request payload could not be empty", "")
}

let datastoreOptions = request.options?.pluginOptions as? AWSAPIPluginDataStoreOptions
guard let datastoreOptions = request.options?.pluginOptions as? AWSAPIPluginDataStoreOptions else {
throw DataStoreError.decodingError("Failed to decode the GraphQLRequest due to a missing options field.", "")
}

return GraphQLResponse<R>.fromAppSyncSubscriptionResponse(
string: payload,
decodePath: request.decodePath,
modelName: datastoreOptions?.modelName
modelName: datastoreOptions.modelName
)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ extension GraphQLResponse {
public static func fromAppSyncResponse<R: Decodable>(
string: String,
decodePath: String?,
modelName: String? = nil
modelName: String
) -> GraphQLResponse<R> {
guard let data = string.data(using: .utf8) else {
return .failure(.transformationError(
Expand All @@ -44,7 +44,7 @@ extension GraphQLResponse {
public static func fromAppSyncSubscriptionResponse<R: Decodable>(
string: String,
decodePath: String?,
modelName: String? = nil
modelName: String
) -> GraphQLResponse<R> {
guard let data = string.data(using: .utf8) else {
return .failure(.transformationError(
Expand Down Expand Up @@ -98,7 +98,7 @@ extension GraphQLResponse {
static func fromAppSyncResponse<R: Decodable>(
json: JSONValue,
decodePath: String?,
modelName: String?
modelName: String
) -> Result<GraphQLResponse<R>, APIError> {
let data = decodePath != nil ? json.value(at: decodePath!) : json
let errors = json.errors?.asArray
Expand Down Expand Up @@ -147,18 +147,17 @@ extension GraphQLResponse {

static func decodeDataPayload<R: Decodable>(
_ dataPayload: JSONValue,
modelName: String?
modelName: String
) -> Result<R, APIError> {
if R.self == String.self {
return encodeDataPayloadToString(dataPayload).map { $0 as! R }
}

/// This is included to allow multi-platform support. Requests that do not have `__typename`
let dataPayloadWithTypeName = modelName.flatMap {
dataPayload.asObject?.merging(
["__typename": .string($0)]
) { a, _ in a }
}.map { JSONValue.object($0) } ?? dataPayload
let dataPayloadWithTypeName = (dataPayload.asObject?.merging(
["__typename": .string(modelName)],
uniquingKeysWith: { a, _ in a }
)).map { JSONValue.object($0) } ?? dataPayload

if R.self == AnyModel.self {
return decodeDataPayloadToAnyModel(dataPayloadWithTypeName).map { $0 as! R }
Expand Down
Loading