feat(workflows): add "attribute action" action

This action lets users perform several actions on threads, such as
(un)lock, (un)set as trending, (un)pin, set as non-issue, obsolete, etc.

The action selector shows the action with the codename it has in the
Protobuf enum. We will show a friendly string when we localize the
feature.

Bug: twpowertools:74

Change-Id: I95f9f1904ffe559c92a786cbdb327613c8ca32ca
diff --git a/src/contentScripts/communityConsole/workflows/actionRunners/attribute.js b/src/contentScripts/communityConsole/workflows/actionRunners/attribute.js
new file mode 100644
index 0000000..bec2511
--- /dev/null
+++ b/src/contentScripts/communityConsole/workflows/actionRunners/attribute.js
@@ -0,0 +1,20 @@
+import {CCApi} from '../../../../common/api.js';
+import {getAuthUser} from '../../../../common/communityConsoleUtils.js';
+
+export default class AttributeRunner {
+  async execute(attributeAction, thread) {
+    if (!attributeAction) {
+      throw new Error(
+          'The workflow is malformed. The attribute action is missing.');
+    }
+    const action = attributeAction.getAttributeAction();
+
+    return await CCApi(
+        'SetThreadAttribute', {
+          1: thread.forumId,
+          2: thread.threadId,
+          3: action,
+        },
+        /* authenticated = */ true, getAuthUser());
+  }
+}
diff --git a/src/contentScripts/communityConsole/workflows/runner.js b/src/contentScripts/communityConsole/workflows/runner.js
index 35c291c..164d226 100644
--- a/src/contentScripts/communityConsole/workflows/runner.js
+++ b/src/contentScripts/communityConsole/workflows/runner.js
@@ -1,8 +1,9 @@
-import {parseUrl, recursiveParentElement} from '../../../common/commonUtils.js';
+import {recursiveParentElement} from '../../../common/commonUtils.js';
 import * as pb from '../../../workflows/proto/main_pb.js';
 
-import CRRunner from './actionRunners/replyWithCR.js';
+import AttributeRunner from './actionRunners/attribute.js';
 import ReadStateRunner from './actionRunners/readState.js';
+import CRRunner from './actionRunners/replyWithCR.js';
 import Thread from './models/thread.js';
 
 export default class WorkflowRunner {
@@ -16,6 +17,7 @@
     this._updateCallback = updateCallback;
 
     // Initialize action runners:
+    this._AttributeRunner = new AttributeRunner();
     this._CRRunner = new CRRunner();
     this._ReadStateRunner = new ReadStateRunner();
   }
@@ -55,6 +57,10 @@
 
   _runAction() {
     switch (this._currentAction?.getActionCase?.()) {
+      case pb.workflows.Action.ActionCase.ATTRIBUTE_ACTION:
+        return this._AttributeRunner.execute(
+            this._currentAction?.getAttributeAction?.(), this._currentThread);
+
       case pb.workflows.Action.ActionCase.REPLY_WITH_CR_ACTION:
         return this._CRRunner.execute(
             this._currentAction?.getReplyWithCrAction?.(), this._currentThread);