jmap_call.ymlΒΆ

---
# Reusable management JMAP call: in `stalwart_jmap_body` (JSON), out `stalwart_jmap`.
# Param: stalwart_jmap_body - JSON request; a batch carries one method call per object owner, its call id naming that owner in a refusal.
# Param: stalwart_jmap_tolerate_rejected - true when the caller reads a set-level rejection itself (e.g. primaryKeyViolation).
- name: "πŸ”§ Resolve the Stalwart management URL and admin login once per run"
  ansible.builtin.set_fact:
    stalwart_jmap_url: "{{ STALWART_JMAP_URL }}"
    stalwart_jmap_user_password: "{{ STALWART_ADMIN_USER ~ ':' ~ STALWART_ADMIN_PASSWORD }}"
  when: stalwart_jmap_user_password is not defined
  no_log: "{{ MASK_CREDENTIALS_IN_LOGS | bool }}"

- 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_user_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
        | selectattr('0', 'equalto', 'error') | map(attribute='1.type')
        | select('in', STALWART_JMAP_TRANSIENT_ERRORS) | list | length == 0
  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_refused | length == 0
    fail_msg: "Stalwart refused the JMAP call {{ stalwart_jmap_refused }}"
    quiet: true
  vars:
    stalwart_jmap_refused: >-
      {%- set refused = {} -%}
      {%- for method, result, call in stalwart_jmap.methodResponses -%}
      {%-   set rejected = {} if (method == 'error' or (stalwart_jmap_tolerate_rejected | default(false) | bool))
                           else (result.notCreated | default({}, true))
                                | combine(result.notUpdated | default({}, true))
                                | combine(result.notDestroyed | default({}, true)
                                          | dict2items | rejectattr('value.type', 'equalto', 'notFound') | items2dict) -%}
      {%-   if method == 'error' or rejected | length > 0 -%}
      {%-     set _ = refused.update({call: {method: result if method == 'error' else rejected}}) -%}
      {%-   endif -%}
      {%- endfor -%}
      {{ refused }}