From 3a26fedd63fb74b909f6ef04f10d965d38aba0c3 Mon Sep 17 00:00:00 2001
From: Lukas Holecek <hluk@email.cz>
Date: Aug 05 2021 10:34:38 +0000
Subject: Docs: Add detailed description for requirements


JIRA: RHELWF-3628

---

diff --git a/docs/decision_requirements.rst b/docs/decision_requirements.rst
index d1efbdf..d3b8450 100644
--- a/docs/decision_requirements.rst
+++ b/docs/decision_requirements.rst
@@ -5,9 +5,34 @@ Decision Requirements
 =====================
 
 Response data for :http:post:`/api/v1.0/decision` contain
-``satisfied_requirements`` and ``unsatisfied_requirements`` fields.
-Value for each field is a list containing requirements of specific
-type.
+``satisfied_requirements`` and ``unsatisfied_requirements`` properties with
+list of requirements.
+
+Satisfied requirements may contain:
+
+- passed test results
+- waived unsatisfied requirements
+- other satisfied requirements
+
+Unsatisfied requirements contain:
+
+- failed test results
+- missing/incomplete tests results
+- other unsatisfied requirements (mainly related to remote rule file)
+
+Each item in the list contains ``type`` property indicating type of the
+requirement.
+
+Unsatisfied requirements containing ``testcase`` property can be waived (using
+this value in a new waiver).
+
+See :ref:`decision_requirements_examples` to get an idea about the data of
+various requirements.
+
+See :ref:`decision_requirements_code_examples` for Python code examples for
+extracting and using the data.
+
+.. _decision_requirements_examples:
 
 Examples
 ========
@@ -207,3 +232,173 @@ found and successfully retrieved, a satisfied requirement is created.
         "subject_identifier": "bash-4.4.20-1.el8_4",
         "subject_type": "koji_build"
     }
+
+.. _decision_requirements_code_examples:
+
+Code Examples
+=============
+
+Below are Python code snippets for working with specific requirement types.
+
+Retrieve decision from Greenwave using Requests Python library:
+
+.. code-block:: python
+
+    import requests
+
+    response = requests.post(GREENWAVE_URL, DECISION_REQUEST_DATA);
+    response.raise_for_status()
+    decision = response.json()
+
+    satisfied = decision["satisfied_requirements"]
+    unsatisfied = decision["unsatisfied_requirements"]
+
+.. important::
+
+   The above code does not handle intermittent network issues. Normally, you
+   would want to use requests session which can retry on a failure.
+
+Passed test results are stored in the ``satisfied_requirements`` list and have
+``test-result-passed`` type.
+
+.. code-block:: python
+
+    passed = [
+        req
+        for req in satisfied
+        if req["type"] == "test-result-passed"
+    ]
+    if passed:
+        print("Passed:")
+        for req in passed:
+            subject_id = req["subject_identifier"]
+            subject_type = req["subject_type"]
+            print(f'  {req["testcase"]} ({subject_id} {subject_type})')
+
+Waived requirements have type ending with "-waived":
+
+- ``test-result-failed-waived``
+- ``test-result-errored-waived``
+- ``test-result-missing-waived``
+- ``invalid-gating-yaml-waived``
+- ``missing-gating-yaml-waived``
+- ``failed-fetch-gating-yaml-waived``
+- other types (can be extended in the future)
+
+.. code-block:: python
+
+    waived = [
+        req
+        for req in satisfied
+        if req["type"].endswith("-waived")
+    ]
+    if waived:
+        print("Waived:")
+        for req in waived:
+            print(f'  {req["testcase"]} ({req["type"]})')
+
+Other satisfied requirements types:
+
+- ``fetched-gating-yaml``
+- ``blacklisted`` (from ``blacklist`` in a policy)
+- ``excluded`` (from ``excluded_packages`` in a policy)
+- other types (can be extended in the future)
+
+.. code-block:: python
+
+    other_satisfied = [
+        req
+        for req in satisfied
+        if req not in waived and req not in passed
+    ]
+    if other_satisfied:
+        print("Passed (not test cases):")
+        for req in other_satisfied:
+            if req["type"] == "fetched-gating-yaml":
+                print(f'  Fetched {req["source"]}')
+            else:
+                print(f'  {req["type"]}: {json.dumps(req)}')
+
+Missing/incomplete test results have ``test-result-missing`` type.
+
+.. code-block:: python
+
+    missing = [
+        req
+        for req in unsatisfied
+        if req["type"] == "test-result-missing"
+    ]
+    if missing:
+        print("Missing:")
+        for req in missing:
+            subject_id = req["subject_identifier"]
+            subject_type = req["subject_type"]
+            print(f'  {req["testcase"]} ({subject_id} {subject_type})')
+
+Failed tests results have ``test-result-failed`` or ``test-result-errored`` type.
+
+.. code-block:: python
+
+    failed = [
+        req
+        for req in unsatisfied
+        if req["type"] in ("test-result-failed", "test-result-errored")
+    ]
+    for req in failed:
+        subject_id = req.get("subject_identifier") or req["item"].get("type")
+        subject_type = req.get("subject_type") or req["item"].get("item")
+        print(f'Failed: {req["testcase"]} ({subject_id} {subject_type})')
+
+Other unsatisfied requirement types:
+
+- ``invalid-gating-yaml``
+- ``missing-gating-yaml``
+- ``failed-fetch-gating-yaml``
+- other types (can be extended in the future)
+
+.. code-block:: python
+
+    other_failed = [
+        req
+        for req in unsatisfied
+        if req not in failed and req not in missing
+    ]
+    if other_failed:
+        print("Failed (not test cases):")
+        for req in other_failed:
+            print(f'  {req["testcase"]} ({req["type"]})')
+
+Unsatisfied requirements containing ``testcase`` property can be waived.
+
+.. code-block:: python
+
+    waivable = [
+        req
+        for req in unsatisfied
+        if "testcase" in req
+    ]
+
+We can print a command to create waivers but user needs to provide **product
+version** (same as in the decision request) and a **comment** (reason for the
+waiver).
+
+.. code-block:: python
+
+    waiver_data = [
+        {
+            "subject_identifier": req.get("subject_identifier") or req["item"].get("type"),
+            "subject_type": req.get("subject_type") or req["item"].get("item"),
+            "testcase": req["testcase"],
+            "scenario": req.get("scenario"),
+            "waived": True,
+            "product_version": PRODUCT_VERSION,
+            "comment": COMMENT,
+        }
+        for req in waivable
+    ]
+    if waiver_data:
+        payload = json.dumps(waiver_data, indent=2)
+        print('Waive failed (ensure "product_version" and "comment" is correct):')
+        print(f"curl --negotiate -u: {WAIVERDB_URL} -d @- <<EOF")
+        print(payload)
+        print("EOF")

