Esempio n. 1
0
    async def action_handler(service, action_type, payload, props, **kwds):
        # if the payload represents a new instance of `model`
        if action_type == get_crud_action('read', name or Model):
            # the props of the message
            message_props = {}
            # if there was a correlation id in the request
            if 'correlation_id' in props:
                # make sure it ends up in the reply
                message_props['correlation_id'] = props['correlation_id']

            try:
                # resolve the query using the service schema
                resolved = service.schema.execute(payload)
                # create the string response
                response = json.dumps({
                    'data': {key:value for key,value in resolved.data.items()},
                    'errors': resolved.errors
                })

                # publish the success event
                await service.event_broker.send(
                    payload=response,
                    action_type=change_action_status(action_type, success_status()),
                    **message_props
                )

            # if something goes wrong
            except Exception as err:
                # publish the error as an event
                await service.event_broker.send(
                    payload=str(err),
                    action_type=change_action_status(action_type, error_status()),
                    **message_props
                )
Esempio n. 2
0
    def _create_linked_handler(self, model):
        # the related action type
        related_action_type = get_crud_action('delete',
                                              model,
                                              status=success_status())

        # the action handler
        async def action_handler(action_type, payload, notify=True, **kwds):
            """
                an action handler to remove related entries in the
                connection db.
            """
            # if the action designates a successful delete of the model
            if action_type == related_action_type:
                # the id of the deleted model
                related_id = payload['id']
                # the query for matching fields
                matching_records = getattr(
                    self.model, model_service_name(model)) == related_id
                ids = [
                    model.id for model in self.model.filter(matching_records)
                ]
                # find the matching records
                self.model.delete().where(matching_records).execute()

                # if we are supposed to notify
                if notify:
                    # notify of the related delete
                    await self.event_broker.send(action_type=get_crud_action(
                        'delete', self.model, status=success_status()),
                                                 payload=ids)

        # pass the action handler
        return action_handler
Esempio n. 3
0
    def _create_linked_handler(self, model):
        # the related action type
        related_action_type = get_crud_action('delete', model, status=success_status())
        # the action handler
        async def action_handler(action_type, payload, notify=True, **kwds):
            """
                an action handler to remove related entries in the
                connection db.
            """
            # if the action designates a successful delete of the model
            if action_type == related_action_type:
                # the id of the deleted model
                related_id = payload['id']
                # the query for matching fields
                matching_records = getattr(self.model, model_service_name(model)) == related_id
                ids = [model.id for model in self.model.filter(matching_records)]
                # find the matching records
                self.model.delete().where(matching_records).execute()

                # if we are supposed to notify
                if notify:
                    # notify of the related delete
                    await self.event_broker.send(
                        action_type=get_crud_action('delete', self.model, status=success_status()),
                        payload=ids
                    )


        # pass the action handler
        return action_handler
Esempio n. 4
0
def summarize_crud_mutation(method, model, isAsync=False):
    """
        This function provides the standard form for crud mutations.
    """

    # create the approrpriate action type
    action_type = get_crud_action(method=method, model=model)
    # the name of the mutation
    name = crud_mutation_name(model=model, action=method)
    # a mapping of methods to input factories
    input_map = {
        'create': create_mutation_inputs,
        'update': update_mutation_inputs,
        'delete': delete_mutation_inputs,
    }
    # a mappting of methods to output factories
    output_map = {
        'create': create_mutation_outputs,
        'update': update_mutation_outputs,
        'delete': delete_mutation_outputs,
    }
    # the inputs for the mutation
    inputs = input_map[method](model)
    # the mutation outputs
    outputs = output_map[method](model)

    # return the appropriate summary
    return summarize_mutation(mutation_name=name,
                              event=action_type,
                              isAsync=isAsync,
                              inputs=inputs,
                              outputs=outputs)
