amplify-android icon indicating copy to clipboard operation
amplify-android copied to clipboard

[API] Subscribe with arguments (real time filtering)

Open chiragmittal19 opened this issue 4 years ago • 8 comments

There should be a way to pass arguments to subscriptions so that only a subset is subscribed.

Model schema -

type MyData @model {
  id: ID!
  x: Int
  other_data: String
}

Subscription method onUpdateMyData which take an input parameter x -

type Subscription {
    onUpdateMyData(x: Int): MyData
        @aws_subscribe(mutations: ["updateMyData"])
}

Mutation method updateMyData -

type Mutation {
    updateMyData(input: UpdateMyDataInput!, condition: ModelMyDataConditionInput): MyData
}

Following the official documentation, I wrote the below code for subscription. Works as expected, i.e, gets triggered whenever the updateMyData mutation is called, irrespective of the value of x.

Amplify.API.subscribe(ModelSubscription.onUpdate(MyData::class.java),
    { Log.i("ApiQuickStart", "Subscription established") },
    fun(onUpdate: GraphQLResponse<MyData>) {
        if (onUpdate.hasData()) {
            Log.i("ApiQuickStart", "Update: " + onUpdate.data.toString())
        } else if (onUpdate.hasErrors()) {
            Log.e("ApiQuickStart", "Error: " + onUpdate.errors.joinToString { it.message })
        } else {
             Log.i("ApiQuickStart", "Null update")
        }
    },
    { onFailure -> Log.e("ApiQuickStart", "Subscription failed", onFailure) },
    { Log.i("ApiQuickStart", "Subscription completed") }
)

But the problem is, I only want to subscribe for a specific value of x, say 5. So, I tried by modifying the request -

ModelSubscription.onUpdate(MyData::class.java).apply {
    putVariable("x", 5)
}

This made no difference. I'm still receiving updates for other values of x.

This is how I call updateMyData mutation from AppSync console -

mutation {
  updateMyData( input: {
    id: 1,
    x: 5,
    other_data: "some updated info"
  } ) {
    id, x, other_data
  }
}

Subscription in AppSync console works like a charm, i.e, it gets triggered only for a subset where x = 5 -

subscription {
  onUpdateMyData(x: 5) {
    id, x, other_data
  }
}

Just to mention, DynamoDB is used as the data source.

chiragmittal19 avatar Jun 16 '20 07:06 chiragmittal19

@chiragmittal19

We could add a filtering argument to ModelSubscription.onUpdate(...), like:

public static <M extends Model> GraphQLRequest<M> onUpdate(
        Class<M> modelType, QueryPredicate filter) {
    ...
}

Is this what you have in mind?

This would constrain the set of results returned over the network, by means of a GraphQL predicate.

Since we don't have that, for the time-being, you could do the filtering on the client. I recommend using the Rx Bindings for this.

RxAmplify.API.subscribe(onUpdate(MyData::class.java))
    .filter { post -> !post.hasErrors() && post.hasData() }
    .map { post -> post.data }
    .filter { data -> data.x == "5" }
    .subscribe(
        { Log.i("Demo", "Found a match: $it") },
        { Log.i("Demo", "No matches.", it) }
    )

jamesonwilliams avatar Jun 16 '20 16:06 jamesonwilliams

A QueryPredicate sounds good. But in terms of performance and cost, will that be equivalent to passing an argument to the subscription method?

I guess it would be better to have an option to pass the complete string, maybe like this -

String subscriptionString = 
  ```subscription {
       onUpdateMyData(x: 5) {
         id, x, other_data
       }
     }```

Amplify.API.subscribe(subscriptionString,
    { Log.i("ApiQuickStart", "Subscription established") },
    fun(onUpdate: GraphQLResponse<MyData>) {
        if (onUpdate.hasData()) {
            Log.i("ApiQuickStart", "Update: " + onUpdate.data.toString())
        } else if (onUpdate.hasErrors()) {
            Log.e("ApiQuickStart", "Error: " + onUpdate.errors.joinToString { it.message })
        } else {
             Log.i("ApiQuickStart", "Null update")
        }
    },
    { onFailure -> Log.e("ApiQuickStart", "Subscription failed", onFailure) },
    { Log.i("ApiQuickStart", "Subscription completed") }
)

chiragmittal19 avatar Jun 17 '20 05:06 chiragmittal19

@chiragmittal19 Thanks for the feedback! We prioritize work based on the number of reactions an issue receives. I'll close this issue for now, but let's leave it as a feature request.

If you are reading this and would like to see this feature implemented, please add a 👍 here: https://github.com/aws-amplify/amplify-android/issues/580#issue-639443740

jamesonwilliams avatar Jul 01 '20 13:07 jamesonwilliams

Yes would like this feature. Very helpful to reduce network traffic

nin234 avatar Jul 26 '20 22:07 nin234

I also want this feature. Maybe this feature makes more secure in addition to moderating network traffic.

w-okada avatar Sep 12 '20 14:09 w-okada

For anyone who finds themselves here... AppSync has a feature that MIGHT help you like it helped me

https://docs.aws.amazon.com/appsync/latest/devguide/extensions.html

$extensions.setSubscriptionFilter

It allows you to add additional filters on the server side

SteveJamesDev avatar Aug 31 '22 00:08 SteveJamesDev

@jamesonwilliams Any update on the feature Request?

vibhorkhurana580 avatar May 08 '24 03:05 vibhorkhurana580

Hey, @vibhorkhurana580 - sorry, I haven't worked on this project in a number of years. Overall I'd encourage folks to migrate to Apollo Kotlin, which also supports AppSync's flavor of WebSockets if you're stuck on that backend for now.

jamesonwilliams avatar May 08 '24 05:05 jamesonwilliams