Skip to content

Commit

Permalink
Merge pull request #267 from cofacts/block-user-script
Browse files Browse the repository at this point in the history
Script that sets blockedReason and blocks reply rquests
  • Loading branch information
MrOrz committed Nov 24, 2021
2 parents 31c8d38 + b2cb9e2 commit 4719a8a
Show file tree
Hide file tree
Showing 4 changed files with 300 additions and 0 deletions.
9 changes: 9 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,15 @@ $ node build/scripts/removeArticleReply.js --userId=<userId> --articleId=<articl

- For more options, run the above script with `--help` or see the file level comments.

### Block a user
- Please announce that the user will be blocked openly with a URL first.
- To block a user, execute the following:
```
$ node build/scripts/blockUser.js --userId=<userId> --blockedReason=<Announcement URL>
```

- For more options, run the above script with `--help` or see the file level comments.


## One-off migration scripts

Expand Down
41 changes: 41 additions & 0 deletions src/scripts/__fixtures__/blockUser.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
export default {
'/users/doc/user-to-block': {
name: 'Naughty spammer',
},

'/users/doc/already-blocked': {
name: 'Blocked spammer',
blockedReason: 'Some announcement',
},

'/replyrequests/doc/already-blocked': {
articleId: 'some-article',
userId: 'user-to-block',
status: 'BLOCKED',
createdAt: '2021-11-11T00:00:00.000Z',
},

'/articles/doc/some-article': {
replyRequestCount: 1,
lastRequestedAt: '2021-01-01T00:00.000Z',
},

'/replyrequests/doc/replyrequest-to-block': {
articleId: 'modified-article',
userId: 'user-to-block',
status: 'NORMAL',
createdAt: '2021-11-11T11:00:00.000Z',
},

'/replyrequests/doc/valid-reply-request': {
articleId: 'modified-article',
userId: 'valid-user',
status: 'NORMAL',
createdAt: '2021-10-10T00:00:00.000Z',
},

'/articles/doc/modified-article': {
replyRequestCount: 2,
lastRequestedAt: '2021-01-01T00:00:01.000Z',
},
};
81 changes: 81 additions & 0 deletions src/scripts/__tests__/blockUser.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
import { loadFixtures, unloadFixtures } from 'util/fixtures';
import client from 'util/client';
import blockUser from '../blockUser';
import fixtures from '../__fixtures__/blockUser';

beforeEach(() => loadFixtures(fixtures));
afterEach(() => unloadFixtures(fixtures));

it('fails if userId is not valid', async () => {
expect(
blockUser({ userId: 'not-exist', blockedReason: 'announcement url' })
).rejects.toMatchInlineSnapshot(
`[Error: User with ID=not-exist does not exist]`
);
});

it('correctly sets the block reason and updates status of their works', async () => {
await blockUser({
userId: 'user-to-block',
blockedReason: 'announcement url',
});

const {
body: { _source: blockedUser },
} = await client.get({
index: 'users',
type: 'doc',
id: 'user-to-block',
});

// Assert that blockedReason is written on the user
expect(blockedUser).toMatchInlineSnapshot(`
Object {
"blockedReason": "announcement url",
"name": "Naughty spammer",
}
`);

//
// Check reply requests
//

// Assert reply requests that is already blocked are not selected to update
//
const {
body: { _source: someArticleWithAlreadyBlockedReplyRequest },
} = await client.get({
index: 'articles',
type: 'doc',
id: 'some-article',
});
expect(someArticleWithAlreadyBlockedReplyRequest).toMatchObject(
fixtures['/articles/doc/some-article']
);

// Assert normal reply requests being blocked and article being updated
//
const {
body: { _source: replyRequestToBlock },
} = await client.get({
index: 'replyrequests',
type: 'doc',
id: 'replyrequest-to-block',
});
expect(replyRequestToBlock.status).toEqual('BLOCKED');
const {
body: { _source: modifiedArticle },
} = await client.get({
index: 'articles',
type: 'doc',
id: 'modified-article',
});
expect(modifiedArticle).toMatchObject({
// Only replyrequests/doc/valid-reply-request
replyRequestCount: 1,
lastRequestedAt:
fixtures['/replyrequests/doc/valid-reply-request'].createdAt,
});
});