Esempio n. 5
0
    async def action_handler(service,
                             action_type,
                             payload,
                             props,
                             notify=True,
                             **kwds):
        # if the payload represents a new instance of `Model`
        if action_type == get_crud_action('create', name or Model):
            # print('handling create for ' + name or Model)
            try:
                # the props of the message
                message_props = {}
                # if there was a correlation id in the request
                if 'correlation_id' in props:
                    # make sure it ends up in the reply
                    message_props['correlation_id'] = props['correlation_id']

                # for each required field
                for requirement in Model.required_fields():

                    # save the name of the field
                    field_name = requirement.name
                    # ensure the value is in the payload
                    # TODO: check all required fields rather than failing on the first
                    if not field_name in payload and field_name != 'id':
                        # yell loudly
                        raise ValueError(
                            "Required field not found in payload: %s" %
                            field_name)

                # create a new model
                new_model = Model(**payload)

                # save the new model instance
                new_model.save()

                # if we need to tell someone about what happened
                if notify:
                    # publish the scucess event
                    await service.event_broker.send(
                        payload=ModelSerializer().serialize(new_model),
                        action_type=change_action_status(
                            action_type, success_status()),
                        **message_props)

            # if something goes wrong
            except Exception as err:
                # if we need to tell someone about what happened
                if notify:
                    # publish the error as an event
                    await service.event_broker.send(
                        payload=str(err),
                        action_type=change_action_status(
                            action_type, error_status()),
                        **message_props)
                # otherwise we aren't supposed to notify
                else:
                    # raise the exception normally
                    raise err
Esempio n. 6
0
    async def action_handler(service, action_type, payload, props, notify=True, **kwds):
        # if the payload represents a new instance of `Model`
        if action_type == get_crud_action('update', name or Model):
            try:
                # the props of the message
                message_props = {}
                # if there was a correlation id in the request
                if 'correlation_id' in props:
                    # make sure it ends up in the reply
                    message_props['correlation_id'] = props['correlation_id']


                # grab the nam eof the primary key for the model
                pk_field = Model.primary_key()

                # make sure there is a primary key to id the model
                if not pk_field.name in payload:
                    # yell loudly
                    raise ValueError("Must specify the pk of the model when updating")

                # grab the matching model
                model = Model.select().where(pk_field == payload[pk_field.name]).get()

                # remove the key from the payload
                payload.pop(pk_field.name, None)

                # for every key,value pair
                for key, value in payload.items():
                    # TODO: add protection for certain fields from being
                    # changed by the api
                    setattr(model, key, value)

                # save the updates
                model.save()

                # if we need to tell someone about what happened
                if notify:
                    # publish the scucess event
                    await service.event_broker.send(
                        payload=ModelSerializer().serialize(model),
                        action_type=change_action_status(action_type, success_status()),
                        **message_props
                    )

            # if something goes wrong
            except Exception as err:
                # if we need to tell someone about what happened
                if notify:
                    # publish the error as an event
                    await service.event_broker.send(
                        payload=str(err),
                        action_type=change_action_status(action_type, error_status()),
                        **message_props
                    )
                # otherwise we aren't supposed to notify
                else:
                    # raise the exception normally
                    raise err
Esempio n. 7
0
    def test_can_change_action_types(self):

        #  try to creat a crud action for a model
        action_type = get_crud_action(method='create', model=self.model)
        # try to change the action
        success_action = change_action_status(action_type, 'success')
        # make sure the new action type is in the result
        assert success_action == 'create.testModel.success', (
            "New action type did not have the correct form.")
Esempio n. 8
0
    def test_can_change_action_types(self):

        #  try to creat a crud action for a model
        action_type = get_crud_action(method='create', model=self.model)
        # try to change the action
        success_action = change_action_status(action_type, 'success')
        # make sure the new action type is in the result
        assert success_action == 'create.testModel.success', (
            "New action type did not have the correct form."
        )
Esempio n. 9
0
    def test_can_generate_crud_action_types(self):
        #  try to creat a crud action for a model
        action_type = get_crud_action(
            method='create',
            model=self.model
        )

        assert action_type == 'create.testModel.pending', (
            "New action type did not have the correct form."
        )
Esempio n. 10
0
 def _verify_crud_mutation(self, model, action):
     # create the mutation
     summarized = summarize_crud_mutation(model=model, method=action)
     # make sure the name matches the convention
     assert summarized['name'] == crud_mutation_name(model=model, action=action), (
         "Summarized %s mutation did not have the right name." % action
     )
     # make sure the event is what we expect
     assert summarized['event'] == get_crud_action(model=model, method=action), (
         "Summarized %s mutation did not have the right event type." % action
     )
