def test_export_status(mock_env_access_key, client):
    # pylint: disable=unused-argument
    """
        test the export status endpoint
    """
    # clear up entries in db
    finish_submissions_exports()

    # no export id
    response = client.simulate_get('/export/status')
    assert response.status_code == 500

    # nonexisting export id
    guid = uuid.uuid4()
    response = client.simulate_get('/export/status?export_id=' + str(guid))
    assert response.status_code == 500

    # invalid export id
    response = client.simulate_get('/export/status?export_id=123')
    assert response.status_code == 500

    # export in progress
    export_obj = create_export(db, BLUEBEAM_USERNAME)
    response = client.simulate_get('/export/status?export_id=' +
                                   str(export_obj.guid))
    assert response.status_code == 200
    response_json = json.loads(response.text)
    assert not response_json['data']['is_finished']

    # clear out the queue
    queue.control.purge()
def test_export_task_resubmission_no_project(mock_env_access_key):
    # pylint: disable=unused-argument
    """Test the export resubmission task but project isn't found in bluebeam"""
    # don't include previous submission
    finish_submissions_exports()
    # create a resubmission so there's something to export
    create_submission(db, mocks.RESUBMISSION_POST_DATA)
    # create the export
    export_obj = create_export(db, BLUEBEAM_USERNAME)
    # mock all responses for expected requests
    with patch('service.resources.bluebeam.requests.request') as mock_reqs:
        fake_responses = []
        # project exists
        fake_responses.append(Mock())
        fake_responses[0].status_code = 404

        mock_reqs.side_effect = fake_responses

        bluebeam_export.s(export_obj=export_obj,
                          access_code=BLUEBEAM_ACCESS_CODE).apply()

        db.refresh(export_obj)

        assert export_obj.date_finished is not None
        assert len(export_obj.result['success']) == 0
        assert len(export_obj.result['failure']) > 0

    # clear out the queue
    queue.control.purge()
def test_export_task_new_project(mock_env_access_key):
    # pylint: disable=unused-argument
    """Test the export task"""
    # don't include previous submission
    finish_submissions_exports()
    # create a submission so there's something to export
    data = mocks.SUBMISSION_POST_DATA.copy()
    data['logger'] = mocks.LOGGER_PARAMETERS.copy()
    data['_id'] = "123"
    create_submission(db, data)
    # create the export
    export_obj = create_export(db, BLUEBEAM_USERNAME)
    # mock all responses for expected requests
    with patch('service.resources.bluebeam.requests.request') as mock_post:
        fake_post_responses = []
        # create project
        fake_post_responses.append(Mock())
        fake_post_responses[
            0].json.return_value = mocks.CREATE_PROJECT_RESPONSE
        fake_post_responses[0].status_code = 200
        # create folders
        i = 1
        while i < 7:
            fake_post_responses.append(Mock())
            fake_post_responses[
                i].json.return_value = mocks.CREATE_FOLDER_RESPONSE
            i += 1
        # get folders
        fake_post_responses.append(Mock())
        fake_post_responses[7].json.return_value = mocks.GET_FOLDERS_RESPONSE
        # create folders
        fake_post_responses.append(Mock())
        fake_post_responses[8].json.return_value = mocks.CREATE_FOLDER_RESPONSE
        # initiate upload
        fake_post_responses.append(Mock())
        fake_post_responses[
            9].json.return_value = mocks.INIT_FILE_UPLOAD_RESPONSE
        # upload
        fake_post_responses.append(Mock())
        fake_post_responses[10].return_value.status_code = 200
        # confirm upload
        fake_post_responses.append(Mock())
        fake_post_responses[11].status_code = 204

        mock_post.side_effect = fake_post_responses

        with patch('tasks.requests.patch') as mock_patch:
            mock_patch.status_code = 200

            bluebeam_export.s(export_obj=export_obj,
                              access_code=BLUEBEAM_ACCESS_CODE).apply()

            db.refresh(export_obj)

        assert export_obj.date_finished is not None
        assert len(export_obj.result['success']) > 0
        assert len(export_obj.result['failure']) == 0

    # clear out the queue
    queue.control.purge()
