Merge pull request #935 from podemos-info/api

API Improvements (v2)
This commit is contained in:
Tomas Bures 2020-08-26 11:58:48 +02:00 committed by GitHub
commit 613a6fb1f0
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 183 additions and 51 deletions

View file

@ -86,18 +86,44 @@ export default class API extends Component {
</div> </div>
</div> </div>
<div className="card mb-3"> <div class="accordion" id="apicalls">
<div className="card-header"> <div class="card">
<b>POST /api/subscribe/:listId {t('addSubscription')}</b> <div class="card-header">
</div> <button type="button" class="btn btn-link" data-toggle="collapse" data-target="#moresubscribers"><h4>GET /api/subscriptions/:listCid {t('Get subscribers')}</h4></button>
<div className="card-body"> </div>
<p className="card-text"> <div id="moresubscribers" class="collapse" data-parent="#apicalls">
{t('thisApiCallEitherInsertsANewSubscription')} <div class="card-body">
</p> <p>
</div> {t('Get subscribers')}
</div> </p>
<h4>POST /api/subscribe/:listId {t('addSubscription')}</h4>
<p>
{t('Query params')}
</p>
<ul>
<li><strong>access_token</strong> {t('yourPersonalAccessToken')}
<ul>
<li><strong>start</strong> {t('startPosition')} (<em>{t('optionalDefault0')}</em>)</li>
<li><strong>limit</strong> {t('limitEmailsCountInResponse')} (<em>{t('optionalDefault10000')}</em>)</li>
</ul>
</li>
</ul>
<p>
<strong>{t('example')}</strong>
</p>
<pre>curl -XGET '{getUrl(`api/subscriptions/P5wKkz-e7?access_token=${accessToken}&limit=10&start=10&search=gmail`)}' </pre>
</div>
</div>
</div>
<div class="card">
<div class="card-header">
<h4><button type="button" class="btn btn-link" data-toggle="collapse" data-target="#moresubscribe"><h4>POST /api/subscribe/:listCid {t('addSubscription')}</h4></button></h4>
</div>
<div id="moresubscribe" class="collapse" data-parent="#apicalls">
<div class="card-body">
<p> <p>
{t('thisApiCallEitherInsertsANewSubscription')} {t('thisApiCallEitherInsertsANewSubscription')}
</p> </p>
@ -141,8 +167,20 @@ export default class API extends Component {
<pre>curl -XPOST '{getUrl(`api/subscribe/B16uVTdW?access_token=${accessToken}`)}' \<br/> <pre>curl -XPOST '{getUrl(`api/subscribe/B16uVTdW?access_token=${accessToken}`)}' \<br/>
--data 'EMAIL=test@example.com&amp;MERGE_CHECKBOX=yes&amp;REQUIRE_CONFIRMATION=yes'</pre> --data 'EMAIL=test@example.com&amp;MERGE_CHECKBOX=yes&amp;REQUIRE_CONFIRMATION=yes'</pre>
<h4>POST /api/unsubscribe/:listId {t('removeSubscription')}</h4> <p>
{t('Response example')}:
</p>
<pre>"data": ("id":"TTrw41znK")</pre>
</div>
</div>
</div>
<div class="card">
<div class="card-header">
<button type="button" class="btn btn-link" data-toggle="collapse" data-target="#moreunsubscribe"><h4>POST /api/unsubscribe/:listCId {t('removeSubscription')}</h4></button>
</div>
<div id="moreunsubscribe" class="collapse" data-parent="#apicalls">
<div class="card-body">
<p> <p>
{t('thisApiCallMarksASubscriptionAs')} {t('thisApiCallMarksASubscriptionAs')}
</p> </p>
@ -168,8 +206,20 @@ export default class API extends Component {
<pre>curl -XPOST '{getUrl(`api/unsubscribe/B16uVTdW?access_token=${accessToken}`)}' \<br/> <pre>curl -XPOST '{getUrl(`api/unsubscribe/B16uVTdW?access_token=${accessToken}`)}' \<br/>
--data 'EMAIL=test@example.com'</pre> --data 'EMAIL=test@example.com'</pre>
<h4>POST /api/delete/:listId {t('deleteSubscription')}</h4> <p>
{t('Response example')}:
</p>
<pre>"data": ("id":"TTrw41znK", "unsubscribed":true)</pre>
</div>
</div>
</div>
<div class="card">
<div class="card-header">
<button type="button" class="btn btn-link" data-toggle="collapse" data-target="#moredelete"><h4>POST /api/delete/:listCId {t('deleteSubscription')}</h4></button>
</div>
<div id="moredelete" class="collapse" data-parent="#apicalls">
<div class="card-body">
<p> <p>
{t('thisApiCallDeletesASubscription')} {t('thisApiCallDeletesASubscription')}
</p> </p>
@ -194,9 +244,20 @@ export default class API extends Component {
<pre>curl -XPOST '{getUrl(`api/delete/B16uVTdW?access_token=${accessToken}`)}' \<br/> <pre>curl -XPOST '{getUrl(`api/delete/B16uVTdW?access_token=${accessToken}`)}' \<br/>
--data 'EMAIL=test@example.com'</pre> --data 'EMAIL=test@example.com'</pre>
<p>
{t('Response example')}:
</p>
<pre>"data": ("id":"TTrw41znK", "deleted":true)</pre>
<h4>POST /api/field/:listId {t('addNewCustomField')}</h4> </div>
</div>
</div>
<div class="card">
<div class="card-header">
<button type="button" class="btn btn-link" data-toggle="collapse" data-target="#morefield"><h4>POST /api/field/:listId {t('addNewCustomField')}</h4></button>
</div>
<div id="morefield" class="collapse" data-parent="#apicalls">
<div class="card-body">
<p> <p>
{t('thisApiCallCreatesANewCustomFieldForA')} {t('thisApiCallCreatesANewCustomFieldForA')}
</p> </p>
@ -241,11 +302,21 @@ export default class API extends Component {
</p> </p>
<pre>curl -XPOST '{getUrl(`api/field/B16uVTdW?access_token=${accessToken}`)}' \<br/> <pre>curl -XPOST '{getUrl(`api/field/B16uVTdW?access_token=${accessToken}`)}' \<br/>
--data 'NAME=Birthday&amp;TYPE=birthday-us&amp;VISIBLE=yes'</pre> --data 'NAME=Comment&TYPE=text'</pre>
<h4>GET /api/blacklist/get {t('getListOfBlacklistedEmails')}</h4>
<p> <p>
{t('Response example')}:
</p>
<pre>"data": ("id":22, "tag":"MERGE_COMMENT")</pre>
</div>
</div>
</div>
<div class="card">
<div class="card-header">
<button type="button" class="btn btn-link" data-toggle="collapse" data-target="#moreblacklistget"><h4>GET /api/blacklist/get {t('getListOfBlacklistedEmails')}</h4></button>
</div>
<div id="moreblacklistget" class="collapse" data-parent="#apicalls">
<div class="card-body">
<p>
{t('thisApiCallGetListOfBlacklistedEmails')} {t('thisApiCallGetListOfBlacklistedEmails')}
</p> </p>
@ -268,8 +339,15 @@ export default class API extends Component {
<pre>curl -XGET '{getUrl(`api/blacklist/get?access_token=${accessToken}&limit=10&start=10&search=gmail`)}' </pre> <pre>curl -XGET '{getUrl(`api/blacklist/get?access_token=${accessToken}&limit=10&start=10&search=gmail`)}' </pre>
<h4>POST /api/blacklist/add {t('addEmailToBlacklist')}</h4> </div>
</div>
</div>
<div class="card">
<div class="card-header">
<button type="button" class="btn btn-link" data-toggle="collapse" data-target="#moreblacklistadd"><h4>POST /api/blacklist/add {t('addEmailToBlacklist')}</h4></button>
</div>
<div id="moreblacklistadd" class="collapse" data-parent="#apicalls">
<div class="card-body">
<p> <p>
{t('thisApiCallEitherAddEmailsToBlacklist')} {t('thisApiCallEitherAddEmailsToBlacklist')}
</p> </p>
@ -294,9 +372,15 @@ export default class API extends Component {
<pre>curl -XPOST '{getUrl(`api/blacklist/add?access_token=${accessToken}`)}' \<br/> <pre>curl -XPOST '{getUrl(`api/blacklist/add?access_token=${accessToken}`)}' \<br/>
--data 'EMAIL=test@example.com'</pre> --data 'EMAIL=test@example.com'</pre>
</div>
<h4>POST /api/blacklist/delete {t('deleteEmailFromBlacklist')}</h4> </div>
</div>
<div class="card">
<div class="card-header">
<button type="button" class="btn btn-link" data-toggle="collapse" data-target="#moreblacklistdelete"><h4>POST /api/blacklist/delete {t('deleteEmailFromBlacklist')}</h4></button>
</div>
<div id="moreblacklistdelete" class="collapse" data-parent="#apicalls">
<div class="card-body">
<p> <p>
{t('thisApiCallEitherDeleteEmailsFrom')} {t('thisApiCallEitherDeleteEmailsFrom')}
</p> </p>
@ -321,9 +405,15 @@ export default class API extends Component {
<pre>curl -XPOST '{getUrl(`api/blacklist/delete?access_token=${accessToken}`)}' \<br/> <pre>curl -XPOST '{getUrl(`api/blacklist/delete?access_token=${accessToken}`)}' \<br/>
--data 'EMAIL=test@example.com'</pre> --data 'EMAIL=test@example.com'</pre>
</div>
<h4>GET /api/lists/:email {t('getTheListsAUserHasSubscribedTo')}</h4> </div>
</div>
<div class="card">
<div class="card-header">
<button type="button" class="btn btn-link" data-toggle="collapse" data-target="#morelistsemail"><h4>GET /api/lists/:email {t('getTheListsAUserHasSubscribedTo')}</h4></button>
</div>
<div id="morelistsemail" class="collapse" data-parent="#apicalls">
<div class="card-body">
<p> <p>
{t('retrieveTheListsThatTheUserWithEmailHas')} {t('retrieveTheListsThatTheUserWithEmailHas')}
</p> </p>
@ -340,10 +430,15 @@ export default class API extends Component {
</p> </p>
<pre>curl -XGET '{getUrl(`api/lists/test@example.com?access_token=${accessToken}`)}'</pre> <pre>curl -XGET '{getUrl(`api/lists/test@example.com?access_token=${accessToken}`)}'</pre>
</div>
</div>
<h4>GET /api/lists-by-namespace/:namespaceId {t('getListsInNamespace')}</h4> </div>
<div class="card">
<div class="card-header">
<button type="button" class="btn btn-link" data-toggle="collapse" data-target="#morelistsnamespace"><h4>GET /api/lists-by-namespace/:namespaceId {t('getListsInNamespace')}</h4></button>
</div>
<div id="morelistsnamespace" class="collapse" data-parent="#apicalls">
<div class="card-body">
<p> <p>
{t('retrieveTheListsThatTheNamespaceHas')} {t('retrieveTheListsThatTheNamespaceHas')}
</p> </p>
@ -360,10 +455,15 @@ export default class API extends Component {
</p> </p>
<pre>curl -XGET '{getUrl(`api/lists-by-namespace/1?access_token=${accessToken}`)}'</pre> <pre>curl -XGET '{getUrl(`api/lists-by-namespace/1?access_token=${accessToken}`)}'</pre>
</div>
</div>
<h4>POST /api/lists {t('createList')}</h4> </div>
<div class="card">
<div class="card-header">
<button type="button" class="btn btn-link" data-toggle="collapse" data-target="#morecreatelist"><h4>POST /api/list {t('createList')}</h4></button>
</div>
<div id="morecreatelist" class="collapse" data-parent="#apicalls">
<div class="card-body">
<p> <p>
{t('createListDescription')} {t('createListDescription')}
</p> </p>
@ -423,10 +523,19 @@ export default class API extends Component {
-d 'PUBLIC_SUBSCRIBE=1' \<br/> -d 'PUBLIC_SUBSCRIBE=1' \<br/>
-d 'LISTUNSUBSCRIBE_DISABLED=0' -d 'LISTUNSUBSCRIBE_DISABLED=0'
</pre> </pre>
<p>
{t('Response example')}:
<h4>DELETE /api/lists/:listId {t('deleteList')}</h4> </p>
<pre>"data": ("id":"WSGjaP1fY")</pre>
</div>
</div>
</div>
<div class="card">
<div class="card-header">
<button type="button" class="btn btn-link" data-toggle="collapse" data-target="#moredeletelist"><h4>DELETE /api/list/:listCId {t('deleteList')}</h4></button>
</div>
<div id="moredeletelist" class="collapse" data-parent="#apicalls">
<div class="card-body">
<p> <p>
{t('deleteListDescription')} {t('deleteListDescription')}
</p> </p>
@ -442,11 +551,20 @@ export default class API extends Component {
<strong>{t('example')}</strong> <strong>{t('example')}</strong>
</p> </p>
<pre>curl -XDELETE '{getUrl(`api/list/B16uVTdW?access_token=${accessToken}`)}'</pre> <pre>curl -XDELETE '{getUrl(`api/list/WSGjaP1fY?access_token=${accessToken}`)}'</pre>
<p>
{t('Response example')}:
<h4>GET /api/rss/fetch/:campaignCid {t('triggerFetchOfACampaign')}</h4> </p>
<pre>{t('Empty object')}</pre>
</div>
</div>
</div>
<div class="card">
<div class="card-header">
<button type="button" class="btn btn-link" data-toggle="collapse" data-target="#morerss"><h4>GET /api/rss/fetch/:campaignCid {t('triggerFetchOfACampaign')}</h4></button>
</div>
<div id="morerss" class="collapse" data-parent="#apicalls">
<div class="card-body">
<p> <p>
{t('forcesTheRssFeedCheckToImmediatelyCheck')} {t('forcesTheRssFeedCheckToImmediatelyCheck')}
</p> </p>
@ -463,9 +581,15 @@ export default class API extends Component {
</p> </p>
<pre>curl -XGET '{getUrl(`api/rss/fetch/5OOnZKrp0?access_token=${accessToken}`)}'</pre> <pre>curl -XGET '{getUrl(`api/rss/fetch/5OOnZKrp0?access_token=${accessToken}`)}'</pre>
</div>
<h4>POST /api/templates/:templateId/send {t('sendTransactionalEmail')}</h4> </div>
</div>
<div class="card">
<div class="card-header">
<button type="button" class="btn btn-link" data-toggle="collapse" data-target="#moretemplate"><h4>POST /api/templates/:templateId/send {t('sendTransactionalEmail')}</h4></button>
</div>
<div id="moretemplate" class="collapse" data-parent="#apicalls">
<div class="card-body">
<p> <p>
{t('sendSingleEmailByTemplateWithGiven')} {t('sendSingleEmailByTemplateWithGiven')}
</p> </p>
@ -494,6 +618,13 @@ export default class API extends Component {
<pre>curl -XPOST '{getUrl(`api/templates/1/send?access_token=${accessToken}`)}' \<br/> <pre>curl -XPOST '{getUrl(`api/templates/1/send?access_token=${accessToken}`)}' \<br/>
--data 'EMAIL=test@example.com&amp;SUBJECT=Test&amp;TAGS[FOO]=bar&amp;TAGS[TEST]=example'</pre> --data 'EMAIL=test@example.com&amp;SUBJECT=Test&amp;TAGS[FOO]=bar&amp;TAGS[TEST]=example'</pre>
</div>
</div>
</div>
</div>
</div> </div>
); );
} }

View file

@ -866,13 +866,14 @@ async function getListsWithEmail(context, email) {
// FIXME - this methods is rather suboptimal if there are many lists. It quite needs permission caching in shares.js // FIXME - this methods is rather suboptimal if there are many lists. It quite needs permission caching in shares.js
return await knex.transaction(async tx => { return await knex.transaction(async tx => {
const lsts = await tx('lists').select(['id', 'cid', 'name']); const lsts = await tx('lists').select(['id', 'cid', 'name', 'description']);
const result = []; const result = [];
for (const list of lsts) { for (const list of lsts) {
await shares.enforceEntityPermissionTx(tx, context, 'list', list.id, 'viewSubscriptions'); await shares.enforceEntityPermissionTx(tx, context, 'list', list.id, 'viewSubscriptions');
const entity = await tx(getSubscriptionTableName(list.id)).where('hash_email', hashEmail(email)).whereNotNull('email').first(); const entity = await tx(getSubscriptionTableName(list.id)).where('hash_email', hashEmail(email)).whereNotNull('email').first();
if (entity) { if (entity) {
list.status=entity.status;
result.push(list); result.push(list);
} }
} }

View file

@ -110,7 +110,7 @@ router.postAsync('/unsubscribe/:listCid', passport.loggedIn, async (req, res) =>
res.status(200); res.status(200);
res.json({ res.json({
data: { data: {
id: subscription.id, id: subscription.cid,
unsubscribed: true unsubscribed: true
} }
}); });
@ -133,7 +133,7 @@ router.postAsync('/delete/:listCid', passport.loggedIn, async (req, res) => {
res.status(200); res.status(200);
res.json({ res.json({
data: { data: {
id: subscription.id, id: subscription.cid,
deleted: true deleted: true
} }
}); });
@ -227,7 +227,7 @@ router.postAsync('/field/:listCid', passport.loggedIn, async (req, res) => {
input[(key || '').toString().trim().toUpperCase()] = (req.body[key] || '').toString().trim(); input[(key || '').toString().trim().toUpperCase()] = (req.body[key] || '').toString().trim();
}); });
const key = (input.NAME || '').toString().trim() || slugify('merge ' + name, '_').toUpperCase(); const key = slugify('merge ' + input.NAME, '_').toUpperCase();
const visible = ['false', 'no', '0', ''].indexOf((input.VISIBLE || '').toString().toLowerCase().trim()) < 0; const visible = ['false', 'no', '0', ''].indexOf((input.VISIBLE || '').toString().toLowerCase().trim()) < 0;
const groupTemplate = (input.GROUP_TEMPLATE || '').toString().toLowerCase().trim(); const groupTemplate = (input.GROUP_TEMPLATE || '').toString().toLowerCase().trim();