Skip to content
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

SyncError in DataStore for ErrorHandler #3232

Open
lawmicha opened this issue Sep 20, 2023 · 7 comments
Open

SyncError in DataStore for ErrorHandler #3232

lawmicha opened this issue Sep 20, 2023 · 7 comments
Labels
datastore Issues related to the DataStore category feature-parity feature-request Request a new feature

Comments

@lawmicha
Copy link
Member

lawmicha commented Sep 20, 2023

I wanted to the scope the changes in this PR to simply pass back the info back to the developer and let them log whatever is called in the callback, and the only reasonable DataStoreError case to do that with is DataStoreError.api since it has the associated types of AmplifyError and MutationEvent. We were already doing this in some other places in this code, so i'm extending it to the missed cases. The long term plan is to have a similar error type as what JS called SyncError, which on iOS will conform to AmplifyError. The JS SyncError looks like this https://github.com/aws-amplify/amplify-js/blob/main/packages/datastore/src/types.ts#L933

export type SyncError<T extends PersistentModel> = {
	message: string;
	errorType: ErrorType;
	errorInfo?: string;
	recoverySuggestion?: string;
	model?: string;
	localModel: T;
	remoteModel: T;
	process: ProcessName;
	operation: string;
	cause?: Error;
};

export type ErrorType =
	| 'ConfigError'
	| 'BadModel'
	| 'BadRecord'
	| 'Unauthorized'
	| 'Transient'
	| 'Unknown';

export enum ProcessName {
	'sync' = 'sync',
	'mutate' = 'mutate',
	'subscribe' = 'subscribe',
}

With this PR, I don't think a developer will reasonably be successful in handling the errorHandler callback (if i understand, this is what you're asking) becuase they need to check that it's a DataStoreError, with case .api, then cast the first parameter to APIError or GraphQLResponseError, and further check for .errors case, and process the GraphQLError's. I think what this PR enables the developer is the ability to get visibility on sync errors, such they can log what's coming back from the errorHandler, which helps them and us debug their situation further. Later on, we can take the error processing logic here and map that to the SyncError. So for example, in this code, the developer receiving a callback on the errorHandler can cast the AmplifyError to SyncError, the processName should be "mutate". For unauthorized, it will be the errorType "Unauthorized", and for conditionalSaveFailed, it will most likely be "BadModel" (not 100% sure on this one). The mapping exercise we will have to do and synchronize with the JS's mapping logic.

Originally posted by @lawmicha in #2532 (comment)

@github-actions
Copy link
Contributor

This issue was opened by a maintainer of this repository; updates will be posted here. If you are also experiencing this issue, please comment here with any relevant information so that we're aware and can prioritize accordingly.

@gbitaudeau
Copy link

Don't know if I well understand the goal of this issue, but when I get synchronisation error, I can't find any information which could be used to fix the error.
As an example, if AppSync responds this kind of error (this is a redacted result for sync Model named Draw) :

