This is an automated email from the ASF dual-hosted git repository.

lahirujayathilake pushed a commit to branch access-integration-v3
in repository https://gitbox.apache.org/repos/asf/airavata-custos.git

commit dbf2d3ff1de3f078f177e6e5dd6a81ca5ed8ffc5
Author: lahiruj <[email protected]>
AuthorDate: Thu May 21 02:12:35 2026 -0400

    Use Keep/DeleteGlobalID for request_person_merge and baseline amie test 
packets
---
 .../AMIE-Processor/handler/request_person_merge.go |  40 ++++----
 .../AMIE-Processor/mock-server/mock-amie-server.py | 114 ++++++++++++++++++++-
 .../request_person_merge/incoming-request.json     |   6 +-
 3 files changed, 134 insertions(+), 26 deletions(-)

diff --git a/connectors/ACCESS/AMIE-Processor/handler/request_person_merge.go 
b/connectors/ACCESS/AMIE-Processor/handler/request_person_merge.go
index 8ddd43d2c..1248bbb96 100644
--- a/connectors/ACCESS/AMIE-Processor/handler/request_person_merge.go
+++ b/connectors/ACCESS/AMIE-Processor/handler/request_person_merge.go
@@ -24,7 +24,6 @@ import (
        "log/slog"
 
        
"github.com/apache/airavata-custos/connectors/ACCESS/AMIE-Processor/model"
-       "github.com/apache/airavata-custos/pkg/models"
        "github.com/apache/airavata-custos/pkg/service"
 )
 
