예제 #1
0
def leases():
    """Filters and returns list of leases or units, this is local app data."""
    request = app.current_request
    user_id = request.context["authorizer"]["principalId"]
    db = DatabaseConnection()
    session = db.session()

    if request.method == "POST":
        try:
            lease_data = request.json_body
            new_lease = LeaseSchema().load(lease_data, session=session)
        except ValidationError as err:
            return {"success": False, "errors": err.messages}
        else:
            new_lease.user_id = user_id
            session.add(new_lease)
            session.commit()

    filters = {
        "fields": ["id", "bluemoon_id", "unit_number"],
        "default_page_size": 25,
        "default_order": "id",
        "default_dir": "desc",
    }
    filtering = ModelFilter(model=Lease,
                            filters=filters,
                            user_id=user_id,
                            params=request.query_params)
    results = filtering.results()
    schema = PaginatedLeaseSchema()
    return gzip_response(data=schema.dump(results), status_code=200)
예제 #2
0
def configuration(id):
    """Fetch the configuration for Bluemoon integration."""
    user_id = app.current_request.context["authorizer"]["principalId"]

    db = DatabaseConnection()
    session = db.session()
    query = session.query(Lease)
    lease = query.filter(Lease.id == id).filter(
        Lease.user_id == user_id).first()

    token = get_token(request=app.current_request)
    bm_api = BluemoonApi(token=token)

    # Configuration object for lease-editor. Passing in some basic data along with
    # a generated callback url
    configuration = {
        "apiUrl":
        bm_api.url,
        "propertyNumber":
        bm_api.property_number(),
        "accessToken":
        token,
        "view":
        "create",
        "callBack":
        "{}/lease/callback/{}".format(os.getenv("UNITS_URL_EXTERNAL"),
                                      lease.id),
        "leaseData": {
            "standard": {
                "address": "123 Super Dr.",
                "unit_number": lease.unit_number
            }
        },
    }
    return gzip_response(data=configuration, status_code=200)
예제 #3
0
 def results(self):
     db = DatabaseConnection()
     session = db.session()
     self.query = session.query(self.model)
     self.filtering()
     self.ordering()
     return self.paginate()
예제 #4
0
def lease_execute(id):
    """Execute using the lease_esignature_id as there could be more than one."""
    request = app.current_request
    user_id = request.context["authorizer"]["principalId"]

    # Validate the json request
    try:
        execute_data = ExecuteSchema().load(request.json_body)
    except ValidationError as err:
        return gzip_response(data={
            "success": False,
            "errors": err.messages
        },
                             status_code=200)

    db = DatabaseConnection()
    session = db.session()
    query = session.query(LeaseEsignature).join("lease")
    lease_esignature = (query.filter(LeaseEsignature.id == id).filter(
        Lease.user_id == user_id).first())
    # Make sure the corresponding item exists in the database
    if not lease_esignature:
        return gzip_response(data={"message": "Not Found"}, status_code=404)
    bm_api = BluemoonApi(token=get_token(request=request))

    # Update the status just in case we did not receive the latest data
    response = bm_api.esignature_details(bm_id=lease_esignature.bluemoon_id)
    if not response:
        return api_error_response()

    storage_data = response["data"]
    lease_esignature.data = storage_data
    try:
        signers_data = storage_data["esign"]["data"]["signers"]["data"]
        lease_esignature.transition_status(signers_data=signers_data)
    except KeyError:
        pass
    session.add(lease_esignature)
    session.commit()
    # End status update check

    # Verify we can execute the document
    if lease_esignature.status != StatusEnum.signed:
        data = {
            "success": False,
            "errors": [{
                "status": "Lease has not been signed by all residents."
            }],
        }
        return gzip_response(data=data, status_code=404)

    # Execute the document
    response = bm_api.execute_lease(bm_id=lease_esignature.bluemoon_id,
                                    data=execute_data)
    # TODO: Add url to Bluemoon response and return value.
    success = "executed" in response and response["executed"]

    return gzip_response(data={"success": success}, status_code=200)