Esempio n. 11
0
    async def action_handler(service, action_type, payload, props, notify=True, **kwds):
        # if the payload represents a new instance of `Model`
        if action_type == get_crud_action('create', name or Model):
            # print('handling create for ' + name or Model)
            try:
                # the props of the message
                message_props = {}
                # if there was a correlation id in the request
                if 'correlation_id' in props:
                    # make sure it ends up in the reply
                    message_props['correlation_id'] = props['correlation_id']

                # for each required field
                for requirement in Model.required_fields():

                    # save the name of the field
                    field_name = requirement.name
                    # ensure the value is in the payload
                    # TODO: check all required fields rather than failing on the first
                    if not field_name in payload and field_name != 'id':
                        # yell loudly
                        raise ValueError(
                            "Required field not found in payload: %s" %field_name
                        )

                # create a new model
                new_model = Model(**payload)

                # save the new model instance
                new_model.save()

                # if we need to tell someone about what happened
                if notify:
                    # publish the scucess event
                    await service.event_broker.send(
                        payload=ModelSerializer().serialize(new_model),
                        action_type=change_action_status(action_type, success_status()),
                        **message_props
                    )

            # if something goes wrong
            except Exception as err:
                # if we need to tell someone about what happened
                if notify:
                    # publish the error as an event
                    await service.event_broker.send(
                        payload=str(err),
                        action_type=change_action_status(action_type, error_status()),
                        **message_props
                    )
                # otherwise we aren't supposed to notify
                else:
                    # raise the exception normally
                    raise err
Esempio n. 12
0
 def _verify_crud_mutation(self, model, action):
     # create the mutation
     summarized = summarize_crud_mutation(model=model, method=action)
     # make sure the name matches the convention
     assert summarized['name'] == crud_mutation_name(
         model=model, action=action), (
             "Summarized %s mutation did not have the right name." % action)
     # make sure the event is what we expect
     assert summarized['event'] == get_crud_action(
         model=model, method=action), (
             "Summarized %s mutation did not have the right event type." %
             action)
Esempio n. 13
0
    async def action_handler(service,
                             action_type,
                             payload,
                             props,
                             notify=True,
                             **kwds):
        # if the payload represents a new instance of `model`
        if action_type == get_crud_action('delete', name or Model):
            try:
                # the props of the message
                message_props = {}
                # if there was a correlation id in the request
                if 'correlation_id' in props:
                    # make sure it ends up in the reply
                    message_props['correlation_id'] = props['correlation_id']
                # the id in the payload representing the record to delete
                record_id = payload['id'] if 'id' in payload else payload['pk']
                # get the model matching the payload
                try:
                    model_query = Model.select().where(
                        Model.primary_key() == record_id)
                except KeyError:
                    raise RuntimeError(
                        "Could not find appropriate id to remove service record."
                    )
                # remove the model instance
                model_query.get().delete_instance()
                # if we need to tell someone about what happened
                if notify:
                    # publish the success event
                    await service.event_broker.send(
                        payload='{"status":"ok"}',
                        action_type=change_action_status(
                            action_type, success_status()),
                        **message_props)

            # if something goes wrong
            except Exception as err:
                # if we need to tell someone about what happened
                if notify:
                    # publish the error as an event
                    await service.event_broker.send(
                        payload=str(err),
                        action_type=change_action_status(
                            action_type, error_status()),
                        **message_props)
                # otherwise we aren't supposed to notify
                else:
                    # raise the exception normally
                    raise err
Esempio n. 14
0
    async def _create_remote_user(self, **payload):
        """
            This method creates a service record in the remote user service
            with the given email.
            Args:
                uid (str): the user identifier to create
            Returns:
                (dict): a summary of the user that was created
        """
        # the action for reading user entries
        read_action = get_crud_action(method='create', model='user')

        # see if there is a matching user
        user_data = await self.event_broker.ask(action_type=read_action,
                                                payload=payload)
        # treat the reply like a json object
        return json.loads(user_data)