def test_export_task_resubmission_log_status_error(mock_env_access_key):
    # pylint: disable=unused-argument
    """Test the export resubmission task with an error when logging status"""
    # don't include previous submission
    finish_submissions_exports()
    # create a resubmission so there's something to export
    data = mocks.RESUBMISSION_POST_DATA.copy()
    data['logger'] = mocks.LOGGER_PARAMETERS.copy()
    data['_id'] = "123"
    create_submission(db, data)

    # create the export
    export_obj = create_export(db, BLUEBEAM_USERNAME)
    # mock all responses for expected requests
    with patch('service.resources.bluebeam.requests.request') as mock_reqs:
        fake_responses = []
        # project exists
        fake_responses.append(Mock())
        fake_responses[0].status_code = 200
        # get folders
        fake_responses.append(Mock())
        fake_responses[1].json.return_value = mocks.GET_FOLDERS_RESPONSE
        # get folders
        fake_responses.append(Mock())
        fake_responses[2].json.return_value = mocks.GET_FOLDERS_RESPONSE
        # create folders
        fake_responses.append(Mock())
        fake_responses[3].json.return_value = mocks.CREATE_FOLDER_RESPONSE
        # initiate upload
        fake_responses.append(Mock())
        fake_responses[4].json.return_value = mocks.INIT_FILE_UPLOAD_RESPONSE
        # upload
        fake_responses.append(Mock())
        fake_responses[5].return_value.status_code = 200
        # confirm upload
        fake_responses.append(Mock())
        fake_responses[6].status_code = 204

        mock_reqs.side_effect = fake_responses

        with patch('tasks.requests.patch') as mock_patch:
            mock_patch.side_effect = Exception("Patch error")

            bluebeam_export.s(export_obj=export_obj,
                              access_code=BLUEBEAM_ACCESS_CODE).apply()

            db.refresh(export_obj)

        assert len(export_obj.result['failure']) > 0

    # clear out the queue
    queue.control.purge()
def finish_submissions_exports():
    """
        sets the date_exported on all existing submissions and
        date_finished on all export_status in the database
    """
    export_obj = create_export(db, BLUEBEAM_USERNAME)

    with db_engine.connect() as con:
        sql = "UPDATE submission SET date_exported=now() at time zone 'utc', " +\
            "export_guid='" + str(export_obj.guid) + "' " +\
            "WHERE date_exported IS NULL"
        con.execute(sql)

        sql = "UPDATE export_status set date_finished=now() at time zone 'utc' " +\
            "WHERE date_finished IS NULL"
        con.execute(sql)
def test_export_task_no_upload_folder(mock_env_access_key):
    # pylint: disable=unused-argument
    """Test the export task when there is no dir set as the uploads dir"""
    # don't include previous submission
    finish_submissions_exports()
    # create a submission so there's something to export
    create_submission(db, mocks.SUBMISSION_POST_DATA)
    # create the export
    export_obj = create_export(db, BLUEBEAM_USERNAME)
    # mock all responses for expected outbound requests
    with patch('service.resources.bluebeam.requests.request') as mock_post:
        fake_post_responses = []
        # create project
        fake_post_responses.append(Mock())
        fake_post_responses[
            0].json.return_value = mocks.CREATE_PROJECT_RESPONSE
        fake_post_responses[0].status_code = 200
        # create folders
        i = 1
        while i < 8:
            fake_post_responses.append(Mock())
            fake_post_responses[
                i].json.return_value = mocks.CREATE_FOLDER_RESPONSE
            i += 1
        # delete project
        fake_post_responses.append(Mock())
        fake_post_responses[8].status_code = 204

        mock_post.side_effect = fake_post_responses

        with patch('service.resources.bluebeam.DIRECTORY_STRUCTURE'
                   ) as mock_dir_structure:
            mock_dir_structure.return_value = [{"name": "CCSF EPR"}]

            bluebeam_export.s(export_obj=export_obj,
                              access_code=BLUEBEAM_ACCESS_CODE).apply()

            db.refresh(export_obj)
            assert export_obj.date_finished is not None
            assert len(export_obj.result['failure']) > 0
            assert export_obj.result['failure'][-1]['err'] == ERR_UPLOAD_FAIL

    # clear out the queue
    queue.control.purge()
