def test_no_change(self, co3_args):
        client = self._connect(co3_args)

        inc = self._create_incident(client, {"name": "test"})

        uri = "/incidents/%d" % inc['id']

        # Create a conflict
        inc["name"] = "the wrong value"
        inc["vers"] -= 1  # Force it to check old_value

        patch = resilient.Patch(inc)

        patch.add_value("name", "test updated")

        def mycb(response, patch_status, patch):
            raise resilient.NoChange

        response = client.patch_with_callback(uri, patch, mycb)

        assert response
        assert response.status_code == 200

        patch_status = resilient.PatchStatus(response.json())

        assert not patch_status.is_success()
        assert patch_status.get_conflict_fields() == ["name"]
    def _patch_incident(self, incident_id, incident_payload):
        """ _patch_incident will update an incident with the specified json payload.
        :param incident_id: incident ID of incident to be updated.
        ;param incident_payload: incident fields to be updated.
        :return:
        """
        try:
            # Update incident
            incident_url = "/incidents/{0}".format(incident_id)
            incident = self.rest_client.get(incident_url)
            patch = resilient.Patch(incident)

            # Iterate over payload dict.
            for name, _ in incident_payload.items():
                if name == 'properties':
                    for field_name, field_value in incident_payload[
                            'properties'].items():
                        patch.add_value(field_name, field_value)
                else:
                    payload_value = incident_payload.get(name)
                    patch.add_value(name, payload_value)

            patch_result = self.rest_client.patch(incident_url, patch)
            result = self._chk_status(patch_result)
            # add back the incident id
            result['id'] = incident_id
            return result

        except Exception as err:
            raise IntegrationError(err)
    def test_patch_invalid_callback(self, co3_args):
        """
        If a callback returns True but didn't modify the passed in patch in any way, that'd be a problem.
        So make sure we throw an exception in that case.
        """
        client = self._connect(co3_args)

        inc = self._create_incident(client, {"name": "test"})

        uri = "/incidents/%d" % inc['id']

        # Create a conflict
        inc["name"] = "the wrong value"
        inc["vers"] -= 1  # Force it to check old_value

        patch = resilient.Patch(inc)

        patch.add_value("name", "test updated")

        def mycb(response, patch_status, patch):
            # Return True but don't modify the patch.
            return True

        with pytest.raises(ValueError) as exception_info:
            client.patch_with_callback(uri, patch, mycb)

        assert "invoked callback did not change the patch object, but returned True" in str(
            exception_info.value)
    def test_patch(self):
        existing = {"a": 1, "properties": {"b": 2}, "vers": 99}

        patch = resilient.Patch(existing)

        patch.add_value("a", 5)
        patch.add_value("properties.b", 6)
        patch.add_value("c", 7)

        dto = patch.to_dict()

        assert dto["version"] == 99

        changes = dto["changes"]

        assert len(changes) == 3

        change = changes[0]
        assert change["field"] == "a"
        assert change["old_value"]["object"] == 1
        assert change["new_value"]["object"] == 5

        change = changes[1]
        assert change["field"] == "properties.b"
        assert change["old_value"]["object"] == 2
        assert change["new_value"]["object"] == 6

        change = changes[2]
        assert change["field"] == "c"
        assert not change["old_value"]["object"]
        assert change["new_value"]["object"] == 7
def _patch_to_close_incident(res_client,
                             incident_id,
                             close_fields,
                             handle_names=False):
    """
    call the Resilient REST API to patch incident
    :param res_client: required for communication back to resilient
    :param incident_id: required
    :param close_fields: required
    :return: response object
    """
    uri = "/incidents/{}".format(incident_id)

    if handle_names:
        uri = "{0}?handle_format=names".format(uri)

    previous_object = res_client.get(uri)
    patch = resilient.Patch(previous_object)

    for field in close_fields:
        patch.add_value(field, close_fields[field])

    response = res_client.patch(uri, patch)

    return response
