def mutation(operations, wait_for_response=False, service_access_token=None):
    """
    Submit a mutation to the graph.

    If ``wait_for_response == True`` the return value is the reponse JSON,
    otherwise the mutation UUID is returned.
    """
    knowledge_graph_url = current_app.config['KNOWLEDGE_GRAPH_URL']

    headers = {'Authorization': 'Bearer {}'.format(service_access_token)}

    response = requests.post(join_url(knowledge_graph_url,
                                      '/mutation/mutation'),
                             json={'operations': operations},
                             headers=headers)

    uuid = response.json()['uuid']

    if wait_for_response:
        completed = False
        while not completed:
            response = requests.get(join_url(
                knowledge_graph_url,
                '/mutation/mutation/{uuid}'.format(uuid=uuid)),
                                    headers=headers).json()
            completed = response['status'] == 'completed'
            # sleep for 200 miliseconds
            time.sleep(0.2)

        return response

    return response.json()['uuid']
def mutation(operations, wait_for_response=False, service_access_token=None):
    """
    Submit a mutation to the graph.

    If ``wait_for_response == True`` the return value is the reponse JSON,
    otherwise the mutation UUID is returned.
    """
    knowledge_graph_url = current_app.config['KNOWLEDGE_GRAPH_URL']

    headers = {'Authorization': 'Bearer {}'.format(service_access_token)}

    response = requests.post(join_url(knowledge_graph_url,
                                      '/mutation/mutation'),
                             json={'operations': operations},
                             headers=headers)

    if not 200 <= response.status_code < 300 and 'uuid' not in response.json():
        logger.warn('Mutation request failed.',
                    extra={'response': response.json()})
        abort(Response('Mutation service failed.', status=504))

    uuid = response.json().get('uuid')

    if wait_for_response:
        completed = False
        while not completed:
            response = requests.get(join_url(
                knowledge_graph_url,
                '/mutation/mutation/{uuid}'.format(uuid=uuid)),
                                    headers=headers)
            completed = response.json()['status'] == 'completed'
            # sleep for 200 miliseconds
            time.sleep(0.2)
    return response
def test_missing_kg_endpoint(kg_app, auth_header, kg_requests, monkeypatch):
    """Test that missing the mutation service is handled gracefully."""
    mutation_url = join_url(current_app.config['KNOWLEDGE_GRAPH_URL'],
                            '/mutation/mutation')
    token_url = current_app.config['DEPLOYER_TOKEN_URL']

    def kg_post(*args, **kwargs):
        """Override requests.post for KG urls."""
        if mutation_url in args[0]:
            """Override /api/mutation/mutation."""
            return Response({}, 404)
        elif token_url in args[0]:
            """Override fetching access tokens."""
            return Response({'access_token': '1234'}, 200)

    monkeypatch.setattr(requests, 'post', kg_post)

    with kg_app.test_client() as client:
        resp = client.post('v1/contexts',
                           data=json.dumps({
                               'image': 'hello-world',
                               'namespace': 'default'
                           }),
                           content_type='application/json',
                           headers=auth_header)

    assert resp.status_code == 504