def test_export_task_resubmission_no_upload_dir(mock_env_access_key):
    # pylint: disable=unused-argument
    """
        Test the export resubmission task when cannot find upload dir
        in preexisting bluebeam project
    """
    # don't include previous submission
    finish_submissions_exports()
    # create a resubmission so there's something to export
    create_submission(db, mocks.RESUBMISSION_POST_DATA)
    # create the export
    export_obj = create_export(db, BLUEBEAM_USERNAME)
    # mock all responses for expected requests
    with patch('service.resources.bluebeam.requests.request') as mock_reqs:
        fake_responses = []
        # project exists
        fake_responses.append(Mock())
        fake_responses[0].status_code = 200
        # get folders
        fake_responses.append(Mock())
        fake_responses[
            1].json.return_value = mocks.GET_FOLDERS_RESPONSE_NO_UPLOAD

        mock_reqs.side_effect = fake_responses

        bluebeam_export.s(export_obj=export_obj,
                          access_code=BLUEBEAM_ACCESS_CODE).apply()

        db.refresh(export_obj)

        assert export_obj.date_finished is not None
        assert len(export_obj.result['success']) == 0
        assert len(export_obj.result['failure']) > 0

    # clear out the queue
    queue.control.purge()
    def on_get(self, _req, resp):
        """ handle export get requests """
        resp.content_type = falcon.MEDIA_HTML
        if 'code' in _req.params:
            # handle redirect back from auth server
            export_obj = None
            try:
                # convert auth code to access token
                redirect_uri = self.create_redirect_url(
                    _req.forwarded_scheme, _req.forwarded_host, _req.port)
                access_response = bluebeam.get_access_token_response(
                    _req.params['code'], redirect_uri)

                # happy path
                # show spinner ui which will poll for export status
                export_obj = create_export(self.session,
                                           access_response['userName'])  # pylint: disable=no-member

                resp.status = falcon.HTTP_200
                template = Template(filename='templates/exporting.html')
                resp.body = template.render(export_id=export_obj.guid)

                bluebeam_export.apply_async(
                    (export_obj, access_response['access_token']),
                    serializer='pickle',
                )
            except Exception as err:  # pylint: disable=broad-except
                # error in scheduling
                err_string = "{0}".format(err)

                # finish this export
                if export_obj is not None:
                    export_obj.date_finished = datetime.utcnow()
                    export_obj.result = err_string
                    self.session.commit()  # pylint: disable=no-member

                # present error ui
                print("error:")
                print(err_string)
                resp.status = falcon.HTTP_500
                template = Template(filename='templates/exporting_error.html')
                resp.body = template.render(error_message=err_string)
        else:
            resp.status = falcon.HTTP_200

            # is there an export in progress?
            exports = self.session.query(ExportStatusModel).filter(  # pylint: disable=no-member
                ExportStatusModel.date_finished.is_(None))

            if exports.count() == 0:
                # is there something to export?
                submissions = self.session.query(SubmissionModel).filter(  # pylint: disable=no-member
                    SubmissionModel.date_exported.is_(None))

                if submissions.count() > 0:
                    # present ui to redirect to bluebeam for auth
                    bluebeam_auth_url = create_url(
                        bluebeam.BLUEBEAM_AUTHSERVER,
                        bluebeam.BLUEBEAM_AUTH_PATH, {
                            'response_type':
                            'code',
                            'client_id':
                            bluebeam.BLUEBEAM_CLIENT_ID,
                            'scope':
                            'full_user',
                            'redirect_uri':
                            self.create_redirect_url(_req.forwarded_scheme,
                                                     _req.forwarded_host,
                                                     _req.port)
                        })
                    template = Template(filename='templates/export.html')
                    resp.body = template.render(
                        bluebeam_auth_url=bluebeam_auth_url)
                else:
                    # nothing to export
                    template = Template(
                        filename='templates/nothing_to_export.html')
                    resp.body = template.render()
            else:
                export_obj = exports.first()
                template = Template(filename='templates/exporting.html')
                resp.body = template.render(export_id=export_obj.guid)