// it('still updates statuses of blocked user even if they are blocked previously')
169 changes: 169 additions & 0 deletions src/scripts/blockUser.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,169 @@
/**
* Given userId & block reason, blocks the user and marks all their existing ArticleReply,
* ArticleReplyFeedback, ReplyRequest, ArticleCategory, ArticleCategoryFeedback as BLOCKED.
*/

import 'dotenv/config';
import yargs from 'yargs';
import client from 'util/client';
import getAllDocs from 'util/getAllDocs';

/**
* Update user to write blockedReason. Throws if user does not exist.
*
* @param {string} userId
* @param {string} blockedReason
*/
async function writeBlockedReasonToUser(userId, blockedReason) {
try {
const {
body: { result: setBlockedReasonResult },
} = await client.update({
index: 'users',
type: 'doc',
id: userId,
body: {
doc: { blockedReason },
},
});

/* istanbul ignore if */
if (setBlockedReasonResult === 'noop') {
console.log(
`Info: user ID ${userId} already has set the same blocked reason.`
);
}
} catch (e) {
/* istanbul ignore else */
if (e.message === 'document_missing_exception') {
throw new Error(`User with ID=${userId} does not exist`);
}

throw e;
}
}

/**
* Convert all reply requests with NORMAL status by the user to BLOCKED status.
*
* @param {userId} userId
*/
async function processReplyRequests(userId) {
const NORMAL_REPLY_REQUEST_QUERY = {
bool: {
must: [{ term: { status: 'NORMAL' } }, { term: { userId } }],
},
};

const articleIdsWithNormalReplyRequests = [];

for await (const {
_source: { articleId },
} of getAllDocs('replyrequests', NORMAL_REPLY_REQUEST_QUERY)) {
articleIdsWithNormalReplyRequests.push(articleId);
}

/* Bulk update reply reqeuests status */
const { body: updateByQueryResult } = await client.updateByQuery({
index: 'replyrequests',
type: 'doc',
body: {
query: NORMAL_REPLY_REQUEST_QUERY,
script: {
lang: 'painless',
source: `ctx._source.status = 'BLOCKED';`,
},
},
refresh: true,
});

console.log('Reply request status update result', updateByQueryResult);

/* Bulk update articles' replyRequestCount & lastRequestedAt */
console.log(
`Updating ${articleIdsWithNormalReplyRequests.length} articles...`
);

for (let i = 0; i < articleIdsWithNormalReplyRequests.length; i += 1) {
const articleId = articleIdsWithNormalReplyRequests[i];

const {
body: {
hits: { total },
aggregations: {
lastRequestedAt: { value_as_string: lastRequestedAt },
},
},
} = await client.search({
index: 'replyrequests',
size: 0,
body: {
query: {
bool: {
must: [{ term: { status: 'NORMAL' } }, { term: { articleId } }],
},
},
aggs: {
lastRequestedAt: { max: { field: 'createdAt' } },
},
},
});

await client.update({
index: 'articles',
type: 'doc',
id: articleId,
body: {
doc: {
lastRequestedAt,
replyRequestCount: total,
},
},
});

console.log(
`[${i + 1}/${
articleIdsWithNormalReplyRequests.length
}] article ${articleId}: changed to ${total} reply requests, last requested at ${lastRequestedAt}`
);
}
}

/**
* Convert all article replies with NORMAL status by the user to BLOCKED status.
*
* @param {userId} userId
*/
// async function processArticleReplies(/* userId */) {}

/**
* @param {object} args
*/
async function main({ userId, blockedReason } = {}) {
await writeBlockedReasonToUser(userId, blockedReason);
await processReplyRequests(userId);
// await processArticleReplies(userId);
}

export default main;

if (require.main === module) {
const argv = yargs
.options({
userId: {
alias: 'u',
description: 'The user ID to block',
type: 'string',
demandOption: true,
},
blockedReason: {
alias: 'r',
description: 'The URL to the annoucement that blocks this user',
type: 'string',
demandOption: true,
},
})
.help('help').argv;

main(argv).catch(console.error);
}

0 comments on commit 4719a8a

Please sign in to comment.