示例#1
0
class BookingService:
    name = "booking_service"

    db = Database(Base)
    dispatcher = EventDispatcher()

    @http("POST", "/restaurants/<int:restaurant_id>")
    def book_table(self, request: Request, restaurant_id: int) -> Response:
        request_params = json.loads(request.data)
        command = BookTableCommand(restaurant_id=restaurant_id,
                                   persons=request_params["persons"])
        booking_table_service = BookingTableApplicationService(
            SQLAlchemyRestaurantRepository(self.db.session),
            SQLAlchemyUnitOfWork(self.db.session),
            NamekoEventPublisher(self.dispatcher),
        )
        booking_table_service.book_table(command)
        return Response(f"Restaurant: {restaurant_id} table booked")

    @http("GET", "/up")
    def up(self, request: Request) -> Response:
        return Response("I'm alive")

    @http("GET", "/restaurants")
    def restaurants(self, request: Request) -> Response:
        repo = SQLAlchemyRestaurantRepository(self.db.session)
        return Response(
            json.dumps([restaurant_serializer(r) for r in repo.all()]))
示例#2
0
class LoginService:
    name = "auth_service"
    secret = "aYoXW26E7w3wiVOq4TnHGEkx0OB4cdHx"

    db = Database(Base)

    @staticmethod
    def create_token():
        arr = bytearray(32)
        for i in range(0, 32):
            arr[i] = random.randint(0, 255)
        return base64.encodebytes(arr).decode().rstrip()

    @rpc
    @timeout(3)
    def login(self, username, password):
        try:
            rec = self.db.get_session().query(User).filter(
                User.username == username).one()
            if password == rec.password:
                payload = {"userId": rec.userid, "token": self.create_token()}
                return {
                    "access_token":
                    jwt.encode(payload, self.secret,
                               algorithm="HS256").decode().rstrip()
                }
            else:
                raise NoResultFound()
        except NoResultFound:
            return {"error": {"code": 403, "message": "Invalid credentials"}}
示例#3
0
class ProductsService:
    name = "products"

    db = Database(DeclarativeBase)

    @http("POST", "/products/")
    def create_product(self, request):
        payload = json.loads(request.get_data(as_text=True))
        product = Product(**payload)
        with self.db.get_session() as session:
            session.add(product)

        return json.dumps({"id": product.id, "name": product.name})
示例#4
0
    class ExampleService(object):
        name = 'exampleservice'

        db = Database(DeclarativeBase)

        @dummy
        def write(self, id_, name):
            obj = User(id=id_, name=name)
            self.db.session.add(obj)
            self.db.session.commit()

        @dummy
        def read(self, id_):
            return self.db.session.query(User).get(id_).name
示例#5
0
class ProjectService:
    name = 'project'

    config = Config()
    database = Database(DeclarativeBase)

    @rpc
    def get(self, project):
        logger.debug(project)

        _project = None
        with self.database.get_session() as session:
            _project = session.query(Project).get(project)
            if _project is None:
                raise NotFound('{} not found'.format(project))

        return ProjectSchema().dump(_project)
    class ExampleServiceWithDatabase:
        name = 'exampleservice'

        db = Database(DeclBase)

        @dummy
        def create_record(self):
            with self.db.get_session() as session:
                session.add(ExampleModel(data='hello'))

        @dummy
        @transaction_retry
        def get_record_count(self):
            with self.db.get_session() as session:
                return session.query(ExampleModel).count()

        @dummy
        def get_record_count_retry_inside(self):
            with self.db.get_session() as session:

                @transaction_retry(session=session)
                def foo():
                    return session.query(ExampleModel).count()

                return foo()

        @dummy
        def get_record_count_no_retry(self):
            with self.db.get_session() as session:
                return session.query(ExampleModel).count()

        @dummy
        @transaction_retry
        def create_without_context_manager(self):
            session = self.db.get_session()
            session.add(ExampleModel(data='created without context manager'))
            session.commit()

        @dummy
        @transaction_retry(session=operator.attrgetter('db.session'))
        def create_with_worker_scoped_session(self):
            self.db.session.add(ExampleModel(data='created in worker scope'))
            self.db.session.commit()
示例#7
0
class ContactsService:

    name = 'contacts'

    db = Database(DeclarativeBase)
    dispatch = EventDispatcher()

    @rpc
    def get_contact(self, id_):
        with self.db.get_session() as session:
            contact = session.query(Contact).get(id_)
            return contact.to_dict()

    @rpc
    def create_contact(self, data):
        with self.db.get_session() as session:
            contact = Contact(**data)
            session.add(contact)

        self.dispatch('contact_created', {'contact': contact.to_dict()})

        return contact.to_dict()