{
  "data": {
    "syncDraws": {
      "items": [
        {
          "id": "70083950-9b18-4e62-90c6-433ae933def3",
          "drawBatch": {
            "id": "e236042a-7c21-4b82-90b3-d8b73d2c6b99",
            "_deleted": true
          },
          "item": {
            "_deleted": true,
            "id": "fa986c78-ff21-46e2-82ff-8d0a218510bc"
          },
          "owner": "cb8c1cd8-f22c-4e30-8a7c-42fa3e2e16b1",
          "updatedAt": "2023-09-20T09:09:06.134Z"
        },
        null
  },
  "errors": [
    {
      "path": [
        "syncDraws",
        "items",
        17,
        "drawBatch"
      ],
      "data": null,
      "errorType": "Unauthorized",
      "errorInfo": null,
      "locations": [
        {
          "line": 10,
          "column": 7,
          "sourceName": null
        }
      ],
      "message": "Not Authorized to access drawBatch on type Draw"
    },
    {
      "path": [
        "syncDraws",
        "items",
        17,
        "item"
      ],
      "locations": null,
      "message": "Cannot return null for non-nullable type: 'Item' within parent 'Draw' (/syncDraws/items[17]/item)"
    }
  ]
}

I get an error on my error handler with only this kind of information:

DataStoreError: failed to process graphqlResponseData
Caused by:
APIError: failed to process graphqlResponseData
Caused by:
DataStoreError: The key `__typename` was not found
Recovery suggestion: Check if the parsed JSON contains the expected `__typename`

Which don't help at all.

Is this issue about the error content or is there another ?

Moreover, the well received result are not insert in the datastore, but perhaps this might be another issue too ?

@lawmicha
Copy link
Member Author

Thank you for the example @gbitaudeau, this issue is to improve the usefulness of the error handler by providing a strongly typed error type like SyncError. If you were to receive a SyncError on the errorHandler with processName "sync" and errorType "Unauthorized", along with the message from the GraphQL error like "Not Authorized to access drawBatch on type Draw" would that be enough information for you to act on? What would you do in this case other than have the visibility that at least one item was unauthorized to be read during sync?

@lawmicha
Copy link
Member Author

lawmicha commented Sep 29, 2023

@gbitaudeau The response and errorHandler payload in your example is different from this issue. Could you open another issue to track this? The expected behavior of your example is the following

  • DataStore's initial sync should reconcile partial GraphQL responses by saving the items in the local Database and emitting the errors to the errorHandler. The errorHandler should be called twice, most likely being a DataStore error containing an underlying GraphQLError object.

The actual behavior

  • When DataStore calls AppSync, using the APIPlugin, APIPlugin's decoding logic failed to decode this to a partial response containing the data objects and errors array. The first change should be made in APIPlugin's decoding tests, making sure a partial response is instantiated. The given a response containing a list of items, where one of the items is null and two errors.
  • The second change, DataStore should handle partial responses correctly, by reconciling the items and emitting the errors back on the error handler.

@gbitaudeau
Copy link

Thank you for the example @gbitaudeau, this issue is to improve the usefulness of the error handler by providing a strongly typed error type like SyncError. If you were to receive a SyncError on the errorHandler with processName "sync" and errorType "Unauthorized", along with the message from the GraphQL error like "Not Authorized to access drawBatch on type Draw" would that be enough information for you to act on? What would you do in this case other than have the visibility that at least one item was unauthorized to be read during sync?

Yes this level of information would be very useful: we can send this to our error tracker (like crashlytics) and take actions to resolve the issue (like a database clean, a migration or even a new application version). We could encounter this kind of problem after a new model version or unexpected evolutions errors.

@gbitaudeau
Copy link

@gbitaudeau The response and errorHandler payload in your example is different from this issue. Could you open another issue to track this? The expected behavior of your example is the following

  • DataStore's initial sync should reconcile partial GraphQL responses by saving the items in the local Database and emitting the errors to the errorHandler. The errorHandler should be called twice, most likely being a DataStore error containing an underlying GraphQLError object.

The actual behavior

  • When DataStore calls AppSync, using the APIPlugin, APIPlugin's decoding logic failed to decode this to a partial response containing the data objects and errors array. The first change should be made in APIPlugin's decoding tests, making sure a partial response is instantiated. The given a response containing a list of items, where one of the items is null and two errors.
  • The second change, DataStore should handle partial responses correctly, by reconciling the items and emitting the errors back on the error handler.

I opened #3259

@lawmicha lawmicha added the feature-request Request a new feature label Oct 2, 2023
@github-actions
Copy link
Contributor

github-actions bot commented Oct 2, 2023

This has been identified as a feature request. If this feature is important to you, we strongly encourage you to give a 👍 reaction on the request. This helps us prioritize new features most important to you. Thank you!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
datastore Issues related to the DataStore category feature-parity feature-request Request a new feature
Projects
None yet
Development

No branches or pull requests

2 participants