Esempio n. 15
0
    async def connection_resolver(self, connection_name, object):

        try:
            # grab the recorded data for this connection
            expected = [ conn for conn in self._external_service_data['connections']\
                              if conn['name'] == connection_name][0]
        # if there is no connection data yet
        except AttributeError:
            raise ValueError("No objects are registered with this schema yet.")
        # if we dont recognize the model that was requested
        except IndexError:
            raise ValueError("Cannot query for {} on {}.".format(
                connection_name, object['name']))

        # the target of the connection
        to_service = expected['connection']['to']['service']

        # ask for only the entries connected to the object
        filters = {object['name']: object['pk']}
        # the field of the connection is the model name
        fields = [to_service]

        # the query for model records
        query = query_for_model(fields, **filters).replace("'", '"')

        # the action type for the question
        action_type = get_crud_action('read', connection_name)

        # get the service name for the connection
        response = json.loads(await
                              self.event_broker.ask(action_type=action_type,
                                                    payload=query))

        if 'errors' in response and response['errors']:
            # return an empty response
            raise ValueError(','.join(response['errors']))

        # grab the ids from the response
        ids = [
            int(entry[to_service]) for entry in response['data']['all_models']
        ]

        # the question for connected nodes
        return ids, to_service
Esempio n. 16
0
    async def _get_matching_user(self, fields=[], **filters):
        # the action type for a remote query
        read_action = get_crud_action(method='read', model='user')
        # the fields of the user to ask for
        user_fields = ['pk'] + fields

        # the query for matching entries
        payload = """
            query {
                %s(%s) {
                    %s
                }
            }
        """ % (root_query(), arg_string_from_dict(filters),
               '\n'.join(user_fields))

        # perform the query and return the result
        return json.loads(await self.event_broker.ask(action_type=read_action,
                                                      payload=payload))
Esempio n. 17
0
    async def _create_remote_user(self, **payload):
        """
            This method creates a service record in the remote user service
            with the given email.
            Args:
                uid (str): the user identifier to create
            Returns:
                (dict): a summary of the user that was created
        """
        # the action for reading user entries
        read_action = get_crud_action(method='create', model='user')

        # see if there is a matching user
        user_data = await self.event_broker.ask(
            action_type=read_action,
            payload=payload
        )
        # treat the reply like a json object
        return json.loads(user_data)
Esempio n. 18
0
    async def connection_resolver(self, connection_name, object):

        try:
            # grab the recorded data for this connection
            expected = [ conn for conn in self._external_service_data['connections']\
                              if conn['name'] == connection_name][0]
        # if there is no connection data yet
        except AttributeError:
            raise ValueError("No objects are registered with this schema yet.")
        # if we dont recognize the model that was requested
        except IndexError:
            raise ValueError("Cannot query for {} on {}.".format(connection_name, object['name']))

        # the target of the connection
        to_service = expected['connection']['to']['service']

        # ask for only the entries connected to the object
        filters = {object['name']: object['pk']}
        # the field of the connection is the model name
        fields = [to_service]

        # the query for model records
        query = query_for_model(fields, **filters).replace("'", '"')

        # the action type for the question
        action_type = get_crud_action('read', connection_name)

        # get the service name for the connection
        response = json.loads(await self.event_broker.ask(
            action_type=action_type,
            payload=query
        ))

        if 'errors' in response and response['errors']:
            # return an empty response
            raise ValueError(','.join(response['errors']))

        # grab the ids from the response
        ids = [int(entry[to_service]) for entry in response['data']['all_models']]

        # the question for connected nodes
        return ids, to_service
Esempio n. 19
0
    async def action_handler(service, action_type, payload, props, notify=True, **kwds):
        # if the payload represents a new instance of `model`
        if action_type == get_crud_action('delete', name or Model):
            try:
                # the props of the message
                message_props = {}
                # if there was a correlation id in the request
                if 'correlation_id' in props:
                    # make sure it ends up in the reply
                    message_props['correlation_id'] = props['correlation_id']
                # the id in the payload representing the record to delete
                record_id = payload['id'] if 'id' in payload else payload['pk']
                # get the model matching the payload
                try:
                    model_query = Model.select().where(Model.primary_key() == record_id)
                except KeyError:
                    raise RuntimeError("Could not find appropriate id to remove service record.")
                # remove the model instance
                model_query.get().delete_instance()
                # if we need to tell someone about what happened
                if notify:
                    # publish the success event
                    await service.event_broker.send(
                        payload='{"status":"ok"}',
                        action_type=change_action_status(action_type, success_status()),
                        **message_props
                    )

            # if something goes wrong
            except Exception as err:
                # if we need to tell someone about what happened
                if notify:
                    # publish the error as an event
                    await service.event_broker.send(
                        payload=str(err),
                        action_type=change_action_status(action_type, error_status()),
                        **message_props
                    )
                # otherwise we aren't supposed to notify
                else:
                    # raise the exception normally
                    raise err