@@ -45,44 +44,49 @@ func (h *RequestPersonMergeHandler) Handle(ctx 
context.Context, tx *sql.Tx, pack
        if err != nil {
                return err
        }
-       primaryGlobalID := getString(body, "PrimaryGlobalID")
-       if err := requireText(primaryGlobalID, "PrimaryGlobalID"); err != nil {
+       keepGlobalID := getString(body, "KeepGlobalID")
+       if err := requireText(keepGlobalID, "KeepGlobalID"); err != nil {
                return err
        }
-       secondaryGlobalID := getString(body, "SecondaryGlobalID")
-       if err := requireText(secondaryGlobalID, "SecondaryGlobalID"); err != 
nil {
+       deleteGlobalID := getString(body, "DeleteGlobalID")
+       if err := requireText(deleteGlobalID, "DeleteGlobalID"); err != nil {
                return err
        }
-       if primaryGlobalID == secondaryGlobalID {
-               return fmt.Errorf("request_person_merge: primary and secondary 
global IDs are identical")
+       if keepGlobalID == deleteGlobalID {
+               return fmt.Errorf("request_person_merge: keep and delete global 
IDs are identical")
        }
 
-       survivor, err := h.svc.GetUserByExternalIdentity(ctx, 
amieIdentitySource, primaryGlobalID)
+       survivor, err := h.svc.GetUserByExternalIdentity(ctx, 
amieIdentitySource, keepGlobalID)
        if err != nil {
                return fmt.Errorf("request_person_merge: resolve surviving 
user: %w", err)
        }
-       retiring, err := h.svc.GetUserByExternalIdentity(ctx, 
amieIdentitySource, secondaryGlobalID)
+       retiring, err := h.svc.GetUserByExternalIdentity(ctx, 
amieIdentitySource, deleteGlobalID)
        if err != nil {
                return fmt.Errorf("request_person_merge: resolve retiring user: 
%w", err)
        }
 
-       // Refuse to merge while the retiring user still has active memberships:
-       // AMIE is expected to inactivate the user's accounts first, and merging
-       // over active state would silently transfer ownership of live 
resources.
        memberships, err := h.svc.ListAllocationsForUser(ctx, retiring.ID)
        if err != nil {
                return fmt.Errorf("request_person_merge: list memberships: %w", 
err)
        }
+       activeMemberships := make([]string, 0, len(memberships))
        for _, m := range memberships {
-               if m.MembershipStatus == models.ACTIVE {
-                       return fmt.Errorf("request_person_merge: retiring user 
%s has active membership %s; inactivate first",
-                               retiring.ID, m.ID)
+               if m.MembershipStatus == "ACTIVE" {
+                       activeMemberships = append(activeMemberships, m.ID)
                }
        }
+       if len(activeMemberships) > 0 {
+               slog.WarnContext(ctx, "merging user with active memberships",
+                       "keep_global_id", keepGlobalID,
+                       "delete_global_id", deleteGlobalID,
+                       "retiring_id", retiring.ID,
+                       "active_membership_ids", activeMemberships,
+               )
+       }
 
-       slog.WarnContext(ctx, "executing user merge",
-               "primary_global_id", primaryGlobalID,
-               "secondary_global_id", secondaryGlobalID,
+       slog.InfoContext(ctx, "executing user merge",
+               "keep_global_id", keepGlobalID,
+               "delete_global_id", deleteGlobalID,
                "survivor_id", survivor.ID,
                "retiring_id", retiring.ID,
        )
diff --git a/connectors/ACCESS/AMIE-Processor/mock-server/mock-amie-server.py 
b/connectors/ACCESS/AMIE-Processor/mock-server/mock-amie-server.py
index 8b331ddc9..c3e495c08 100644
--- a/connectors/ACCESS/AMIE-Processor/mock-server/mock-amie-server.py
+++ b/connectors/ACCESS/AMIE-Processor/mock-server/mock-amie-server.py
@@ -195,10 +195,10 @@ def gen_valid_person_merge():
     primary = str(random.randint(100000, 999999))
     secondary = str(random.randint(100000, 999999))
     return make_packet("request_person_merge", {
+        "KeepGlobalID": primary,
         "KeepPersonID": f"person-keep-{uuid.uuid4().hex[:8]}",
+        "DeleteGlobalID": secondary,
         "DeletePersonID": f"person-delete-{uuid.uuid4().hex[:8]}",
-        "PrimaryGlobalID": primary,
-        "SecondaryGlobalID": secondary,
         "MergeReason": "Duplicate person records",
     })
 
@@ -426,16 +426,120 @@ def generate_all_handlers_once():
         gen_valid_account_inactivate(),
         gen_valid_account_reactivate(),
         make_packet("request_person_merge", {
+            "KeepGlobalID": primary_gid,
             "KeepPersonID": f"person-keep-{uuid.uuid4().hex[:8]}",
+            "DeleteGlobalID": secondary_gid,
             "DeletePersonID": f"person-delete-{uuid.uuid4().hex[:8]}",
-            "PrimaryGlobalID": primary_gid,
-            "SecondaryGlobalID": secondary_gid,
             "MergeReason": "all_handlers test scenario",
         }),
         gen_inform_transaction_complete(),
     ]
 
 
+# Deterministic baseline scenario. Skips handlers that need a Custos UUID
+# (request_account_create, the inactivate/reactivate handlers).
+
+def gen_baseline_scenario():
+    return [
+        make_packet("request_project_create", {
+            "GrantNumber": "BL-001",
+            "PfosNumber": "PFOS-BL-001",
+            "ProjectTitle": "Baseline Project 1",
+            "PiGlobalID": "bl-pi-001",
+            "PiFirstName": "Pat",
+            "PiLastName": "First",
+            "PiEmail": "[email protected]",
+            "PiOrganization": "Baseline Org",
+            "PiOrgCode": "BASELINE",
+            "NsfStatusCode": "AC",
+            "PiDnList": ["/C=US/O=Baseline Org/CN=Pat First"],
+            "ServiceUnitsAllocated": "10000",
+            "StartDate": "2026-01-01",
+            "EndDate": "2026-12-31",
+            "ResourceList": ["baseline-cluster.example.edu"],
+            "AllocationType": "new",
+        }),
+        make_packet("request_project_create", {
+            "GrantNumber": "BL-002",
+            "PfosNumber": "PFOS-BL-002",
+            "ProjectTitle": "Baseline Project 2",
+            "PiGlobalID": "bl-pi-002",
+            "PiFirstName": "Sam",
+            "PiLastName": "Second",
+            "PiEmail": "[email protected]",
+            "PiOrganization": "Baseline Org",
+            "PiOrgCode": "BASELINE",
+            "NsfStatusCode": "AC",
+            "PiDnList": [],
+            "ServiceUnitsAllocated": "20000",
+            "StartDate": "2026-01-01",
+            "EndDate": "2026-12-31",
+            "ResourceList": ["baseline-cluster.example.edu"],
+            "AllocationType": "new",
+        }),
+        # Re-delivery of BL-001 as a supplement; writes a 
compute_allocation_diffs row.
+        make_packet("request_project_create", {
+            "GrantNumber": "BL-001",
+            "PfosNumber": "PFOS-BL-001",
+            "ProjectTitle": "Baseline Project 1",
+            "PiGlobalID": "bl-pi-001",
+            "PiFirstName": "Pat",
+            "PiLastName": "First",
+            "PiEmail": "[email protected]",
+            "PiOrganization": "Baseline Org",
+            "PiOrgCode": "BASELINE",
+            "NsfStatusCode": "AC",
+            "ServiceUnitsAllocated": "5000",
+            "StartDate": "2026-01-01",
+            "EndDate": "2026-12-31",
+            "ResourceList": ["baseline-cluster.example.edu"],
+            "AllocationType": "supplement",
+        }),
+        make_packet("data_project_create", {
+            "ProjectID": "BL-001",
+            "PersonID": "bl-pi-001-person",
+            "GlobalID": "bl-pi-001",
+            "DnList": [
+                "/C=US/O=Baseline Org/CN=Pat First Extra",
+                "/DC=EDU/CN=patfirst",
+            ],
+        }),
+        make_packet("data_account_create", {
+            "ProjectID": "BL-002",
+            "PersonID": "bl-pi-002-person",
+            "GlobalID": "bl-pi-002",
+            "DnList": [
+                "/C=US/O=Baseline Org/CN=Sam Second",
+            ],
+        }),
+        make_packet("request_user_modify", {
+            "ActionType": "replace",
+            "ProjectID": "BL-001",
+            "PersonID": "bl-pi-001-person",
+            "UserPersonID": "bl-pi-001-user",
+            "UserGlobalID": "bl-pi-001",
+            "UserFirstName": "Pat",
+            "UserLastName": "First-Updated",
+            "UserEmail": "[email protected]",
+            "UserOrganization": "Baseline Org",
+            "UserOrgCode": "BASELINE",
+            "NsfStatusCode": "AC",
+        }),
+        make_packet("request_person_merge", {
+            "KeepGlobalID": "bl-pi-001",
+            "KeepPersonID": "bl-keep-person",
+            "DeleteGlobalID": "bl-pi-002",
+            "DeletePersonID": "bl-delete-person",
+            "MergeReason": "Duplicate person records",
+        }),
+        make_packet("inform_transaction_complete", {
+            "StatusCode": "Success",
+            "Message": "Baseline complete",
+            "DetailCode": "1",
+        }),
+    ]
+
+
 # ----- API endpoints -----
 
 @app.route("/packets/<site>", methods=["GET"])
@@ -482,6 +586,8 @@ def create_scenario(site):
         packets = generate_all_handlers_once()
     elif scenario_type == "dev_email":
         packets = gen_dev_email_scenario()
+    elif scenario_type == "baseline":
+        packets = gen_baseline_scenario()
     else:
         packets = generate_batch(success_count=3, failure_count=2)
     pending_packets.extend(packets)
diff --git 
a/connectors/ACCESS/AMIE-Processor/testdata/request_person_merge/incoming-request.json
 
b/connectors/ACCESS/AMIE-Processor/testdata/request_person_merge/incoming-request.json
index 5ffdfb697..0ef136352 100644
--- 
a/connectors/ACCESS/AMIE-Processor/testdata/request_person_merge/incoming-request.json
+++ 
b/connectors/ACCESS/AMIE-Processor/testdata/request_person_merge/incoming-request.json
@@ -23,12 +23,10 @@
     "transaction_state": "in-progress"
   },
   "body": {
+    "KeepGlobalID": "12345",
     "KeepPersonID": "test-person-primary-123",
+    "DeleteGlobalID": "67890",
     "DeletePersonID": "test-person-secondary-456",
-    "PrimaryGlobalID": "12345",
-    "SecondaryGlobalID": "67890",
-    "PrimarySitePersonID": "primary-user",
-    "SecondarySitePersonID": "secondary-user",
     "MergeReason": "Duplicate person records",
     "Comment": "Person merge for testing"
   }

Reply via email to