예제 #5
0
def lease_print(id):
    """Print requires the lease_id as it just uses that data, no esignature request."""
    user_id = app.current_request.context["authorizer"]["principalId"]

    db = DatabaseConnection()
    session = db.session()
    query = session.query(Lease)
    lease = query.filter(Lease.id == id).filter(
        Lease.user_id == user_id).first()
    if not lease:
        return gzip_response(data={"message": "Not Found"}, status_code=404)
    if not lease.bluemoon_id:
        return gzip_response(data={"message": "Bluemoon Lease not created."},
                             status_code=200)

    data = app.current_request.json_body
    token = get_token(request=app.current_request)
    selected_forms = data.get("forms")
    try:
        forms = forms_mapper(selected_forms=selected_forms, token=token)
    except MissingLeaseFormsException:
        return api_error_response()

    post_data = {"lease_id": lease.bluemoon_id, "data": forms}
    bm_api = BluemoonApi(token=token)

    response = bm_api.post_raw(path="lease/generate/pdf", data=post_data)
    content_type = response.headers.get("Content-Type")
    context = {}

    if content_type == "application/pdf":
        length = 0
        mem = io.BytesIO()
        for chunk in response.iter_content(chunk_size=128):
            length += len(chunk)
            mem.write(chunk)
        mem.seek(0)

        now = datetime.datetime.now()
        file_name = "{0:%d}/{0:%m}/{1}.pdf".format(now, uuid.uuid4().hex)
        s3_client.upload_fileobj(mem, BUCKET, file_name)
        signed_url = s3_client.generate_presigned_url(
            ClientMethod="get_object",
            Params={
                "Bucket": BUCKET,
                "Key": file_name
            },
            ExpiresIn=3600,
        )
        context["success"] = True
        context["url"] = signed_url
    elif content_type == "application/json":
        context.update(response.json())

    return gzip_response(data=context, status_code=200)
예제 #6
0
def fetch_esignature_document(id):
    """Fetch the complete lease document with receipt"""
    app.log.debug("here")
    user_id = app.current_request.context["authorizer"]["principalId"]
    token = get_token(request=app.current_request)

    db = DatabaseConnection()
    session = db.session()
    query = session.query(LeaseEsignature).join("lease")
    lease_esignature = (query.filter(LeaseEsignature.id == id).filter(
        Lease.user_id == user_id).first())
    if not lease_esignature:
        return gzip_response(data={"message": "Not Found"}, status_code=404)

    bm_api = BluemoonApi(token=token)
    response = bm_api.get_raw(
        path="esignature/lease/pdf/{}".format(lease_esignature.bluemoon_id))
    content_type = response.headers.get("Content-Type")
    context = {}

    if content_type == "application/pdf":
        length = 0
        mem = io.BytesIO()
        for chunk in response.iter_content(chunk_size=128):
            length += len(chunk)
            mem.write(chunk)
        mem.seek(0)

        now = datetime.datetime.now()
        file_name = "{0:%d}/{0:%m}/{1}.pdf".format(now, uuid.uuid4().hex)
        s3_client.upload_fileobj(mem, BUCKET, file_name)
        signed_url = s3_client.generate_presigned_url(
            ClientMethod="get_object",
            Params={
                "Bucket": BUCKET,
                "Key": file_name
            },
            ExpiresIn=3600,
        )
        context["success"] = True
        context["url"] = signed_url
    elif content_type == "application/json":
        context.update(response.json())

    return gzip_response(data=context, status_code=200)
예제 #7
0
def lease_request_esign(id):
    request = app.current_request
    user_id = request.context["authorizer"]["principalId"]

    db = DatabaseConnection()
    session = db.session()
    query = session.query(Lease)
    lease = query.filter(Lease.id == id).filter(
        Lease.user_id == user_id).first()
    if not lease:
        return gzip_response(data={"message": "Not Found"}, status_code=404)

    data = app.current_request.json_body
    token = get_token(request=app.current_request)
    selected_forms = data.get("forms")
    try:
        forms = forms_mapper(selected_forms=selected_forms, token=token)
    except MissingLeaseFormsException:
        return api_error_response()

    bm_api = BluemoonApi(token=token)
    post_data = {
        "lease_id": lease.bluemoon_id,
        "external_id": lease.id,
        "send_notifications": True,
        "notification_url": "{}/{}".format(os.getenv("UNITS_URL"),
                                           "notifications"),
        "data": forms,
    }
    response = bm_api.request_esignature(data=post_data)
    if not response.get("success"):
        return gzip_response(data=response, status_code=200)

    lease_esignature = LeaseEsignature(
        lease_id=lease.id,
        bluemoon_id=response["data"]["id"],
        data=response["data"]["data"],
    )
    session.add(lease_esignature)
    session.commit()
    return gzip_response(data=LeaseEsignatureSchema().dump(lease_esignature),
                         status_code=201)
예제 #8
0
def lease_callback(id):
    """Fetches lease and handles the callback."""
    # This endpoint receives the AJAX request from the lease-editor
    request = app.current_request

    db = DatabaseConnection()
    session = db.session()
    query = session.query(Lease)
    lease = query.filter(Lease.id == id).first()
    if not lease:
        return gzip_response(data={"message": "Not Found"}, status_code=404)

    # The request body contains the full lease object but I only care about
    # the lease id
    if request.method == "POST" and "id" in request.json_body:
        lease.bluemoon_id = request.json_body["id"]
        session.add(lease)
        session.commit()

    return gzip_response(data=LeaseSchema().dump(lease), status_code=200)
