Skip to content

Commit 81f7962

Browse files
committed
Add api.disco.entities.find API function
This lets us search for a disco entity that implements a particular disco feature. The entity must be either our own JID, our domain's JID or one of the (top-level) items advertised by our domain JID.
1 parent 8b72400 commit 81f7962

File tree

3 files changed

+103
-7
lines changed

3 files changed

+103
-7
lines changed

src/headless/plugins/disco/api.js

Lines changed: 32 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { getOpenPromise } from '@converse/openpromise';
22
import _converse from '../../shared/_converse.js';
33
import api from '../../shared/api/index.js';
44
import converse from '../../shared/api/public.js';
5-
import log from "@converse/log";
5+
import log from '@converse/log';
66

77
const { Strophe, $iq } = converse.env;
88

@@ -197,6 +197,36 @@ export default {
197197
* @memberOf api.disco
198198
*/
199199
entities: {
200+
/**
201+
* Finds the first entity advertising a given feature.
202+
*
203+
* @method api.disco.entities.find
204+
* @param {string} feature The feature var to search for.
205+
* @returns {Promise<DiscoEntity|null>} The matching DiscoEntity instance or null if not found.
206+
*/
207+
async find(feature) {
208+
const bare = _converse.session.get('bare_jid');
209+
const bare_entity = await api.disco.entities.get(bare);
210+
if (bare_entity && (await bare_entity.getFeature(feature))) {
211+
return bare_entity;
212+
}
213+
214+
const domain = Strophe.getDomainFromJid(bare);
215+
const domain_entity = await api.disco.entities.get(domain);
216+
if (domain_entity && (await domain_entity.getFeature(feature))) {
217+
return domain_entity;
218+
}
219+
220+
const items = await api.disco.entities.items(domain);
221+
for (const entity of items) {
222+
const jid = entity.get('jid');
223+
if (await api.disco.features.has(feature, jid)) {
224+
return api.disco.entities.get(jid);
225+
}
226+
}
227+
return null;
228+
},
229+
200230
/**
201231
* Get the corresponding `DiscoEntity` instance.
202232
*
@@ -302,10 +332,7 @@ export default {
302332

303333
const items = await api.disco.entities.items(jid);
304334

305-
const promises = [
306-
entity.getFeature(feature),
307-
...items.map((i) => i.getFeature(feature)),
308-
];
335+
const promises = [entity.getFeature(feature), ...items.map((i) => i.getFeature(feature))];
309336
const result = await Promise.all(promises);
310337
return result.filter((f) => f instanceof Object);
311338
},

src/headless/plugins/disco/tests/disco.js

Lines changed: 63 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
/*global converse */
22
import mock from "../../../tests/mock.js";
33

4-
const { u, stx } = converse.env;
4+
const { u, stx, Strophe } = converse.env;
55

66
describe("Service Discovery", function () {
77

@@ -108,7 +108,6 @@ describe("Service Discovery", function () {
108108
['discoInitialized'], {},
109109
function (_converse) {
110110

111-
const { Strophe } = converse.env;
112111
spyOn(_converse.api, "trigger").and.callThrough();
113112
_converse.disco_entities.get(_converse.domain).features.create({'var': Strophe.NS.MAM});
114113
expect(_converse.api.trigger).toHaveBeenCalled();
@@ -117,4 +116,66 @@ describe("Service Discovery", function () {
117116
expect(last_call.args[1].get('var')).toBe(Strophe.NS.MAM);
118117
}));
119118
});
119+
120+
describe('api.disco.entities.find', function () {
121+
it(
122+
"returns our own JID's entity if the bare JID advertises the desired feature",
123+
mock.initConverse([], {}, async function (_converse) {
124+
const bare = _converse.session.get('bare_jid');
125+
await mock.waitUntilDiscoConfirmed(_converse, bare, [], ['feature1']);
126+
const result = await _converse.api.disco.entities.find('feature1');
127+
expect(result.get('jid')).toBe(bare);
128+
})
129+
);
130+
131+
it(
132+
'returns the domain entity if bare JID does not advertise but domain does',
133+
mock.initConverse([], {}, async function (_converse) {
134+
const bare = _converse.session.get('bare_jid');
135+
const domain = Strophe.getDomainFromJid(bare);
136+
await mock.waitUntilDiscoConfirmed(_converse, bare, [], []);
137+
await mock.waitUntilDiscoConfirmed(_converse, domain, [], ['feature2']);
138+
const result = await _converse.api.disco.entities.find('feature2');
139+
expect(result.get('jid')).toBe(domain);
140+
})
141+
);
142+
143+
it(
144+
'returns first matching item entity if neither bare nor domain advertises but an item does',
145+
mock.initConverse([], {}, async function (_converse) {
146+
const bare = _converse.session.get('bare_jid');
147+
const domain = Strophe.getDomainFromJid(bare);
148+
await mock.waitUntilDiscoConfirmed(_converse, bare, [], []);
149+
await mock.waitUntilDiscoConfirmed(
150+
_converse,
151+
domain,
152+
[{ 'category': 'server', 'type': 'IM' }],
153+
['http://jabber.org/protocol/disco#items']
154+
);
155+
await mock.waitUntilDiscoConfirmed(_converse, domain, [], [], ['a@b', 'c@d'], 'items');
156+
await mock.waitUntilDiscoConfirmed(_converse, 'a@b', [], []);
157+
await mock.waitUntilDiscoConfirmed(_converse, 'c@d', [], ['feature3']);
158+
const result = await _converse.api.disco.entities.find('feature3');
159+
expect(result.get('jid')).toBe('c@d');
160+
})
161+
);
162+
163+
it(
164+
'returns null if no entity matches',
165+
mock.initConverse([], {}, async function (_converse) {
166+
const bare = _converse.session.get('bare_jid');
167+
const domain = Strophe.getDomainFromJid(bare);
168+
await mock.waitUntilDiscoConfirmed(_converse, bare, [], []);
169+
await mock.waitUntilDiscoConfirmed(
170+
_converse,
171+
domain,
172+
[{ 'category': 'server', 'type': 'IM' }],
173+
['http://jabber.org/protocol/disco#items']
174+
);
175+
await mock.waitUntilDiscoConfirmed(_converse, domain, [], [], [], 'items');
176+
const result = await _converse.api.disco.entities.find('feature4');
177+
expect(result).toBeNull();
178+
})
179+
);
180+
});
120181
});

src/headless/types/plugins/disco/api.d.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,14 @@ declare namespace _default {
7979
*/
8080
export function items(jid: string, node?: string): Promise<any>;
8181
export namespace entities {
82+
/**
83+
* Finds the first entity advertising a given feature.
84+
*
85+
* @method api.disco.entities.find
86+
* @param {string} feature The feature var to search for.
87+
* @returns {Promise<DiscoEntity|null>} The matching DiscoEntity instance or null if not found.
88+
*/
89+
function find(feature: string): Promise<import("./entity").default | null>;
8290
/**
8391
* Get the corresponding `DiscoEntity` instance.
8492
*

0 commit comments

Comments
 (0)