--- # Reusable management JMAP call: in `stalwart_jmap_body` (JSON), out `stalwart_jmap`. # Param: stalwart_jmap_tolerate_rejected - true when the caller reads a set-level rejection itself (e.g. alreadyExists). # Param: stalwart_jmap_act_as - account address the recovery admin acts for; unset runs the call as the admin itself. - name: "🔧 Stalwart JMAP call" ansible.builtin.shell: cmd: >- container exec -i {{ STALWART_CONTAINER_ADDRESS }} {{ 30 | curl }} -sS -f -K - -H "Content-Type:application/json" {{ STALWART_JMAP_URL }} stdin: |- user = {{ ((stalwart_jmap_act_as ~ '%' if stalwart_jmap_act_as is defined else '') ~ STALWART_ADMIN_USER ~ ':' ~ STALWART_ADMIN_PASSWORD) | to_json(ensure_ascii=False) }} data-binary = {{ stalwart_jmap_body | to_json(ensure_ascii=False) }} register: stalwart_jmap_raw changed_when: false retries: 12 delay: 5 until: >- stalwart_jmap_raw.rc == 0 and ( (stalwart_jmap_raw.stdout | from_json).methodResponses[0][0] != 'error' or (stalwart_jmap_raw.stdout | from_json).methodResponses[0][1].type not in STALWART_JMAP_TRANSIENT_ERRORS ) timeout: "{{ lookup('timeout', 120) | int }}" # nocheck: include-uniform-apply-keyword — seven call sites include this file; the in-file no_log is the SPOT, per-site apply would repeat it 7× no_log: "{{ MASK_CREDENTIALS_IN_LOGS | bool }}" - name: "🔧 Parse Stalwart JMAP response" ansible.builtin.set_fact: stalwart_jmap: "{{ stalwart_jmap_raw.stdout | from_json }}" no_log: "{{ MASK_CREDENTIALS_IN_LOGS | bool }}" - name: "✅ Ensure Stalwart accepted the JMAP call" ansible.builtin.assert: that: - stalwart_jmap.methodResponses[0][0] != 'error' - stalwart_jmap_rejected | length == 0 fail_msg: "Stalwart refused the JMAP call {{ stalwart_jmap.methodResponses[0][0] }}: {{ stalwart_jmap_rejected or stalwart_jmap.methodResponses[0][1] }}" quiet: true vars: stalwart_jmap_rejected: >- {{ {} if (stalwart_jmap_tolerate_rejected | default(false) | bool) else (stalwart_jmap.methodResponses[0][1].notCreated | default({}, true)) | combine(stalwart_jmap.methodResponses[0][1].notUpdated | default({}, true)) | combine(stalwart_jmap.methodResponses[0][1].notDestroyed | default({}, true) | dict2items | rejectattr('value.type', 'equalto', 'notFound') | items2dict) }}