ICAT update entities

4 views
Skip to first unread message

Marjolaine Bodin

unread,
Jun 17, 2026, 11:31:26 AM (6 days ago) Jun 17
to icatgroup

Dear all,

I have a question regarding the update of entities in ICAT via the REST entityManager endpoint.

I'm trying to update the title of an Investigation using a user with CRU flags on the Investigation table, and I'm running into two issues depending on what I send.

Case 1: partial payload

-> 412 error, OBJECT_ALREADY_EXISTS

I guess there is a conflict with the unique constraint on (FACILITY_ID, NAME, VISIT_ID).


Case 2: full payload

-> 403 error INSUFFICIENT_PRIVILEGES

ICAT  treats the update as a delete+recreate of the relationship, which requires DELETE permission. Is it expected?


Case 3: Updating it via soap works.


See attached a script , and the results:

=== Test 1: without facility => 412 OBJECT_ALREADY_EXISTS ===
EXPECTED FAILURE: 412 - {"code":"OBJECT_ALREADY_EXISTS","message":"Investigation exists with facility = 'Facility:305', name = 'ID002606', visitId = 'ID00'","offset":0}

=== Test 2: with facility => 403 INSUFFICIENT_PRIVILEGES ===
EXPECTED FAILURE: 403 - {"code":"INSUFFICIENT_PRIVILEGES","message":"DELETE access implied by UPDATE to this Investigation is not allowed.","offset":0}

=== Test 3: SOAP update => succeeds ===
SUCCESS: SOAP update worked


Am I doing something wrong? Is this a known limitation of the REST entityManager?

Thanks a lot for your help,

All the best,

  Marjolaine


icat_update.py

Patrick Austin - STFC UKRI

unread,
Jun 17, 2026, 12:07:45 PM (6 days ago) Jun 17
to Marjolaine Bodin, icatgroup
Hi Marjolaine,

Unfortunately I cannot read the attachment as due to the extension .py, it gets blocked.

From having briefly looked at the code, it looks like there are the following caveats:

If I had to speculate, I'd say you might be hitting these in each of your first two cases respectively? And that to be successful you should include the Investigation id in the body, but not any related entities like Facility.

This is all just based on the icat.server code, so I have not tried this myself and don't have any first hand experience with the endpoint. If you send your Python in the email body rather than as an attachment that might help diagnose the problem.

Thanks,

Patrick


From: icat...@googlegroups.com <icat...@googlegroups.com> on behalf of Marjolaine Bodin <esrf.marjo...@gmail.com>
Sent: 17 June 2026 16:31
To: icatgroup <icat...@googlegroups.com>
Subject: [icatgroup:2149] ICAT update entities
 
--
You received this message because you are subscribed to the Google Groups "icatgroup" group.
To unsubscribe from this group and stop receiving emails from it, send an email to icatgroup+...@googlegroups.com.
To view this discussion visit https://groups.google.com/d/msgid/icatgroup/CAHTuXn5cSt91TG9w7QNhKcDAAy%3DSws%2BG7ske7qgDky8_knpDuQ%40mail.gmail.com.

Marjolaine Bodin

unread,
Jun 17, 2026, 12:09:58 PM (6 days ago) Jun 17
to Patrick Austin - STFC UKRI, icatgroup
Hi Patrick, of course sorry.

here is the example:

import requests

ICAT_URL = "http://icat-test:8080/icat"
ICAT_SOAP_URL = "http://icat-test:8080/ICATService/ICAT"
SESSION_ID = "valid_session_id"   # can update investigation entity
INVESTIGATION_ID = 130847627


def test_update_partial():
    print("=== Test 1: without facility => 412 OBJECT_ALREADY_EXISTS ===")
    import json
    payload = {
        "sessionId": SESSION_ID,
        "entities": json.dumps([{
            "Investigation": {
                "id": INVESTIGATION_ID,
                "title": "test",
            }
        }])
    }
    response = requests.post(f"{ICAT_URL}/entityManager", data=payload)
    if response.status_code == 412:
        print(f"EXPECTED FAILURE: {response.status_code} - {response.text}")
    else:
        print(f"UNEXPECTED: {response.status_code} - {response.text}")