Beispiel #6
0
    def update_incident(self, payload, existing_incident):
        """ update_incident will update an incident with the specified json payload.
        ;param payload: incident fields to be updated.
        existing_incident ([dict]): existing incident fields, or None for create operation
        :return: incident updated
        """
        try:
            # Update incident
            incident_url = "/".join([INCIDENT_URL, str(existing_incident['id'])])

            patch = resilient.Patch(existing_incident)

            # Iterate over payload dict.
            for name, _ in payload.items():
                if name == 'properties':
                    for field_name, field_value in payload['properties'].items():
                        patch.add_value(field_name, field_value)
                else:
                    payload_value = payload.get(name)
                    patch.add_value(name, payload_value)

            patch_result = self.rest_client.patch(incident_url, patch)
            result = self._chk_status(patch_result)
            # add back the incident id
            result['id'] = existing_incident['id']
            return result

        except Exception as err:
            raise IntegrationError(err)
    def test_exchange_conflicting_value(self):
        # Given a base object with a value of "test1".
        base = dict(mytest1="test1")

        # And a patch that is attempting to modify that base object to have a value of "test2".
        patch = resilient.Patch(base)

        patch.add_value("mytest1", "test2")

        # Confirm that it does indeed have an "old value" of "test1" (this is taken from the base object).
        assert patch.get_old_value("mytest1") == "test1"

        # When we create a patch status that simulates a conflict error from the server (where the
        # value of base changed from "test1" to "blah").
        patch_status = resilient.PatchStatus({
            "success":
            False,
            "field_failures": [{
                "field": "mytest1",
                "your_original_value": "test2",
                "actual_current_value": "blah"
            }],
            "message":
            "Some message"
        })

        # When I exchange the conflicting value...
        patch.exchange_conflicting_value(patch_status, "mytest1", "test2")

        # The patch's "old value" will be the current server's value.
        assert patch.get_old_value("mytest1") == "blah"
        assert patch.get_new_value("mytest1") == "test2"
    def test_conflict_with_handler(self, co3_args):
        client = self._connect(co3_args)

        inc = self._create_incident(client, {"name": "test"})

        uri = "/incidents/%d" % inc['id']

        # Create a conflict
        inc["name"] = "the wrong value"
        inc["vers"] -= 1  # Force it to check old_value

        patch = resilient.Patch(inc)

        patch.add_value("name", "test updated")

        def mycb(response, patch_status, patch):
            patch.exchange_conflicting_value(patch_status, "name",
                                             "test updated take 2")

        response = client.patch_with_callback(uri, patch, mycb)

        assert response
        assert response.status_code == 200

        assert "test updated take 2" == client.get(uri)["name"]
    def test_has_changes(self):
        patch = resilient.Patch(dict(testfield=1))

        assert not patch.has_changes()

        patch.add_value("testfield", 5)

        assert patch.has_changes()
    def test_partial_property_name(self):
        existing = {"properties": {"a": 5}}
        patch = resilient.Patch(existing)

        with pytest.raises(ValueError) as exception_info:
            patch.add_value("properties", 99)

        assert "Invalid field_name parameter" in str(exception_info.value)