def kg_requests(monkeypatch):
    """Monkeypatch requests to immitate the KnowledgeGraph."""
    mutation_url = join_url(current_app.config['KNOWLEDGE_GRAPH_URL'],
                            '/mutation/mutation')
    named_type_url = join_url(current_app.config['KNOWLEDGE_GRAPH_URL'],
                              '/types/management/named_type')
    token_url = current_app.config['DEPLOYER_TOKEN_URL']

    def kg_post(*args, **kwargs):
        """Override requests.post for KG urls."""
        if mutation_url in args[0]:
            """Override /api/mutation/mutation."""
            return Response({'uuid': '1234'}, 201)
        elif token_url in args[0]:
            """Override fetching access tokens."""
            return Response({'access_token': '1234'}, 200)
        else:
            return r_post(*args, **kwargs)

    def kg_get(*args, **kwargs):
        """Overrides requests.get for KG URLs."""
        if mutation_url in args[0]:
            """Override /api/mutation/mutation/uuid."""
            return Response(
                {
                    'status': 'completed',
                    'response': {
                        'event': {
                            'status': 'success',
                            'results': [{
                                'id': 1234
                            }]
                        }
                    }
                }, 200)

        elif named_type_url in args[0]:
            """Override /api/types/management/named_type."""
            return named_type_response
        else:
            return r_get(*args, **kwargs)

    monkeypatch.setattr(requests, 'get', kg_get)
    monkeypatch.setattr(requests, 'post', kg_post)
    def named_types(self):
        """Fetch named types from types service."""
        if self._named_types is None:
            service_access_token = get_service_access_token(
                token_url=current_app.config['DEPLOYER_TOKEN_URL'],
                audience='renga-services',
                client_id=current_app.config['RENGA_AUTHORIZATION_CLIENT_ID'],
                client_secret=current_app.
                config['RENGA_AUTHORIZATION_CLIENT_SECRET'])

            headers = {
                'Authorization': 'Bearer {}'.format(service_access_token)
            }

            self._named_types = requests.get(join_url(
                current_app.config['KNOWLEDGE_GRAPH_URL'],
                'types/management/named_type'),
                                             headers=headers).json()
        return self._named_types
    def named_types(self):
        """Fetch named types from types service."""
        if self._named_types is None:
            service_access_token = get_service_access_token(
                token_url=current_app.config['DEPLOYER_TOKEN_URL'],
                audience='renga-services',
                client_id=current_app.config['RENGA_AUTHORIZATION_CLIENT_ID'],
                client_secret=current_app.
                config['RENGA_AUTHORIZATION_CLIENT_SECRET'])

            headers = {
                'Authorization': 'Bearer {}'.format(service_access_token)
            }

            response = requests.get(join_url(
                current_app.config['KNOWLEDGE_GRAPH_URL'],
                'types/management/named_type'),
                                    headers=headers)
            if not 200 <= response.status_code < 300:
                logger.error('Retrieving types failed.')
                raise RuntimeError('Retrieving types failed.')
            else:
                self._named_types = response.json()
        return self._named_types
def test_failed_mutation(kg_app, auth_header, kg_requests, monkeypatch,
                         engine):
    """Test response to mutation failure."""
    with kg_app.test_client() as client:
        # 1. test context creation
        resp = client.post('v1/contexts',
                           data=json.dumps({
                               'image': 'hello-world',
                               'namespace': 'default'
                           }),
                           content_type='application/json',
                           headers=auth_header)

        context = json.loads(resp.data.decode())

    mutation_url = join_url(current_app.config['KNOWLEDGE_GRAPH_URL'],
                            '/mutation/mutation')
    named_type_url = join_url(current_app.config['KNOWLEDGE_GRAPH_URL'],
                              '/types/management/named_type')

    def kg_get_failed(*args, **kwargs):
        """Overrides requests.get for KG URLs."""
        if mutation_url in args[0]:
            """Override /api/mutation/mutation/uuid."""
            return Response(
                {
                    'status': 'completed',
                    'response': {
                        'event': {
                            'status': 'failed',
                            'results': [{
                                'id': 1234
                            }]
                        }
                    }
                }, 200)
        elif named_type_url in args[0]:
            """Override /api/types/management/named_type."""
            return named_type_response
        else:
            return r_get(*args, **kwargs)

    monkeypatch.setattr(requests, 'get', kg_get_failed)

    # 2. test failure due to failed mutation
    with kg_app.test_client() as client:
        resp = client.post('v1/contexts',
                           data=json.dumps({
                               'image': 'hello-world',
                               'namespace': 'default'
                           }),
                           content_type='application/json',
                           headers=auth_header)
        assert resp.status_code == 500

        # 2. test failure during execution creation
        resp = client.post('v1/contexts/{0}/executions'.format(
            context['identifier']),
                           data=json.dumps({'engine': engine}),
                           content_type='application/json',
                           headers=auth_header)
        assert resp.status_code == 500