Esempio n. 20
0
    async def _get_matching_user(self, fields=[], **filters):
        # the action type for a remote query
        read_action = get_crud_action(method='read', model='user')
        # the fields of the user to ask for
        user_fields = ['pk'] + fields

        # the query for matching entries
        payload = """
            query {
                %s(%s) {
                    %s
                }
            }
        """ % (root_query(), arg_string_from_dict(filters), '\n'.join(user_fields))

        # perform the query and return the result
        return json.loads(await self.event_broker.ask(
            action_type=read_action,
            payload=payload
        ))
Esempio n. 21
0
def summarize_crud_mutation(method, model, isAsync=False):
    """
        This function provides the standard form for crud mutations.
    """

    # create the approrpriate action type
    action_type = get_crud_action(method=method, model=model)
    # the name of the mutation
    name = crud_mutation_name(model=model, action=method)
    # a mapping of methods to input factories
    input_map = {"create": create_mutation_inputs, "update": update_mutation_inputs, "delete": delete_mutation_inputs}
    # a mappting of methods to output factories
    output_map = {
        "create": create_mutation_outputs,
        "update": update_mutation_outputs,
        "delete": delete_mutation_outputs,
    }
    # the inputs for the mutation
    inputs = input_map[method](model)
    # the mutation outputs
    outputs = output_map[method](model)

    # return the appropriate summary
    return summarize_mutation(mutation_name=name, event=action_type, isAsync=isAsync, inputs=inputs, outputs=outputs)
Esempio n. 22
0
    def test_can_generate_crud_action_types(self):
        #  try to creat a crud action for a model
        action_type = get_crud_action(method='create', model=self.model)

        assert action_type == 'create.testModel.pending', (
            "New action type did not have the correct form.")
Esempio n. 23
0
    async def object_resolver(self, object_name, fields, obey_auth=False, current_user=None, **filters):
        """
            This function resolves a given object in the remote backend services
        """

        try:
            # check if an object with that name has been registered
            registered = [model for model in self._external_service_data['models'] \
                                if model['name']==object_name][0]
        # if there is no connection data yet
        except AttributeError:
            raise ValueError("No objects are registered with this schema yet.")
        # if we dont recognize the model that was requested
        except IndexError:
            raise ValueError("Cannot query for object {} on this service.".format(object_name))

        # the valid fields for this object
        valid_fields = [field['name'] for field in registered['fields']]

        # figure out if any invalid fields were requested
        invalid_fields = [field for field in fields if field not in valid_fields]
        try:
            # make sure we never treat pk as invalid
            invalid_fields.remove('pk')
        # if they weren't asking for pk as a field
        except ValueError:
            pass

        # if there were
        if invalid_fields:
            # yell loudly
            raise ValueError("Cannot query for fields {!r} on {}".format(
                invalid_fields, registered['name']
            ))

        # make sure we include the id in the request
        fields.append('pk')

        # the query for model records
        query = query_for_model(fields, **filters)

        # the action type for the question
        action_type = get_crud_action('read', object_name)

        # query the appropriate stream for the information
        response = await self.event_broker.ask(
            action_type=action_type,
            payload=query
        )

        # treat the reply like a json object
        response_data = json.loads(response)

        # if something went wrong
        if 'errors' in response_data and response_data['errors']:
            # return an empty response
            raise ValueError(','.join(response_data['errors']))

        # grab the valid list of matches
        result = response_data['data'][root_query()]

        # grab the auth handler for the object
        auth_criteria = self.auth_criteria.get(object_name)

        # if we care about auth requirements and there is one for this object
        if obey_auth and auth_criteria:

            # build a second list of authorized entries
            authorized_results = []

            # for each query result
            for query_result in result:
                # create a graph entity for the model
                graph_entity = GraphEntity(self, model_type=object_name, id=query_result['pk'])
                # if the auth handler passes
                if await auth_criteria(model=graph_entity, user_id=current_user):
                    # add the result to the final list
                    authorized_results.append(query_result)

            # overwrite the query result
            result = authorized_results

        # apply the auth handler to the result
        return result