示例#8
0
class LoginService:
    name = "auth_service"
    secret = settings["JWT_SECRET"]

    db = Database(Base)
    sentry = SentryReporter()

    @staticmethod
    def create_token():
        arr = bytearray(32)
        for i in range(0, 32):
            arr[i] = random.randint(0, 255)
        return base64.encodebytes(arr).decode().rstrip()

    @rpc(expected_exceptions=InvalidCredentials)
    @timeout(3)
    def login(self, username, password):
        try:
            rec = self.db.get_session().query(User).filter(
                User.username == username).one()
            if password == rec.password:
                payload = {"userId": rec.userid, "token": self.create_token()}
                return {
                    "access_token":
                    jwt.encode(payload, self.secret,
                               algorithm="HS256").decode().rstrip()
                }
            else:
                raise InvalidCredentials("Wrong password")
        except NoResultFound:
            raise InvalidCredentials("User not found")

    @event_handler("heartbeat_service",
                   "heartbeat",
                   handler_type=BROADCAST,
                   reliable_delivery=False)
    def on_heartbeat(self, ts):
        print("Received Heartbeat with timestamp {}".format(ts), flush=True)
示例#9
0
class TodoService:
    """
    Service class that is instantiated by nameko to run the todo api
    """
    name = "TodoService"

    db = Database(DeclBase)

    @http('POST', '/todo/add/')
    @transaction_retry()
    def add(self, request):
        """
        Method for adding a new todo to the database via POST request

        Example request ::

            {
                "name": "EnglishLesson",
                "date": "2017-01-12 12:00:00"
             }


        The response contains the posted data with the parsed date and an added id column ::

            {
                "id" : 1,
                "name": "EnglishLesson",
                "date": "2017-01-12T12:00:00+00:00"
            }
        """

        dict = json.loads(request.get_data(as_text=True))

        try:
            #Get the name and date values from request. This might raise KeyError
            name = dict['name']
            date = dict['date']

            #If date value is a string try converting to datetime. This might raise Value Error
            if isinstance(date, str):
                date = parse(date)

            #Create SQL Alchemy Todo Object and add new entry to the database
            obj = Todo(name=name, date=date)
            schema = TodoSchema()
            session = self.db.get_session()
            session.add(obj)
            session.commit()

            #Prepare response data using marshmallow schema
            response = json.dumps(schema.dump(obj).data)
            session.close()
            return 201, response

        # Handle errors resulting from invalid data in the request
        except (ValueError, KeyError):
            return 400, 'Bad Request'

    @http('GET', '/todo/delete/<int:id>')
    @transaction_retry()
    def delete(self, request, id):
        """
        Method for deleting a single todo referenced by the id

        Example request ::

            /todo/delete/1


        The response contains data which was deleted from the database ::

            {
                "id" : 1,
                "name": "EnglishLesson",
                "date": "2017-01-12T12:00:00+00:00"
            }
        """
        schema = TodoSchema()
        session = self.db.get_session()

        #Get data for response
        obj = session.query(Todo).filter(Todo.id == id).first()
        response = json.dumps(schema.dump(obj).data)

        #Delete entry from DB
        session.query(Todo).filter(Todo.id == id).delete()
        session.commit()
        session.close()
        return response

    @http('GET', '/todo/get/<int:id>')
    @transaction_retry()
    def get(self, request, id):
        """
        Method retrieving a single todo referenced with its id

        Example request ::

            /todo/get/1


        The response contains data which was deleted from the database ::

            {
                "id" : 1,
                "name": "EnglishLesson",
                "date": "2017-01-12T12:00:00+00:00"
            }

        """
        session = self.db.get_session()
        results = session.query(Todo).filter(Todo.id == id).first()
        session.close()
        schema = TodoSchema()
        return json.dumps(schema.dump(results).data)

    @http('GET', '/todo/list/')
    @transaction_retry()
    def list(self, request):
        """
        Returns list of all todos in the database

        Example request ::

            /todo/list


        The response contains all todos that are saved in the database::

            [
                {
                    "id" : 1,
                    "name": "EnglishLesson",
                    "date": "2017-01-12T12:00:00+00:00"
                },
                {
                    "id" : 2,
                    "name": "FrenchLesson",
                    "date": "2017-01-13T13:00:00+00:00"
                }
            ]

        """
        session = self.db.get_session()
        results = session.query(Todo).all()
        session.close()
        schema = TodoSchema(many=True)
        return json.dumps(schema.dump(results).data)