def generic_patch(client, uri, template_file_name):
    with open(template_file_name, 'r') as update_file:
        patch = resilient.Patch(json.loads(update_file.read()))

    response = client.patch(uri, patch)

    print('Response:  ', file=sys.stderr)
    print(json.dumps(response, indent=4))
 def test_patch_no_conflict(self, co3_args):
     """ do incident_patch with no conflict """
     client = self._connect(co3_args)
     inc = self._create_incident(client, {"name": "test"})
     uri = "/incidents/%d" % inc['id']
     patch = resilient.Patch(inc)
     patch.add_value("name", "test updated")
     response = client.patch(uri, patch, overwrite_conflict=False)
     assert resilient.PatchStatus(response.json()).is_success()
     inc = client.get("/incidents/%d" % inc['id'])
     assert inc['name'] == "test updated"
    def test_delete(self):
        patch = resilient.Patch(dict(a=1, b=2))

        assert not patch.has_changes()

        patch.add_value("a", 11)

        assert patch.has_changes()

        assert patch.get_old_value("a") == 1
        assert patch.get_new_value("a") == 11

        patch.delete_value("a")

        assert not patch.has_changes()
    def test_add_twice(self):
        patch = resilient.Patch({"a": 5})

        patch.add_value("a", 7)
        patch.add_value("a", 8)

        mydict = patch.to_dict()

        changes = mydict["changes"]

        assert len(changes) == 1

        assert changes[0]["field"] == "a"
        assert changes[0]["old_value"]["object"] == 5
        assert changes[0]["new_value"]["object"] == 8
    def test_patch_null_old_value(self, co3_args):
        client = self._connect(co3_args)

        inc = self._create_incident(client, {"name": "test", "description": None})

        patch = resilient.Patch(inc)

        patch.add_value("description", "new value")

        uri = "/incidents/%d" % inc['id']

        response = client.patch(uri, patch)

        inc = client.get("/incidents/%d" % inc['id'])

        assert inc["description"] == "new value"
    def test_null_old_value(self):
        patch = resilient.Patch({"blah": "old value"})

        patch.add_value("blah", "new value", old_value=None)

        mydict = patch.to_dict()

        assert mydict

        changes = mydict["changes"]

        assert changes

        assert len(changes) == 1
        assert changes[0]["field"] == "blah"
        assert not changes[0]["old_value"]["object"]
        assert changes[0]["new_value"]["object"] == "new value"
def main():
    """
    program main
    """

    parser = ExampleArgumentParser(config_file=resilient.get_config_file())
    opts = parser.parse_args()

    # Create SimpleClient for a REST connection to the Resilient services
    client = resilient.get_client(opts)

    inc_id = opts["incid"]
    desc = opts["desc"]

    try:
        uri = '/incidents/{}'.format(inc_id)

        incident = client.get(uri)

        # Create a patch object.  You need to pass it the base object (the thing being patched).  This
        # object contains the old values, which are sent to the server.
        patch = resilient.Patch(incident)

        patch.add_value("description", desc)

        print('''
At this point, we have a copy of the specified incident.  If you want to trigger a conflict to see
what will happen, then you can do so now.

Press the Enter key to continue''')
        input()

        # Apply the patch and overwrite any conflicts.
        client.patch(uri, patch, overwrite_conflict=True)

        # Confirm that our change was applied.  This is not something that you'd typically need to do since the
        # patch applied successfully, but this illustrates that the change was applied for the purposes of this
        # example.
        assert desc == client.get(uri)["description"]

    except resilient.SimpleHTTPException as ecode:
        print("patch failed : {}".format(ecode))
    def test_patch_conflict(self, co3_args, overwrite_conflict):
        """ do incident patch that results in conflict """
        client = self._connect(co3_args)
        inc = self._create_incident(client, {"name": "test"})
        uri = "/incidents/%d" % inc['id']
        # Create a conflict
        inc["name"] = "the wrong value"
        inc["vers"] -= 1  # Force it to check old_value
        patch = resilient.Patch(inc)
        patch.add_value("name", "test updated")

        if overwrite_conflict:
            # If overwrite_conflict is specified then patch will return.
            response = client.patch(uri,
                                    patch,
                                    overwrite_conflict=overwrite_conflict)

            assert resilient.PatchStatus(
                response.json()).is_success() is overwrite_conflict
        else:
            # Not overwriting conflict, so an exception will be thrown.
            with pytest.raises(
                    resilient.PatchConflictException) as exception_info:
                client.patch(uri, patch, overwrite_conflict=overwrite_conflict)

            # Gather the patch_status value from the exception for additional verification.
            patch_status = exception_info.value.patch_status

            fail_msg = "could not be applied due to a conflicting edit by another user.  The following field(s) were in conflict:  name."
            assert fail_msg in patch_status.get_message()

            assert patch_status.get_conflict_fields() == ["name"]
            assert patch_status.get_your_original_value(
                "name") == "the wrong value"
            assert patch_status.get_actual_current_value("name") == "test"

        inc = client.get("/incidents/%d" % inc['id'])

        if overwrite_conflict:
            assert inc['name'] == "test updated"
        else:
            assert inc['name'] == "test"
    def test_no_old_value(self):
        patch = resilient.Patch({})

        # this one is allowed
        patch.add_value("a", new_value=5, old_value=3)

        dto = patch.to_dict()

        changes = dto["changes"]

        assert len(changes) == 1
        assert changes[0]["field"] == "a"
        assert changes[0]["old_value"]["object"] == 3
        assert changes[0]["new_value"]["object"] == 5

        with pytest.raises(ValueError) as exception_info:
            patch.add_value("b", new_value=10)

        assert "Constructor previous_object or method old_value argument is required" in str(
            exception_info.value)