def test_update_full_object():
    print("\n=== Test 2: with facility => 403 INSUFFICIENT_PRIVILEGES ===")
    import json
    payload = {
        "sessionId": SESSION_ID,
        "entities": json.dumps([{
            "Investigation": {
                "id": INVESTIGATION_ID,
                "name": "ID002606",
                "visitId": "ID00",
                "title": "test",
                "facility": {"id": 305},
                "type": {"id": 325},
            }
        }])
    }
    response = requests.post(f"{ICAT_URL}/entityManager", data=payload)
    if response.status_code == 403:
        print(f"EXPECTED FAILURE: {response.status_code} - {response.text}")
    else:
        print(f"UNEXPECTED: {response.status_code} - {response.text}")


def test_soap():
    print("\n=== Test 3: SOAP update => succeeds ===")
    soap_body = f"""<?xml version="1.0" encoding="UTF-8"?>
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:tns="http://icatproject.org">
  <soap:Body>
    <tns:update>
      <sessionId>{SESSION_ID}</sessionId>
      <bean xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="tns:investigation">
        <id>{INVESTIGATION_ID}</id>
        <name>ID002606</name>
        <visitId>ID00</visitId>
        <title>test</title>
        <startDate>2026-06-17T08:00:00.000+00:00</startDate>
        <facility><id>305</id></facility>
        <type><id>325</id></type>
      </bean>
    </tns:update>
  </soap:Body>
</soap:Envelope>"""

    response = requests.post(
        ICAT_SOAP_URL,
        data=soap_body.encode("utf-8"),
        headers={"Content-Type": "text/xml; charset=utf-8", "SOAPAction": ""}
    )
    if response.status_code == 200:
        print("SUCCESS: SOAP update worked")
    else:
        print(f"UNEXPECTED FAILURE: {response.status_code} - {response.text}")


if __name__ == "__main__":
    test_update_partial()
    test_update_full_object()
    test_soap()

Marjolaine Bodin

unread,
Jun 17, 2026, 12:12:55 PM (6 days ago) Jun 17
to Patrick Austin - STFC UKRI, icatgroup
I’m sending the investigation ID, so this is treated as an update (as shown in the ICAT logs). However, when I include the facility ID and type, I get a PERMISSIONS error, which I don’t understand.
If I don't include them (first case), I have this other 412 error.

Thanks a lot for your feedback!
  Marjolaine

Patrick Austin - STFC UKRI

unread,
Jun 17, 2026, 12:30:28 PM (6 days ago) Jun 17
to Marjolaine Bodin, icatgroup
Hi Marjolaine,

Regarding the first error (partial update), I can't see an obvious place where the problem is - I would have expected that to work, but it must be going wrong somewhere.

Regarding the second, comparing what the REST vs. SOAP versions do I'm fairly confident that the former is wrong, and will always erroneously think you're changing the id of the related item (probably Facility in this case), leading to it checking DELETE permissions, which you don't have.

While I think that could be patched, I think the more general question would be why do the two interfaces not share the same underlying code? And rather than patching the one that is seemingly less reliable, can/should we use the same underlying functions on EntityBeanManager?

Potentially questions for a collaboration meeting?

Patrick


From: Marjolaine Bodin <esrf.marjo...@gmail.com>
Sent: 17 June 2026 17:12
To: Austin, Patrick (STFC,RAL,SC) <patrick...@stfc.ac.uk>
Cc: icatgroup <icat...@googlegroups.com>
Subject: Re: [icatgroup:2149] ICAT update entities
 

Marjolaine Bodin

unread,
Jun 18, 2026, 3:26:07 AM (6 days ago) Jun 18
to Patrick Austin - STFC UKRI, icatgroup
Hi Patrick,
Thanks a lot for your feedback! 
Happy to discuss further in a collaboration meeting if that makes sense!
All the best,
  Marjolaine
Reply all
Reply to author
Forward
0 new messages