示例#10
0
class UserStats:
    """    User stats  service  """

    name = "userstats"
    config = Config()
    db = Database(DeclarativeBase)

    @consume("user_event", group_id="user_event")
    def consume_user_event(self, new_user_event: bytes):
        """ Subcribe user_event topic and update database """
        user = self._deserialise_message(message=new_user_event.value)
        record_id = f'{new_user_event.topic}-{new_user_event.offset}-{new_user_event.timestamp}'
        self._insert_new_user(record_id, user)

    @rpc
    def report(self):
        """ Return user count by city """
        return self._get_report()

    @timer(interval=REPORT_PERIOD)
    def email_report(self):
        """ Email will be sent only if SEND_EMAIL false """
        report = self._get_report()
        to = ['*****@*****.**']
        subject = 'report'
        content = str(report)

        if not SEND_EMAIL:
            logging.info(
                f"New report generated. Fake email sent. to: {to}, subject: {subject}, content: {content}"
            )
            return

        yag = yagmail.SMTP(EMAIL_SOURCE, EMAIL_PASSWORD)
        yag.send(cc=to.encode('utf-8'),
                 subject=subject.encode('utf-8'),
                 contents=content.encode('utf-8'))
        logging.info(
            f"Email sent. to: {to}, subject: {subject}, content: {content}")

    def _deserialise_message(self, message: bytes) -> dict:
        return json.loads(message.decode('utf-8'))

    def _insert_new_user(self, record_id: str, user: dict):
        user_db_record = User(seq_id=record_id,
                              user_id=user['id'],
                              city=user.get('address', {}).get('city', ''),
                              state=user.get('address', {}).get('state', ''),
                              country=user.get('address',
                                               {}).get('country', ''),
                              post_code=user.get('address',
                                                 {}).get('postCode', ''),
                              datetime=user.get('datetime', ''))

        with self.db.get_session() as session:
            try:
                session.add(user_db_record)
                session.commit()
            except IntegrityError:
                logging.info('Duplicated event')

        logging.info(f'New user inserted: {user}')

    def _get_user_count_by_city(self):
        with self.db.get_session() as session:
            result = (session \
                .query(
                    User.city,
                    func.count(User.user_id.distinct()).label('n_count'))
                .group_by(User.city)
                .order_by('n_count')).all()
        return dict(result)

    def _get_report(self):
        report_datetime = datetime.datetime.now()
        report = {
            'timestamp': str(report_datetime),
            'stats': self._get_user_count_by_city()
        }

        return report
