Skip to content

Commit 076cd8a

Browse files
committed
disco: support optional JID argument in api.disco.entities.find
1 parent 5b8f354 commit 076cd8a

File tree

4 files changed

+60
-13
lines changed

4 files changed

+60
-13
lines changed

src/headless/plugins/disco/api.js

Lines changed: 29 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -202,20 +202,39 @@ export default {
202202
*
203203
* @method api.disco.entities.find
204204
* @param {string} feature The feature var to search for.
205-
* @returns {Promise<DiscoEntity|null>} The matching DiscoEntity instance or null if not found.
205+
* @param {string} [jid] The entity JID whose subtree to search. If omitted, own bare JID and domain are queried.
206+
* @returns {Promise<DiscoEntity[]>} An array of matching DiscoEntity instances.
206207
*/
207-
async find(feature) {
208+
async find(feature, jid) {
208209
await api.waitUntil('discoInitialized');
209210
const disco_entities = /** @type {DiscoEntities} */ (_converse.state.disco_entities);
210211
if (!disco_entities) {
211212
return [];
212213
}
213-
const checks = disco_entities.map(async (entity) => {
214+
let candidates = [];
215+
if (jid) {
216+
const entity = await api.disco.entities.get(jid, true);
217+
if (entity) candidates.push(entity);
218+
const items = await api.disco.entities.items(jid);
219+
candidates.push(...items);
220+
} else {
221+
const bare = _converse.session.get('bare_jid');
222+
const bare_entity = await api.disco.entities.get(bare, true);
223+
if (bare_entity) candidates.push(bare_entity);
224+
const domain = Strophe.getDomainFromJid(bare);
225+
const domain_entity = await api.disco.entities.get(domain, true);
226+
if (domain_entity) candidates.push(domain_entity);
227+
const items = await api.disco.entities.items(domain);
228+
candidates.push(...items);
229+
}
230+
// Deduplicate by JID
231+
const unique = Array.from(new Map(candidates.map(e => [e.get('jid'), e])).values());
232+
const checks = unique.map(async (entity) => {
214233
const has = await entity.getFeature(feature);
215234
return has ? entity : null;
216235
});
217236
const results = await Promise.all(checks);
218-
return results.filter((e) => e instanceof Object);
237+
return results.filter(e => e);
219238
},
220239

221240
/**
@@ -254,10 +273,12 @@ export default {
254273
*/
255274
async items(jid) {
256275
const entity = await api.disco.entities.get(jid);
257-
await entity.waitUntilItemsFetched;
258-
259-
const disco_entities = /** @type {DiscoEntities} */ (_converse.state.disco_entities);
260-
return disco_entities.filter((e) => e.get('parent_jids')?.includes(jid));
276+
if (entity) {
277+
await entity.waitUntilItemsFetched;
278+
const disco_entities = /** @type {DiscoEntities} */ (_converse.state.disco_entities);
279+
return disco_entities.filter((e) => e.get('parent_jids')?.includes(jid));
280+
}
281+
return [];
261282
},
262283

263284
/**

src/headless/plugins/disco/entity.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -192,7 +192,7 @@ class DiscoEntity extends Model {
192192
const jid = item.getAttribute('jid');
193193
let entity = _converse.state.disco_entities.get(jid);
194194
if (entity) {
195-
const parent_jids = entity.get('parent_jids');
195+
const parent_jids = entity.get('parent_jids') || [];
196196
entity.set({ parent_jids: [...parent_jids, this.get('jid')] });
197197
} else {
198198
entity = api.disco.entities.create({

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

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -186,8 +186,8 @@ describe("Service Discovery", function () {
186186
const results = await _converse.api.disco.entities.find('feature');
187187
expect(Array.isArray(results)).toBe(true);
188188
expect(results.length).toBe(4);
189-
expect(results[0].get('jid')).toBe(domain);
190-
expect(results[1].get('jid')).toBe(bare);
189+
expect(results[0].get('jid')).toBe(bare);
190+
expect(results[1].get('jid')).toBe(domain);
191191
expect(results[2].get('jid')).toBe('a@b');
192192
expect(results[3].get('jid')).toBe('c@d');
193193
})
@@ -211,5 +211,30 @@ describe("Service Discovery", function () {
211211
expect(results.length).toBe(0);
212212
})
213213
);
214+
215+
it(
216+
'searches only under the provided JID subtree',
217+
mock.initConverse([], {}, async function (_converse) {
218+
const bare = _converse.session.get('bare_jid');
219+
const domain = Strophe.getDomainFromJid(bare);
220+
// domain items: a@b supports featureX, c@d does not
221+
await mock.waitUntilDiscoConfirmed(
222+
_converse,
223+
domain,
224+
[{ 'category': 'server', 'type': 'IM' }],
225+
['http://jabber.org/protocol/disco#items']
226+
);
227+
await mock.waitUntilDiscoConfirmed(_converse, domain, [], [], ['a@b', 'c@d'], 'items');
228+
await mock.waitUntilDiscoConfirmed(_converse, 'a@b', [], ['featureX']);
229+
await mock.waitUntilDiscoConfirmed(_converse, 'c@d', [], []);
230+
const resultsForAB = await _converse.api.disco.entities.find('featureX', 'a@b');
231+
expect(Array.isArray(resultsForAB)).toBe(true);
232+
expect(resultsForAB.length).toBe(1);
233+
expect(resultsForAB[0].get('jid')).toBe('a@b');
234+
const resultsForCD = await _converse.api.disco.entities.find('featureX', 'c@d');
235+
expect(Array.isArray(resultsForCD)).toBe(true);
236+
expect(resultsForCD.length).toBe(0);
237+
})
238+
);
214239
});
215240
});

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

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -84,9 +84,10 @@ declare namespace _default {
8484
*
8585
* @method api.disco.entities.find
8686
* @param {string} feature The feature var to search for.
87-
* @returns {Promise<DiscoEntity|null>} The matching DiscoEntity instance or null if not found.
87+
* @param {string} [jid] The entity JID whose subtree to search. If omitted, own bare JID and domain are queried.
88+
* @returns {Promise<DiscoEntity[]>} An array of matching DiscoEntity instances.
8889
*/
89-
function find(feature: string): Promise<import("./entity").default | null>;
90+
function find(feature: string, jid?: string): Promise<import("./entity").default[]>;
9091
/**
9192
* Get the corresponding `DiscoEntity` instance.
9293
*

0 commit comments

Comments
 (0)