Closed elevenetc closed 4 years ago
@psylinse This code doesn't really create a channel:
val channel = client.channel(ModelType.channel_messaging, hashMapOf<String, Any>(), listOf(user, userToChatWith))
It creates new in memory instance which doesn't do any request:
public Channel channel(String type, HashMap<String, Object> extraData, List<String> members) {
return new Channel(this, type, extraData, members);
}
Do you call client.queryChannel
to create new channel? This code snippet works and it creates channel with two members:
List<String> list = new ArrayList<>();
list.add("user-id-a");
list.add("user-id-b");
Client client = StreamChat.getInstance(getApplication());
Channel newChannel = client.channel(ModelType.channel_messaging, new HashMap<String, Object>(), list);
client.queryChannel(newChannel, new ChannelQueryRequest(), new QueryChannelCallback() {
@Override
public void onSuccess(ChannelState response) {
}
@Override
public void onError(String errMsg, int errCode) {
}
});
Could you try it?
Everything works if I change to using the channel.query
or client.queryChannel
. The source of my issue was I was using .watch
first, which is what was trying to create the channel. It appears the semantics of .watch
are different or it's straight up dropping the members
field. Can you try switching your code to using .watch
instead of .queryChannel
and see what happens?
See: https://github.com/GetStream/stream-chat-android/issues/184#issuecomment-587134325
FWIW, if I we're to guess without digging in too deep, I think there's some weird mix of type erasure + serialization that's the root cause here. Since .watch
takes a ChannelWatchRequest but ultimately gets mapped to a ChannelQueryRequest
in retrofit, I wonder if it's somehow losing information when serializing. Just speculation, but might be a useful place to start.
Also, for reference, this is the working code: https://github.com/nparsons08/stream-flutter/blob/2-messaging/mobile/android/app/src/main/kotlin/io/getstream/flutter_the_stream/MainActivity.kt#L135-L179
The only difference when it was failing was the use of .watch
instead of .query
@psylinse I tried your code and it still works with watch
. This code:
Client client = StreamChat.getInstance(getActivity());
String name = "new-20";
List<String> members = new ArrayList<>();
HashMap<String, Object> extra = new HashMap<>();
members.add("stream-eugene");
members.add("stream-eugene-2");
extra.put("members", members);
extra.put("name", name);
Channel newChannel = client.channel(ModelType.channel_messaging, name, extra);
newChannel.watch(new ChannelWatchRequest().withMessages(25), new QueryWatchCallback() {
@Override
public void onSuccess(ChannelState response) {
}
@Override
public void onError(String errMsg, int errCode) {
}
});
Is visible in OkHttp
logcat logs:
--> POST https://chat-us-east-staging.stream-io-api.com/channels/messaging/new-20/query?api_key=d2q3juekvgsf&user_id=stream-eugene&client_id=d65b8877-dc66-44b1-b700-61a141e6310a
Content-Type: application/json; charset=UTF-8
Content-Length: 139
{"data":{"name":"new-20","members":["stream-eugene","stream-eugene-2"]},"messages":{"limit":25},"presence":false,"state":true,"watch":true}
--> END POST (139-byte body)
Which is finished successfully with 201
.
@psylinse I think something is missed around users which you try to use with watch
request:
val channelId = listOf(user, userToChatWith).sorted().joinToString("-")
val channel = client.channel(ModelType.channel_messaging, channelId, hashMapOf<String, Any>("members" to listOf(user, userToChatWith)))
Your error says about chuck
user (which is current one, you called setUser(chuck)). It means that user chuck
is not a member of the channel, so he can't watch it. So whats inside this list listOf(user, userToChatWith)
? Is it listOf("chuck", "another-user")
?
Can you try joining the channel with the other user? If I created the channel with watch with an explicit channelId, it was created fine for the first user but there are read issues for the second user:
GetOrCreateChannel failed with error: "User 'chuck' with role user is not allowed to access Resource ReadChannel on channel type messaging
My code is just a list of strings: listOf("creating-user", "chuck")
.
Also, try creating the channel without any channelId
information:
val channel = client.channel(ModelType.channel_messaging, hashMapOf<String, Any>(), listOf(user, userToChatWith))
You should see it fail to create the channel entirely upon .watch
Can you try joining the channel with the other user? If I created the channel with watch with an explicit channelId, it was created fine for the first user but there are read issues for the second user:
How exactly you're adding a new member to the channel?
Also, try creating the channel without any
channelId
information:
val channel = client.channel(ModelType.channel_messaging, hashMapOf<String, Any>(), listOf(user, userToChatWith))
You should see it fail to create the channel entirely upon
.watch
Tried it and everything works as expected. Backend creates channel id if it is not defined in request.
The same error says about the same thing: chuck
is not a member of the channel, that's why he is not allowed to watch the channel
We're walking around the same problem and seems without logcat logs I can't really help you. I have feeling that still something wrong with users. If you record all logs into file and post it here I can try to find the problem.
I've created two scenarios to show the two ways .watch
breaks. I'm using the full code and giving full logs in case there's some nuance I'm missing somewhere. When checking out the logs, the interesting stuff happens at the bottom.
Scenario 1: nico chats with sara, explicit channel id
In this case, we're only changing channel.query
to channel.watch
with an explicit channel id. This one fails for Sara, who joins the chat second. Nico is able to create/watch the channel. Sara hits a permission error.
Notice the channel is created with no members, so only Nico has permission to join.
Scenario 2: hugo tries to chat with anyone, no explicit channel id
Here hugo can't start a chat with anyone. In this case, we're only changing channel.query
to channel.watch
without an explicit channel id. He's trying to create channels without explicit channel id's and hits a permission error right away:
Notice it's failing on members being empty again. What am I missing? It seems like channel.query
w/ a .withWatch
should be equivalent to channel.watch
. It feels like there's some serialization issue where .watch
is causing the POST to be void of member information.
I've confirmed the code with .query
works. Here's a scenario with henry chatting with john:
Reviewed logs and everything looks correct. Just to have the same understanding:
Scenario 1:
nico-sara
. Response says that there is no members.nico-sara
To fix Scenario 1
channel.update
where sara
is added as a memberScenario 2:
To fix Scenario 2
Few things about how backend works:
POST /channels/{type}/query
and POST /channels/{type}/{channel-id}/query
. These endpoints may be used to
watch=true
to receive socket notifications about the channel for current userPOST /channels/{type}/query
requires at least 2 members to create channel, otherwise error is returnedPOST /channels/{type}/{channel-id}/query
doesn't require memberswithWatch
or watch
are not mixed with members.About missing members
Notice it's failing on members being empty again. What am I missing?
Here I can't see where you're adding members.
Here is snippet which uses withWatch
with members and no channel id:
HashMap<String, Object> extraData = new HashMap<>();
extraData.put("name", channelName + "(no id with watch)");
List<String> members = new ArrayList<>();
members.add("stream-eugene"); //current user
members.add("stream-eugene-2");
extraData.put("members", members);
Channel channel = new Channel(client, ModelType.channel_messaging, extraData, members);
ChannelQueryRequest request = new ChannelQueryRequest().withMessages(10).withWatch();
channel.query(request, new QueryChannelCallback() {
@Override
public void onSuccess(ChannelState response) {
}
@Override
public void onError(String errMsg, int errCode) {
}
});
And in logs it has members and watch:true
:
OkHttp D --> POST https://chat-us-east-staging.stream-io-api.com/channels/messaging/query?api_key=d2q3juekvgsf&user_id=stream-eugene&client_id=f5e67ef4-25d6-42f8-bd49-99a5d15521e6
D Content-Type: application/json; charset=UTF-8
D Content-Length: 160
D {"data":{"name":"hello-002(no id with watch)","members":["stream-eugene","stream-eugene-2"]},"messages":{"limit":10},"presence":false,"state":true,"watch":true}
D --> END POST (160-byte body)
OkHttp D <-- 201 Created https://chat-us-east-staging.stream-io-api.com/channels/messaging/query?api_key=d2q3juekvgsf&user_id=stream-eugene&client_id=f5e67ef4-25d6-42f8-bd49-99a5d15521e6 (692ms)
D Server: fasthttp
D Date: Thu, 05 Mar 2020 13:37:18 GMT
D Content-Type: application/json;charset=utf-8
D Content-Length: 3328
D X-Ratelimit-Limit: 10000
D Access-Control-Max-Age: 86400
D Access-Control-Allow-Headers: x-requested-with, content-type, accept, origin, authorization, x-csrftoken, x-stream-client, stream-auth-type
D Access-Control-Allow-Methods: GET, POST, PUT, PATCH, DELETE, OPTIONS
D X-Ratelimit-Remaining: 9999
D X-Ratelimit-Reset: 1583415480
D Access-Control-Allow-Origin: *
D Cache-Control: no-cache
D {"channel":{"id":"!members-uCKt4UDFUY2_IQGW6Fx_VSUaAuBSdIKye7vYJ6AjowQ","type":"messaging","cid":"messaging:!members-uCKt4UDFUY2_IQGW6Fx_VSUaAuBSdIKye7vYJ6AjowQ","last_message_at":"2020-03-05T13:35:58.876192Z","created_at":"2020-02-18T14:07:00.957059
Z","updated_at":"2020-02-18T14:07:00.95706Z","created_by":{"id":"stream-eugene","role":"user","created_at":"2020-01-20T14:42:19.78607Z","updated_at":"2020-03-05T13:35:06.380982Z","last_active":"2020-03-05T13:35:06.376615Z","banned":false,"online":tru
e,"extraData":{},"image":"https://bit.ly/31EzdNK","name":"stream-eugene","totalUnreadCount":0,"unreadChannels":0},"frozen":false,"member_count":2,"config":{"created_at":"2020-03-05T12:52:42.205963741Z","updated_at":"2020-03-05T12:52:42.2059639Z","nam
e":"messaging","typing_events":true,"read_events":true,"connect_events":true,"search":true,"reactions":true,"replies":true,"mutes":true,"uploads":true,"url_enrichment":true,"message_retention":"infinite","max_message_length":5000,"automod":"disabled"
,"automod_behavior":"flag","commands":[{"name":"giphy","description":"Post a random gif to the channel","args":"[text]","set":"fun_set"}]}},"messages":[{"id":"stream-eugene-638f1bc4-29f6-46f1-b0c9-4748fb528264","text":"Q","html":"\u003cp\u003eQ\u003c
/p\u003e\n","type":"regular","user":{"id":"stream-eugene","role":"user","created_at":"2020-01-20T14:42:19.78607Z","updated_at":"2020-03-05T13:35:06.380982Z","last_active":"2020-03-05T13:35:06.376615Z","banned":false,"online":true,"extraData":{},"imag
e":"https://bit.ly/31EzdNK","name":"stream-eugene","totalUnreadCount":0,"unreadChannels":0},"attachments":[],"latest_reactions":[],"own_reactions":[],"reaction_counts":{},"reaction_scores":{},"reply_count":0,"created_at":"2020-03-05T13:35:58.876192Z"
,"updated_at":"2020-03-05T13:35:58.876193Z","mentioned_users":[]}],"watcher_count":1,"read":[{"user":{"id":"stream-eugene-2","role":"user","created_at":"2020-01-23T13:12:52.01943Z","updated_at":"2020-03-04T16:44:41.493571Z","last_active":"2020-03-04T
16:44:41.490551Z","banned":false,"online":false,"image":"https://bit.ly/2BAm5OT","name":"stream-eugene-2"},"last_read":"2020-02-18T14:07:00.972643328Z"},{"user":{"id":"stream-eugene","role":"user","created_at":"2020-01-20T14:42:19.78607Z","updated_at
":"2020-03-05T13:35:06.380982Z","last_active":"2020-03-05T13:35:06.376615Z","banned":false,"online":true,"extraData":{},"image":"https://bit.ly/31EzdNK","name":"stream-eugene","totalUnreadCount":0,"unreadChannels":0},"last_read":"2020-02-18T14:07:00.
97229952Z"}],"members":[{"user":{"id":"stream-eugene","role":"user","created_at":"2020-01-20T14:42:19.78607Z","updated_at":"2020-03-05T13:35:06.380982Z","last_active":"2020-03-05T13:35:06.376615Z","banned":false,"online":true,"name":"stream-eugene","
totalUnreadCount":0,"unreadChannels":0,"extraData":{},"image":"https://bit.ly/31EzdNK"},"role":"owner","created_at":"2020-02-18T14:07:00.963203Z","updated_at":"2020-02-18T14:07:00.963203Z"},{"user":{"id":"stream-eugene-2","role":"user","created_at":"
2020-01-23T13:12:52.01943Z","updated_at":"2020-03-04T16:44:41.493571Z","last_active":"2020-03-04T16:44:41.490551Z","banned":false,"online":false,"image":"https://bit.ly/2BAm5OT","name":"stream-eugene-2"},"role":"member","created_at":"2020-02-18T14:07
:00.963203Z","updated_at":"2020-02-18T14:07:00.963203Z"}],"duration":"6.20ms"}
Hmm, I don't think that's correct still. The line you linked is for a different channel set up (livestream
). The method we're looking at is .setupPrivateChannel
in my source. Here are the relevant lines from that same commit. Sorry for that confusion.
In Scenario 1, calling .update
is not required if I use .query
. In Scenario 2, I'm doing exactly what you describe and it does not work. Here's some more info:
Scenario 1
I shouldn't have to call .update
. What is wrong with this line in the scenario 1 branch? Nico is creating the channel with a .watch
and member information should be in the request because of that line. In the logs, it is not making it into the POST
. You're saying to call .update
after, but I don't need to do this if I use .query
instead of .watch
initially as nico. The request is generated with member data with .query
but is absent for .watch
. Just for clarity, here is the relevant code cut out of my source:
val client = StreamChat.getInstance(application)
val channel = client.channel(ModelType.channel_messaging, channelId, hashMapOf<String, Any>("members" to listOf(user, userToChatWith)))
channel.watch(ChannelWatchRequest().withMessages(25), object : QueryWatchCallback {
override fun onSuccess(response: ChannelState) {
// do stuff
}
override fun onError(errMsg: String, errCode: Int) {
// error
}
})
Scenario 2
I am adding members: see this line in the scenario 2 branch. Once again, if I call .query
it works fine, only .watch
fails. If I add a channel id
we end up at Scenario 1
. Here is the relevant code sliced out:
val client = StreamChat.getInstance(application)
val channel = client.channel(ModelType.channel_messaging, hashMapOf<String, Any>(), listOf(user, userToChatWith))
channel.watch(ChannelWatchRequest().withMessages(25), object : QueryWatchCallback {
override fun onSuccess(response: ChannelState) {
eventSink.success(ObjectMapper().writeValueAsString(response.messages))
}
override fun onError(errMsg: String, errCode: Int) {
eventSink.error(errCode.toString(), errMsg, null)
}
})
I really think there's a semantics bug between .watch
and .query
.
BTW, the code should be easily runnable with the associated blog post in each branch if you want to repro it:
This problem is going to be completely resolved on the upcoming v4 release, I am going to close this issue. You can already try the beta version of v4 or otherwise subscribe to this issue #554 to get notified when v4 is released as stable.
I'm receiving this issue with the latest client 3.3.0. If I create the channel like so (kotlin):
it leads to this error:
I tried with setting the
channelId
explicitly however that leads to an unreadable channel for the second user to join:which leads to this error for the second user (works fine for the user starting the channel):
GetOrCreateChannel failed with error: "User 'chuck' with role user is not allowed to access Resource ReadChannel on channel type messaging
when I try to.watch
the channel.Both users are being created as a part of a log in process via a nodejs backend:
What am I doing wrong?
Originally posted by @psylinse in https://github.com/GetStream/stream-chat-android/issues/184#issuecomment-587122673