Skip to content

Commit

Permalink
Fixes #1038: Support setting node config manually
Browse files Browse the repository at this point in the history
  • Loading branch information
jcbrand committed Dec 23, 2024
1 parent 558afde commit c4f67f4
Show file tree
Hide file tree
Showing 3 changed files with 183 additions and 28 deletions.
1 change: 1 addition & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
### Github Issues
- #122: Set horizontal layout direction based on the language
- #698: Add support for MUC private messages
- #1038: Support setting node config manually
- #1057: Removed the `mobile` view mode. Instead of setting `view_mode` to `mobile`, set it to `fullscreen`.
- #1174: Show MUC avatars in the rooms list
- #1195: Add actions to quote and copy messages
Expand Down
56 changes: 29 additions & 27 deletions src/headless/plugins/pubsub/api.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import log from '../../log.js';
import { parseErrorStanza } from '../../shared/parsers.js';
import { parseStanzaForPubSubConfig } from './parsers.js';

const { Strophe, stx, u } = converse.env;
const { Strophe, stx } = converse.env;

export default {
/**
Expand Down Expand Up @@ -49,10 +49,7 @@ export default {
try {
response = await api.sendIQ(stanza);
} catch (error) {
if (u.isErrorStanza(error)) {
throw parseErrorStanza(error);
}
throw error;
throw await parseErrorStanza(error);
}
return parseStanzaForPubSubConfig(response);
},
Expand All @@ -72,7 +69,7 @@ export default {
const entity_jid = jid || bare_jid;
const new_config = {
...(await api.pubsub.config.get(entity_jid, node)),
...config
...config,
};

const stanza = stx`
Expand All @@ -95,10 +92,7 @@ export default {
try {
await api.sendIQ(stanza);
} catch (error) {
if (u.isErrorStanza(error)) {
throw parseErrorStanza(error);
}
throw error;
throw await parseErrorStanza(error);
}
return new_config;
},
Expand All @@ -119,6 +113,9 @@ export default {
* @returns {Promise<void|Element>}
*/
async publish(jid, node, item, options, strict_options = true) {
if (!node) throw new Error('api.pubsub.config.publish: node value required');
if (!item) throw new Error('api.pubsub.config.publish: item value required');

const bare_jid = _converse.session.get('bare_jid');
const entity_jid = jid || bare_jid;

Expand Down Expand Up @@ -167,30 +164,35 @@ export default {
Strophe.getDomainFromJid(entity_jid)
)));