Beispiel #20
0
    def update_incident_properties(self, incident_id, fields):
        """ Update Resilient incident custom property or fields.

        :param incident_id: Incident ID.
        :param fields: Property fields to be updates.
        :return: response object
        """
        try:
            response = None
            resilient_client = self.rest_client()
            uri = "/incidents/{}".format(incident_id)

            previous_object = resilient_client.get(uri)
            patch = resilient.Patch(previous_object)

            properties = fields.get("properties")

            if properties:
                for field in properties:
                    if field not in previous_object["properties"]:
                        raise ValueError(
                            "Invalid property parameter {}".format(field))

                    patch.changes[field] = \
                        resilient.patch.Change(field, properties[field], previous_object["properties"][field])
            else:
                for field in fields:
                    patch.add_value(field, fields[field])

            response = resilient_client.patch(uri, patch)

        except SimpleHTTPException as ex:
            LOG.error(
                'Something went wrong when attempting to patch the Incident: %s',
                ex)

        return response
Beispiel #21
0
def main():
    """
    program main
    """

    parser = ExampleArgumentParser(config_file=resilient.get_config_file())
    opts = parser.parse_args()

    inc_id = opts["incid"]
    itypes = opts["itype"]

    # Create SimpleClient for a REST connection to the Resilient services
    client = resilient.get_client(opts)

    try:
        uri = '/incidents/{}?handle_format=names'.format(inc_id)

        incident = client.get(uri)

        # Create a patch object.  You need to pass it the base object (the thing being patched).
        patch = resilient.Patch(incident)

        # The initial patch will contain the change we want to make.
        old_itypes = incident["incident_type_ids"]

        patch.add_value("incident_type_ids", old_itypes + itypes)

        def patch_conflict_handler(response, patch_status, patch):
            # If this gets called then there was a patch conflict, we we need to
            # adjust the patch to include an update.  This only gets called if
            # a field we're trying to change has failed.  In that case the actual
            # value currently on the server is included in the patch_status object.
            #
            # You can retrieve the current server value using
            # patch_status.get_actual_current_value(field_name).  This
            # will return the actual value that exists on the server.
            #
            # In our case, we'll be appending to this value.
            #
            print("patch conflict detected, operation returned:  ")
            print(json.dumps(patch_status.to_dict(), indent=4))

            current_value = patch_status.get_actual_current_value(
                "incident_type_ids")

            patch.exchange_conflicting_value(patch_status, "incident_type_ids",
                                             current_value + itypes)

        print("existing itypes: {}".format(old_itypes))
        print("wanted to add these: {}".format(itypes))

        print('''
At this point, we have a copy of the specified incident.  If you want to trigger a conflict to see
what will happen, then you can do so now.

Press the Enter key to continue''')
        input()

        client.patch_with_callback(uri, patch, patch_conflict_handler)

        # Confirm that our change was applied.  This is not something that you'd typically need to do since the
        # patch applied successfully, but this illustrates that the change was applied for the purposes of this
        # example.
        #
        new_itypes = client.get(uri)["incident_type_ids"]

        # Remove the original description, which will leave only our addition.
        print("itypes after update: {}".format(new_itypes))

        assert set(itypes).issubset(new_itypes)

    except resilient.SimpleHTTPException as ecode:
        print("patch failed : {}".format(ecode))