예제 #9
0
def lease(id):
    """Fetches lease unit and handles updates."""
    request = app.current_request
    user_id = request.context["authorizer"]["principalId"]

    db = DatabaseConnection()
    session = db.session()
    query = session.query(Lease)
    lease = query.filter(Lease.id == id).filter(
        Lease.user_id == user_id).first()
    if not lease:
        return gzip_response(data={"message": "Not Found"}, status_code=404)

    if request.method == "POST" and "id" in request.json_body:
        # Currently there is no update available
        app.log.info("update lease")
        # session.add(lease)
        # session.commit()

    return gzip_response(data=LeaseSchema().dump(lease), status_code=200)
예제 #10
0
def notifications():
    """Lease Esignature Requests notifications from Bluemoon."""
    data = app.current_request.json_body
    # Verify the data fits the minimum standars
    if "id" not in data:
        return gzip_response(data={"message": "Bad Request"}, status_code=405)

    db = DatabaseConnection()
    session = db.session()
    query = session.query(LeaseEsignature)
    lease_esignature = query.filter(
        LeaseEsignature.bluemoon_id == data["id"]).first()

    lease_esignature.data = data
    try:
        # The data is a bit nested, but ideally Bluemoon API will pass a document status soon
        signers_data = data["esign"]["data"]["signers"]["data"]
        lease_esignature.transition_status(signers_data=signers_data)
    except KeyError:
        pass
    session.add(lease_esignature)
    session.commit()
    return {"success": True}
예제 #11
0
 def __init__(self):
     self.url = "{}{}".format(os.getenv("API_URL"), "/oauth/token")
     self.client_id = os.getenv("OAUTH_CLIENT_ID")
     self.client_secret = os.getenv("OAUTH_CLIENT_SECRET")
     self.db = DatabaseConnection()
     self.user = None
예제 #12
0
class BluemoonAuthorization(object):
    def __init__(self):
        self.url = "{}{}".format(os.getenv("API_URL"), "/oauth/token")
        self.client_id = os.getenv("OAUTH_CLIENT_ID")
        self.client_secret = os.getenv("OAUTH_CLIENT_SECRET")
        self.db = DatabaseConnection()
        self.user = None

    def user_by_id(self, user_id):
        session = self.db.session()
        query = session.query(User)
        user = query.filter(User.id == user_id).first()
        return user

    def user_by_token(self, token):
        session = self.db.session()
        query = session.query(User)
        user = query.filter(User.access_token == token).first()
        return user

    def manage_user(self, data, username):
        """Create oauthed user."""
        # I am basically creating my user based on the Bluemoon data
        # typically you would have your application user and a single bluemoon api user
        session = self.db.session()
        query = session.query(User)
        user = query.filter(User.username == username).first()
        now = datetime.datetime.now()
        expires = now + datetime.timedelta(seconds=data["expires_in"])
        if not user:
            user = User(
                username=username,
                access_token=data["access_token"],
                refresh_token=data["refresh_token"],
                expires=expires,
            )
            session.add(user)
        else:
            user.access_token = data["access_token"]
            user.refresh_token = data["refresh_token"]
            user.expires = expires
        session.commit()
        self.user = user

    def authenticate(self, username, password):
        payload = {
            "username": username,
            "password": password,
            "grant_type": "password",
            "client_id": self.client_id,
            "client_secret": self.client_secret,
        }
        # It is important to always pass the accept and content-type json headers for a post
        response = requests.post(
            self.url,
            headers={
                "Accept": "application/json",
                "Content-Type": "application/json"
            },
            json=payload,
        )
        data = response.json()
        if response.status_code == 200 and "access_token" in data:
            self.manage_user(data=data, username=username)
            return {"success": True}

        return data

    def check_authorization(self, token):
        # Verifying the token has not expired.
        user = self.user_by_token(token=token)
        if not user:
            return False
        now = datetime.datetime.now()
        if now < user.expires:
            return user
        return False

    def logout(self, token):
        bm_api = BluemoonApi(token)
        # Revoke the token in the API.
        results = bm_api.logout()
        if results["success"]:
            user = self.user_by_token(token)
            # This basicall revokes the token locally
            data = {"access_token": "", "refresh_token": "", "expires_in": 0}
            self.manage_user(data=data, username=user.username)
            return True