if (!supports_publish_options) {
if (strict_options) {
log.warn(`api.pubsub.publish: #publish-options not supported, refusing to publish item.`);
log.warn(stanza);
return;
} else {
log.warn(`api.pubsub.publish: #publish-options not supported, publishing anyway.`);
}
if (!supports_publish_options && strict_options) {
log.warn(`api.pubsub.publish: #publish-options not supported, refusing to publish item.`);
log.warn(stanza);
return;
}

try {
await api.sendIQ(stanza);
} catch (iq) {
const e = await parseErrorStanza(iq);
if (
iq instanceof Element &&
!strict_options &&
iq.querySelector(`precondition-not-met[xmlns="${Strophe.NS.PUBSUB_ERROR}"]`)
e.name === 'conflict' &&
/** @type {import('shared/errors').StanzaError} */(e).extra[Strophe.NS.PUBSUB_ERROR] === 'precondition-not-met'
) {
// The publish-options precondition couldn't be
// met. We re-publish but without publish-options.
const el = stanza.tree();
el.querySelector('publish-options').outerHTML = '';
log.warn(`api.pubsub.publish: Republishing without publish options. ${el.outerHTML}`);
await api.sendIQ(el);
// Manually configure the node if we can't set it via publish-options
await api.pubsub.config.set(entity_jid, node, options);
try {
await api.sendIQ(stanza);
} catch (e) {
log.error(e);
if (!strict_options) {
// The publish-options precondition couldn't be met.
// We re-publish but without publish-options.
const el = stanza.tree();
el.querySelector('publish-options').outerHTML = '';
log.warn(`api.pubsub.publish: #publish-options precondition-not-met, publishing anyway.`);
await api.sendIQ(el);
}
}
} else {
throw iq;
}
Expand Down
154 changes: 153 additions & 1 deletion src/headless/plugins/pubsub/tests/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -192,7 +192,7 @@ describe('The pubsub API', function () {
let first_error_thrown = false;
promise
.catch((e) => {
expect(e instanceof errors.NotImplementedError).toBe(true);
expect(e instanceof errors.FeatureNotImplementedError).toBe(true);
first_error_thrown = true;
})
.finally(() => {
Expand Down Expand Up @@ -467,4 +467,156 @@ describe('The pubsub API', function () {
})
);
});

describe('publishing to a node', function () {
it(
"will try to manually configure the node if publish-options aren't supported",
mock.initConverse([], {}, async function (_converse) {
await mock.waitForRoster(_converse, 'current', 0);

const pubsub_jid = 'pubsub.shakespeare.lit';

const { api } = _converse;
const sent_stanzas = api.connection.get().sent_stanzas;
const own_jid = _converse.session.get('jid');

const node = 'princely_musings';
const promise = api.pubsub.publish(pubsub_jid, node, stx`<item></item>`, { access_model: 'whitelist' });

await mock.waitUntilDiscoConfirmed(
_converse,
pubsub_jid,
[{ 'category': 'pubsub', 'type': 'pep' }],
['http://jabber.org/protocol/pubsub#publish-options']
);

let sent_stanza = await u.waitUntil(() =>
sent_stanzas.filter((iq) => iq.querySelector('pubsub publish')).pop()
);
expect(sent_stanza).toEqualStanza(stx`
<iq type="set"
from="${_converse.bare_jid}"
to="${pubsub_jid}"
xmlns="jabber:client"
id="${sent_stanza.getAttribute('id')}">
<pubsub xmlns="http://jabber.org/protocol/pubsub">
<publish node="princely_musings"><item/></publish>
<publish-options>
<x xmlns="jabber:x:data" type="submit">
<field var="FORM_TYPE" type="hidden">
<value>http://jabber.org/protocol/pubsub#publish-options</value>
</field>
<field var="pubsub#access_model"><value>whitelist</value></field>
</x>
</publish-options>
</pubsub>
</iq>`);

let response = stx`<iq type='error'
xmlns="jabber:client"
from='${pubsub_jid}'
to='${own_jid}'
id="${sent_stanza.getAttribute('id')}">
<error type='modify'>
<conflict xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/>
<precondition-not-met xmlns='http://jabber.org/protocol/pubsub#errors'/>
</error>
</iq>`;
_converse.api.connection.get()._dataRecv(mock.createRequest(response));

sent_stanza = await u.waitUntil(() =>
sent_stanzas.filter((iq) => iq.querySelector('pubsub configure')).pop()
);
_converse.api.connection.get()._dataRecv(
mock.createRequest(stx`
<iq type='result'
xmlns="jabber:client"
from='${pubsub_jid}'
to='${own_jid}'
id="${sent_stanza.getAttribute('id')}">
<pubsub xmlns='http://jabber.org/protocol/pubsub#owner'>
<configure node='${node}'>
<x xmlns='jabber:x:data' type='form'>
<field var='pubsub#access_model' type='list-single' label='Specify the subscriber model'>
<option><value>authorize</value></option>
<option><value>open</value></option>
<option><value>presence</value></option>
<option><value>roster</value></option>
<option><value>whitelist</value></option>
<value>open</value>
</field>
</x>
</configure>
</pubsub>
</iq>`)
);

sent_stanza = await u.waitUntil(() =>
sent_stanzas
.filter((iq) => iq.getAttribute('type') === 'set' && iq.querySelector('pubsub configure'))
.pop()
);

expect(sent_stanza).toEqualStanza(stx`<iq xmlns="jabber:client"
from="${_converse.bare_jid}"
to="${pubsub_jid}"
type="set"
id="${sent_stanza.getAttribute('id')}">
<pubsub xmlns="http://jabber.org/protocol/pubsub#owner">
<configure node="princely_musings">
<x xmlns="jabber:x:data" type="submit">
<field var="FORM_TYPE" type="hidden"><value>http://jabber.org/protocol/pubsub#nodeconfig</value></field>
<field var="access_model"><value>whitelist</value></field>
</x>
</configure>
</pubsub>
</iq>`);

_converse.api.connection.get()._dataRecv(
mock.createRequest(stx`
<iq type='result'
xmlns="jabber:client"
from='${pubsub_jid}'
to='${own_jid}'
id="${sent_stanza.getAttribute('id')}"></iq>`)
);

// Clear old stanzas
while (sent_stanzas.length) sent_stanzas.pop();

sent_stanza = await u.waitUntil(() =>
sent_stanzas.filter((iq) => iq.querySelector('pubsub publish')).pop()
);
expect(sent_stanza).toEqualStanza(stx`
<iq type="set"
from="${_converse.bare_jid}"
to="${pubsub_jid}"
xmlns="jabber:client"
id="${sent_stanza.getAttribute('id')}">
<pubsub xmlns="http://jabber.org/protocol/pubsub">
<publish node="princely_musings"><item/></publish>
<publish-options>
<x xmlns="jabber:x:data" type="submit">
<field var="FORM_TYPE" type="hidden">
<value>http://jabber.org/protocol/pubsub#publish-options</value>
</field>
<field var="pubsub#access_model"><value>whitelist</value></field>
</x>
</publish-options>
</pubsub>
</iq>`);

_converse.api.connection.get()._dataRecv(
mock.createRequest(stx`
<iq type='result'
xmlns="jabber:client"
from='${pubsub_jid}'
to='${own_jid}'
id="${sent_stanza.getAttribute('id')}"></iq>`)
);

await promise;
})
);
});
});

0 comments on commit c4f67f4

Please sign in to comment.