Repository: dmwm/wmcore
Issue: [MS] update to the pileup active state should trigger a timestamp update

**Impact of the bug**
MSPileup

**Describe the bug**
As described in the requirements of this issue: https://github.com/dmwm/WMCore/issues/11424

whenever a user changes the `active` state of a pileup document, it should automatically trigger its timestamp as well (activatedOn or deactivatedOn).

**How to reproduce it**
Set a pileup configuration `active` attribute to `false` and `true` and check the attributes: activatedOn and deactivatedOn

**Expected behavior**
Detect when client is changing the `active` status of a given pileup and also update the relevant timestamp attribute, according to the following rule:
* if user provides `active=true`, then MSPileup needs to also update `activatedOn` to the current GMT timestamp
* if user provides `active=false`, then MSPileup needs to also update `deactivatedOn` to the current GMT timestamp

**Additional context and error message**
We could check whether there is a real change of the `active` state in the document. But I think we can simply assume that the user already did that, if he did not, this will renew the timestamp value.

Changed files:
src/python/WMCore/MicroService/MSPileup/MSPileupData.py, test/python/WMCore_t/MicroService_t/MSPileup_t/MSPileupData_t.py

diff --git a/src/python/WMCore/MicroService/MSPileup/MSPileupData.py b/src/python/WMCore/MicroService/MSPileup/MSPileupData.py
index a491437ec..f69bf6d06 100644
--- a/src/python/WMCore/MicroService/MSPileup/MSPileupData.py
+++ b/src/python/WMCore/MicroService/MSPileup/MSPileupData.py
@@ -45,6 +45,21 @@ def stripKeys(docs, skeys=None):
     return docs
 
 
+def getNewTimestamp(doc):
+    """
+    Given a pileup doc - or a subset of it - return a dictionary
+    with a couple timestamp attributes that need to be updated.
+    :param doc: a python dictionary representing the pileup information
+    :return: a python dictionary to update the pileup object
+    """
+    subDoc = {'lastUpdateTime': gmtimeSeconds()}
+    if "active" in doc and doc['active'] is True:
+        subDoc['activatedOn'] = gmtimeSeconds()
+    elif "active" in doc and doc['active'] is False:
+        subDoc['deactivatedOn'] = gmtimeSeconds()
+    return subDoc
+
+
 class MSPileupData():
     """
     MSPileupData provides logic behind data used and stored by MSPileup module
@@ -164,7 +179,8 @@ class MSPileupData():
                 self.logger.error(err)
                 return [err.error()]
 
-        doc['lastUpdateTime'] = gmtimeSeconds()
+        # mandatory timestamp updates
+        doc.update(getNewTimestamp(doc))
         # we do not need to create MSPileupObj and validate it since our doc comes directly from DB
         try:
             self.dbColl.update_one(spec, {"$set": doc})
diff --git a/test/python/WMCore_t/MicroService_t/MSPileup_t/MSPileupData_t.py b/test/python/WMCore_t/MicroService_t/MSPileup_t/MSPileupData_t.py
index 7a9c6c30e..187c245e9 100644
--- a/test/python/WMCore_t/MicroService_t/MSPileup_t/MSPileupData_t.py
+++ b/test/python/WMCore_t/MicroService_t/MSPileup_t/MSPileupData_t.py
@@ -9,9 +9,9 @@ import time
 import unittest
 
 # WMCore modules
-from WMCore.MicroService.MSPileup.MSPileupData import MSPileupData, stripKeys
+from WMCore.MicroService.MSPileup.MSPileupData import MSPileupData, stripKeys, getNewTimestamp
 from WMCore.MicroService.MSPileup.MSPileupError import MSPILEUP_SCHEMA_ERROR
-from Utils.Timers import encodeTimestamp, decodeTimestamp
+from Utils.Timers import encodeTimestamp, decodeTimestamp, gmtimeSeconds
 
 
 class MSPileupTest(unittest.TestCase):
@@ -150,6 +150,29 @@ class MSPileupTest(unittest.TestCase):
         res = self.mgr.getPileup(spec)
         self.assertEqual(len(res), 0)
 
+    def testGetNewTimestamp(self):
+        """Test the getNewTimestamp function"""
+        timeNow = gmtimeSeconds()
+        resp = getNewTimestamp({})
+        self.assertEqual(len(resp), 1)
+        self.assertTrue(resp['lastUpdateTime'] >= timeNow)
+
+        resp = getNewTimestamp({'lastUpdateTime': 1})
+        self.assertEqual(len(resp), 1)
+        self.assertTrue(resp['lastUpdateTime'] >= timeNow)
+
+        resp = getNewTimestamp({'active': True})
+        self.assertEqual(len(resp), 2)
+        self.assertTrue(resp['lastUpdateTime'] >= timeNow)
+        self.assertTrue(resp['activatedOn'] >= timeNow)
+        self.assertFalse('deactivatedOn' in resp)
+
+        resp = getNewTimestamp({'active': False})
+        self.assertEqual(len(resp), 2)
+        self.assertTrue(resp['lastUpdateTime'] >= timeNow)
+        self.assertTrue(resp['deactivatedOn'] >= timeNow)
+        self.assertFalse('activatedOn' in resp)
+
 
 if __name__ == '__main__':
     unittest.main()