Files
redmine/docs/redmine_contacts_duplicate_merge_feasibility.md
T
Jason Thistlethwaite 9ee9eb3ec6 Organized
2026-10-17 20:50:35 -04:00

3.2 KiB

Redmine Contacts duplicate-merge feasibility (API vs script)

Scope

Assess whether duplicate contact merge (web flow: /contacts/<contact_id>/duplicates?project_id=<project_slug> with duplicate_id=<target_id>) can be safely automated from redmine_contacts.py or shell tooling.

Findings from local plugin source

1) Merge flow exists as a controller action, not a JSON API endpoint

  • Controller: plugins/redmine_contacts/app/controllers/contacts_duplicates_controller.rb
  • Routes: plugins/redmine_contacts/config/routes.rb
    • match 'contacts/:contact_id/duplicates' => 'contacts_duplicates#index', :via => [:get, :post]
    • match 'contacts_duplicates/:action' => 'contacts_duplicates', :via => [:get, :post]

ContactsDuplicatesController does not declare accept_api_auth and does not implement format.api responses.

2) Merge behavior is server-side and destructive

Inside merge action:

  • combines notes, deals, related_deals, issues, projects from source contact into duplicate target,
  • unions email/phone/tag sets,
  • saves target,
  • destroys source contact.

This matches the intended business behavior (source contact removed, linked records moved).

3) Web form posts to merge action with duplicate_id

  • View: plugins/redmine_contacts/app/views/contacts_duplicates/index.html.erb
  • Uses Rails form_tag to contacts_duplicates#merge with project_id, contact_id, and posted duplicate_id.

Practical feasibility

A) Native Redmine REST API with API key

Not currently feasible for this merge operation.

Reason: no API auth route/serializer for duplicate merge in this plugin path.

B) Scripted browser-equivalent flow (curl/requests with session cookies)

Feasible with caution.

Required mechanics:

  1. Authenticate as a normal Redmine user (session/cookie auth).
  2. GET duplicate page for the source contact to obtain valid session + CSRF authenticity token.
  3. POST to merge action with:
    • project_id
    • contact_id (source)
    • duplicate_id (target)
    • authenticity token
  4. Verify redirect/result contact and audit that source contact no longer exists.

Risk notes

  • This is a destructive operation (deletes source contact).
  • Needs stronger guardrails than normal update operations:
    • explicit --apply style confirmation,
    • dry-run mode that only validates both contacts and project scope,
    • optional --test-instance-only safety default,
    • write operation log with source/target IDs and timestamp.

Recommendation for redmine_contacts.py

  1. Do not add duplicate merge via API-key-only path unless plugin gains a dedicated JSON API action.
  2. If we implement now, add a separate command using session-auth web flow (not mixed into normal contact update path), e.g.:
    • merge-duplicate --source-contact <id> --target-contact <id> --project <slug> [--apply]
  3. Keep it disabled-by-default for production base URL, with explicit override flag.

Suggested next technical step

Prototype a standalone script against http://192.168.50.170 that:

  • logs in,
  • resolves CSRF token,
  • performs merge POST,
  • verifies resulting contact IDs/issue linkage,
  • emits structured JSON result for later CLI integration.