Esempio n. 24
0
    async def object_resolver(self,
                              object_name,
                              fields,
                              obey_auth=False,
                              current_user=None,
                              **filters):
        """
            This function resolves a given object in the remote backend services
        """

        try:
            # check if an object with that name has been registered
            registered = [model for model in self._external_service_data['models'] \
                                if model['name']==object_name][0]
        # if there is no connection data yet
        except AttributeError:
            raise ValueError("No objects are registered with this schema yet.")
        # if we dont recognize the model that was requested
        except IndexError:
            raise ValueError(
                "Cannot query for object {} on this service.".format(
                    object_name))

        # the valid fields for this object
        valid_fields = [field['name'] for field in registered['fields']]

        # figure out if any invalid fields were requested
        invalid_fields = [
            field for field in fields if field not in valid_fields
        ]
        try:
            # make sure we never treat pk as invalid
            invalid_fields.remove('pk')
        # if they weren't asking for pk as a field
        except ValueError:
            pass

        # if there were
        if invalid_fields:
            # yell loudly
            raise ValueError("Cannot query for fields {!r} on {}".format(
                invalid_fields, registered['name']))

        # make sure we include the id in the request
        fields.append('pk')

        # the query for model records
        query = query_for_model(fields, **filters)

        # the action type for the question
        action_type = get_crud_action('read', object_name)

        # query the appropriate stream for the information
        response = await self.event_broker.ask(action_type=action_type,
                                               payload=query)

        # treat the reply like a json object
        response_data = json.loads(response)

        # if something went wrong
        if 'errors' in response_data and response_data['errors']:
            # return an empty response
            raise ValueError(','.join(response_data['errors']))

        # grab the valid list of matches
        result = response_data['data'][root_query()]

        # grab the auth handler for the object
        auth_criteria = self.auth_criteria.get(object_name)

        # if we care about auth requirements and there is one for this object
        if obey_auth and auth_criteria:

            # build a second list of authorized entries
            authorized_results = []

            # for each query result
            for query_result in result:
                # create a graph entity for the model
                graph_entity = GraphEntity(self,
                                           model_type=object_name,
                                           id=query_result['pk'])
                # if the auth handler passes
                if await auth_criteria(model=graph_entity,
                                       user_id=current_user):
                    # add the result to the final list
                    authorized_results.append(query_result)

            # overwrite the query result
            result = authorized_results

        # apply the auth handler to the result
        return result
Esempio n. 25
0
    async def action_handler(service,
                             action_type,
                             payload,
                             props,
                             notify=True,
                             **kwds):
        # if the payload represents a new instance of `Model`
        if action_type == get_crud_action('update', name or Model):
            try:
                # the props of the message
                message_props = {}
                # if there was a correlation id in the request
                if 'correlation_id' in props:
                    # make sure it ends up in the reply
                    message_props['correlation_id'] = props['correlation_id']

                # grab the nam eof the primary key for the model
                pk_field = Model.primary_key()

                # make sure there is a primary key to id the model
                if not pk_field.name in payload:
                    # yell loudly
                    raise ValueError(
                        "Must specify the pk of the model when updating")

                # grab the matching model
                model = Model.select().where(
                    pk_field == payload[pk_field.name]).get()

                # remove the key from the payload
                payload.pop(pk_field.name, None)

                # for every key,value pair
                for key, value in payload.items():
                    # TODO: add protection for certain fields from being
                    # changed by the api
                    setattr(model, key, value)

                # save the updates
                model.save()

                # if we need to tell someone about what happened
                if notify:
                    # publish the scucess event
                    await service.event_broker.send(
                        payload=ModelSerializer().serialize(model),
                        action_type=change_action_status(
                            action_type, success_status()),
                        **message_props)

            # if something goes wrong
            except Exception as err:
                # if we need to tell someone about what happened
                if notify:
                    # publish the error as an event
                    await service.event_broker.send(
                        payload=str(err),
                        action_type=change_action_status(
                            action_type, error_status()),
                        **message_props)
                # otherwise we aren't supposed to notify
                else:
                    # raise the exception normally
                    raise err