示例#11
0
class AppointmentsService:
    """
    This Service is responsible for the management of appointments.
    """

    name = 'appointments'
    # api = OpenApi('appointments.yaml')

    db = Database(DeclarativeBase)
    dispatch = EventDispatcher()
    #statsd = StatsD('prod')
    tracer = Tracer()

    # @api.operation('get_appointment')
    # @statsd.timer('get_appointment')
    @http("GET", "/appointments/<int:appointment_id>")
    def get_appointment(self, request, appointment_id):
        """
        Returns all Information for the reqeusted Appointment
        :param request: http request
        :param appointment_id: the ID of a Appointment
        :return: 200 Details for the requested Appointment
        :return: 404 No Appointment for given ID
        :return: 500 Connection Error to Database 
        """
        try:
            appointment = self.db.session.query(Appointment).get(
                appointment_id)

            formatted_appointment = "\n\n"
            formatted_appointment += "Name: " + appointment.customer_name + "\nTreatment ID: " + str(
                appointment.treatment_id
            ) + "\nTreatment Name: " + appointment.treatment_name + "\nDate: " + appointment.date.strftime(
                "%d.%m.%Y") + "\nTime: " + str(
                    appointment.start_time) + ":00 - " + str(
                        appointment.end_time) + ":00\n\n"

            return 200, u"\nAppointment: {}".format(formatted_appointment)
        except AttributeError:
            return 404, "No Treatment exists for the given ID.\n"
        except exc.SQLAlchemyError:
            return 500, "Could not read Appointment.\n"

    # @api.operation('get_appointments')
    # @statsd.timer('get_appointments')
    @http("GET", "/appointments/list")
    def get_appointments(self, request):
        """
        Shows a list of all settled Appointments
        :param request: http request
        :return: 200 List of all booked Appointments
        :return: 404 No stored Appointments 
        :return: 500 Connection Error to Database 
        """
        try:
            appointments = self.db.session.query(Appointment).all()

            formatted_appointments = "\n\n"

            for appointment in appointments:
                formatted_appointments += "Name: " + appointment.customer_name + "\nTreatment ID: " + str(
                    appointment.treatment_id
                ) + "\nTreatment Name: " + appointment.treatment_name + "\nDate: " + appointment.date.strftime(
                    "%d.%m.%Y") + "\nTime: " + str(
                        appointment.start_time) + ":00 - " + str(
                            appointment.end_time) + ":00\n\n"

            if formatted_appointments == "\n\n":
                return 404, "No Appointments exist.\n"
            else:
                return 200, u"\nAppointment: {}".format(formatted_appointments)

        except exc.SQLAlchemyError:
            return 500, "Could not read Appointments.\n"

    # @api.operation('create_appointment')
    # @statsd.timer('create_appointment')
    @http("POST", "/appointments/")
    def create_appointment(self, request):
        """
        Saves a new Appointment
        :param request: new Appointment that should be saved
        :return: 201 Detailed information for newly created Appointment
        :return: 404 No Treatment for given TreatmentID 
        :return: 409 Appointment Details do not match the requirements
        :return: 500 Connection Error  
        """

        appointment_detail = json.loads(request.get_data())

        if appointment_detail['start_time'] >= appointment_detail['end_time']:
            return 409, "Start time has to be before end time.\n"
        elif appointment_detail['start_time'] < 8 or appointment_detail[
                'start_time'] > 17 or appointment_detail[
                    'end_time'] < 9 or appointment_detail['end_time'] > 18:
            return 409, "Appointments are only available from 8 - 18. Please choose another timeslot.\n"

        try:
            URL = "http://" + config.get(
                'TREATMENTS_SERVICE') + "/treatments/" + str(
                    appointment_detail['treatment_id'])
            t_response = requests.get(url=URL)
            t_response.encoding = 'utf-8'
            treatment_data = literal_eval(t_response.text)
        except SyntaxError:
            return 404, "No Treatment exists for given TreatmentID.\n"
        except requests.exceptions.ConnectionError:
            return 500, "Could not read Treatments.\n"

        if treatment_data['minduration'] > (appointment_detail['end_time'] -
                                            appointment_detail['start_time']):
            return 409, "An appointment for " + treatment_data[
                'name'] + " takes at least " + str(
                    treatment_data['minduration']
                ) + " hour(s). Please choose another timeslot.\n"
        elif treatment_data['maxduration'] < (
                appointment_detail['end_time'] -
                appointment_detail['start_time']):
            return 409, "An appointment for " + treatment_data[
                'name'] + " takes maximum " + str(
                    treatment_data['maxduration']
                ) + " hour(s). Please choose another timeslot.\n"

        try:
            conflicts = 0
            appointments = self.db.session.query(Appointment).all()
            for appointment in appointments:
                if treatment_data['name'] == appointment.treatment_name:
                    if appointment_detail['date'] == appointment.date.strftime(
                            "%Y-%m-%d"):
                        if not (appointment_detail['start_time'] <=
                                appointment.start_time
                                and appointment_detail['end_time'] <=
                                appointment.start_time) and not (
                                    appointment.end_time <=
                                    appointment_detail['end_time']
                                    and appointment.end_time <=
                                    appointment_detail['start_time']):
                            conflicts += 1

            if conflicts == 0:
                newappointment = Appointment(
                    treatment_id=appointment_detail['treatment_id'],
                    treatment_name=treatment_data['name'],
                    customer_name=appointment_detail['customer_name'],
                    date=appointment_detail['date'],
                    start_time=appointment_detail['start_time'],
                    end_time=appointment_detail['end_time'],
                    duration=appointment_detail['end_time'] -
                    appointment_detail['start_time'])

                with self.db.get_session() as session:
                    session.add(newappointment)

                appointment_detail['treatment_name'] = treatment_data['name']
                self.dispatch("booked_appointment", appointment_detail)

                return 201, u"\nAppointment: {}".format(appointment_detail)
            else:
                return 409, "There were " + str(
                    conflicts
                ) + " conflicts with other appointments. Please choose a free timeslot.\n"

        except exc.SQLAlchemyError:
            return 500, "Could not save Appointment.\n"