예제 #1
0
def update_linked_app(app, user_id):
    if not app.domain_link:
        raise AppLinkError(_(
            'This project is not authorized to update from the master application. '
            'Please contact the maintainer of the master app if you believe this is a mistake. '
        ))
    try:
        master_version = app.get_master_version()
    except RemoteRequestError:
        raise AppLinkError(_(
            'Unable to pull latest master from remote CommCare HQ. Please try again later.'
        ))

    if master_version > app.version:
        try:
            latest_master_build = app.get_latest_master_release()
        except ActionNotPermitted:
            raise AppLinkError(_(
                'This project is not authorized to update from the master application. '
                'Please contact the maintainer of the master app if you believe this is a mistake. '
            ))
        except RemoteAuthError:
            raise AppLinkError(_(
                'Authentication failure attempting to pull latest master from remote CommCare HQ.'
                'Please verify your authentication details for the remote link are correct.'
            ))
        except RemoteRequestError:
            raise AppLinkError(_(
                'Unable to pull latest master from remote CommCare HQ. Please try again later.'
            ))

        report_map = get_static_report_mapping(latest_master_build.domain, app['domain'])

        try:
            app = overwrite_app(app, latest_master_build, report_map)
        except AppEditingError as e:
            raise AppLinkError(
                _(
                    'This application uses mobile UCRs '
                    'which are not available in the linked domain: {ucr_id}'
                ).format(ucr_id=str(e))
            )

    if app.master_is_remote:
        try:
            missing_mm_populated = pull_missing_multimedia_for_app(app)
        except RemoteRequestError:
            raise AppLinkError(_(
                'Error fetching multimedia from remote server. Please try again later.'
            ))
        if not missing_mm_populated:
            raise MultimediaMissingError(_(
                'Application has missing multimedia even after an attempt to pull them. '
                'An email has been sent with details. Please try again. If persists, report an issue.'
            ))

    app.domain_link.update_last_pull('app', user_id, model_details=AppLinkDetail(app_id=app._id))

    # reapply linked application specific data
    app.reapply_overrides()
예제 #2
0
 def test_app_update_then_fail(self):
     self._make_master1_build(True)
     # The missing validate_xform patch means this test will fail on travis, but make sure it also fails locally
     with patch('corehq.apps.app_manager.models.ApplicationBase.make_build', side_effect=Exception('Boom!')):
         self.manager.release([
             self._model_status(MODEL_APP, detail=AppLinkDetail(app_id=self.master1._id).to_json()),
         ], [self.linked_domain], True)
         self._assert_error(self.linked_domain, "Updated app but did not build or release: Boom!")
예제 #3
0
def update_linked_app(app, master_app_id_or_build, user_id):
    if not app.domain_link:
        raise AppLinkError(_(
            'This project is not authorized to update from the master application. '
            'Please contact the maintainer of the master app if you believe this is a mistake. '
        ))

    if isinstance(master_app_id_or_build, str):
        try:
            master_build = app.get_latest_master_release(master_app_id_or_build)
        except ActionNotPermitted:
            raise AppLinkError(_(
                'This project is not authorized to update from the master application. '
                'Please contact the maintainer of the master app if you believe this is a mistake. '
            ))
        except RemoteAuthError:
            raise AppLinkError(_(
                'Authentication failure attempting to pull latest master from remote CommCare HQ.'
                'Please verify your authentication details for the remote link are correct.'
            ))
        except RemoteRequestError:
            raise AppLinkError(_(
                'Unable to pull latest master from remote CommCare HQ. Please try again later.'
            ))
    else:
        master_build = master_app_id_or_build
    master_app_id = master_build.master_id

    previous = app.get_latest_build_from_upstream(master_app_id)
    if previous is None or master_build.version > previous.upstream_version:
        old_multimedia_ids = set([media_info.multimedia_id for path, media_info in app.multimedia_map.items()])
        report_map = get_static_report_mapping(master_build.domain, app['domain'])

        try:
            app = overwrite_app(app, master_build, report_map)
        except AppEditingError as e:
            raise AppLinkError(
                _(
                    'This application uses mobile UCRs '
                    'which are not available in the linked domain: {ucr_id}'
                ).format(ucr_id=str(e))
            )

        if app.master_is_remote:
            try:
                pull_missing_multimedia_for_app(app, old_multimedia_ids)
            except RemoteRequestError:
                raise AppLinkError(_(
                    'Error fetching multimedia from remote server. Please try again later.'
                ))

        # reapply linked application specific data
        app.reapply_overrides()
        app.save()

    app.domain_link.update_last_pull('app', user_id, model_details=AppLinkDetail(app_id=app._id))
예제 #4
0
def build_app_view_model(app, last_update=None):
    if not app:
        return None

    return build_linked_data_view_model(
        model_type=MODEL_APP,
        name=f"{LINKED_MODELS_MAP[MODEL_APP]} ({app.name})",
        detail=AppLinkDetail(app_id=app._id).to_json(),
        last_update=last_update,
    )
예제 #5
0
 def test_app_build_and_release(self, *args):
     self._make_master1_build(True)
     original_version = self.linked_app.version
     self.manager.release([
         self._model_status(MODEL_APP, detail=AppLinkDetail(app_id=self.master1._id).to_json()),
     ], [self.linked_domain], True)
     self._assert_domain_outcomes({self.linked_domain}, set())
     self.linked_application = LinkedApplication.get(self.linked_app._id)
     self.assertEqual(original_version + 1, self.linked_application.version)
     self.assertTrue(self.linked_application.is_released)
예제 #6
0
    def test_app_not_pushed_if_not_found(self):
        unpushed_app = Application.new_app(self.domain, "Not Yet Pushed App")
        unpushed_app.save()
        self.addCleanup(unpushed_app.delete)
        model = self._linked_data_view_model(
            MODEL_APP, detail=AppLinkDetail(app_id=unpushed_app._id).to_json())
        manager = ReleaseManager(self.domain, self.user.username)

        errors = manager._release_app(self.domain_link, model, manager.user)

        self.assertTrue("Could not find app" in errors)
예제 #7
0
    def _get_master_model_status(self,
                                 apps,
                                 fixtures,
                                 reports,
                                 ignore_models=None):
        model_status = []
        ignore_models = ignore_models or []

        for model, name in LINKED_MODELS:
            if (model not in ignore_models
                    and model not in (MODEL_APP, MODEL_FIXTURE, MODEL_REPORT)
                    and
                (model != MODEL_CASE_SEARCH
                 or toggles.SYNC_SEARCH_CASE_CLAIM.enabled(self.domain))):
                model_status.append({
                    'type': model,
                    'name': name,
                    'last_update': ugettext('Never'),
                    'detail': None,
                    'can_update': True
                })

        linked_models = dict(LINKED_MODELS)
        for app in apps.values():
            update = {
                'type': MODEL_APP,
                'name': '{} ({})'.format(linked_models['app'], app.name),
                'last_update': None,
                'detail': AppLinkDetail(app_id=app._id).to_json(),
                'can_update': True
            }
            model_status.append(update)
        for fixture in fixtures.values():
            update = {
                'type': MODEL_FIXTURE,
                'name': '{} ({})'.format(linked_models['fixture'],
                                         fixture.tag),
                'last_update': None,
                'detail': FixtureLinkDetail(tag=fixture.tag).to_json(),
                'can_update': fixture.is_global,
            }
            model_status.append(update)
        for report in reports.values():
            report = ReportConfiguration.get(report.get_id)
            update = {
                'type': MODEL_REPORT,
                'name': f"{linked_models['report']} ({report.title})",
                'last_update': None,
                'detail': ReportLinkDetail(report_id=report.get_id).to_json(),
                'can_update': True,
            }
            model_status.append(update)

        return model_status
예제 #8
0
 def test_app_build_and_release(self, *args):
     self._make_master1_build(True)
     original_version = self.linked_app.version
     self._assert_release([
         self._linked_data_view_model(
             MODEL_APP,
             detail=AppLinkDetail(app_id=self.master1._id).to_json()),
     ],
                          build_apps=True)
     self.linked_application = LinkedApplication.get(self.linked_app._id)
     self.assertEqual(original_version + 1, self.linked_application.version)
     self.assertTrue(self.linked_application.is_released)
예제 #9
0
    def test_already_synced_app_view_models_are_built(self):
        self._create_sync_event(
            MODEL_APP,
            AppLinkDetail(app_id=self.linked_app._id).to_json())

        _, downstream_apps = get_upstream_and_downstream_apps(
            self.downstream_domain)
        view_models = build_pullable_view_models_from_data_models(
            self.downstream_domain, self.domain_link, downstream_apps, {}, {},
            {}, {}, pytz.UTC)
        expected_length = len(DOMAIN_LEVEL_DATA_MODELS) + 1
        self.assertEqual(expected_length, len(view_models))
예제 #10
0
    def test_linked_apps_are_popped(self):
        self._create_sync_event(
            MODEL_APP,
            AppLinkDetail(app_id=self.linked_app._id).to_json())

        _, downstream_apps = get_upstream_and_downstream_apps(
            self.downstream_domain)
        self.assertTrue(1, len(downstream_apps))
        build_pullable_view_models_from_data_models(self.downstream_domain,
                                                    self.domain_link,
                                                    downstream_apps, {}, {},
                                                    {}, {}, pytz.UTC)
        self.assertEqual(0, len(downstream_apps))
예제 #11
0
def build_app_view_model(app, last_update=None):
    can_update = False
    name = _('Unknown App')
    detail = None

    if app:
        can_update = True
        name = app.name
        detail = AppLinkDetail(app_id=app._id).to_json()

    view_model = build_linked_data_view_model(
        model_type=MODEL_APP,
        name=f"{LINKED_MODELS_MAP[MODEL_APP]} ({name})",
        detail=detail,
        last_update=last_update,
        can_update=can_update
    )

    return view_model
예제 #12
0
    def _get_master_model_status(self,
                                 apps,
                                 fixtures,
                                 reports,
                                 keywords,
                                 ignore_models=None):
        model_status = []
        ignore_models = ignore_models or []

        for model, name in LINKED_MODELS:
            if (model not in ignore_models
                    and model not in (MODEL_APP, MODEL_FIXTURE, MODEL_REPORT,
                                      MODEL_KEYWORD) and
                (model != MODEL_CASE_SEARCH
                 or toggles.SYNC_SEARCH_CASE_CLAIM.enabled(self.domain))
                    and (model != MODEL_DATA_DICTIONARY
                         or toggles.DATA_DICTIONARY.enabled(self.domain))
                    and (model != MODEL_DIALER_SETTINGS
                         or toggles.WIDGET_DIALER.enabled(self.domain))
                    and (model != MODEL_OTP_SETTINGS
                         or toggles.GAEN_OTP_SERVER.enabled(self.domain))
                    and (model != MODEL_HMAC_CALLOUT_SETTINGS
                         or toggles.HMAC_CALLOUT.enabled(self.domain))):
                model_status.append({
                    'type': model,
                    'name': name,
                    'last_update': ugettext('Never'),
                    'detail': None,
                    'can_update': True
                })

        linked_models = dict(LINKED_MODELS)
        for app in apps.values():
            update = {
                'type': MODEL_APP,
                'name': '{} ({})'.format(linked_models['app'], app.name),
                'last_update': None,
                'detail': AppLinkDetail(app_id=app._id).to_json(),
                'can_update': True
            }
            model_status.append(update)
        for fixture in fixtures.values():
            update = {
                'type': MODEL_FIXTURE,
                'name': '{} ({})'.format(linked_models['fixture'],
                                         fixture.tag),
                'last_update': None,
                'detail': FixtureLinkDetail(tag=fixture.tag).to_json(),
                'can_update': fixture.is_global,
            }
            model_status.append(update)
        for report in reports.values():
            report = ReportConfiguration.get(report.get_id)
            update = {
                'type': MODEL_REPORT,
                'name': f"{linked_models['report']} ({report.title})",
                'last_update': None,
                'detail': ReportLinkDetail(report_id=report.get_id).to_json(),
                'can_update': True,
            }
            model_status.append(update)

        for keyword in keywords.values():
            update = {
                'type': MODEL_KEYWORD,
                'name': f"{linked_models['keyword']} ({keyword.keyword})",
                'last_update': None,
                'detail':
                KeywordLinkDetail(keyword_id=str(keyword.id)).to_json(),
                'can_update': True,
            }
            model_status.append(update)

        return model_status
예제 #13
0
    def page_context(self):
        timezone = get_timezone_for_request()

        def _link_context(link, timezone=timezone):
            return {
                'linked_domain':
                link.linked_domain,
                'master_domain':
                link.master_domain,
                'remote_base_url':
                link.remote_base_url,
                'remote_username':
                link.remote_username,
                'remote_api_key':
                link.remote_api_key,
                'is_remote':
                link.is_remote,
                'last_update':
                server_to_user_time(link.last_pull, timezone)
                if link.last_pull else 'Never',
            }

        model_status = []
        linked_models = dict(LINKED_MODELS)
        master_link = get_domain_master_link(self.domain)
        if master_link:
            linked_apps = {
                app._id: app
                for app in get_brief_apps_in_domain(self.domain)
                if app.doc_type == 'LinkedApplication'
            }
            models_seen = set()
            history = DomainLinkHistory.objects.filter(
                link=master_link
            ).annotate(row_number=RawSQL(
                'row_number() OVER (PARTITION BY model, model_detail ORDER BY date DESC)',
                []))
            for action in history:
                models_seen.add(action.model)
                if action.row_number != 1:
                    # first row is the most recent
                    continue
                name = linked_models[action.model]
                update = {
                    'type': action.model,
                    'name': name,
                    'last_update': server_to_user_time(action.date, timezone),
                    'detail': action.model_detail,
                    'can_update': True
                }
                if action.model == 'app':
                    app_name = 'Unknown App'
                    if action.model_detail:
                        detail = action.wrapped_detail
                        app = linked_apps.pop(detail.app_id, None)
                        app_name = app.name if app else detail.app_id
                        if app:
                            update['detail'] = action.model_detail
                        else:
                            update['can_update'] = False
                    else:
                        update['can_update'] = False
                    update['name'] = '{} ({})'.format(name, app_name)
                model_status.append(update)

            # Add in models that have never been synced
            for model, name in LINKED_MODELS:
                if model not in models_seen and model != 'app':
                    model_status.append({
                        'type': model,
                        'name': name,
                        'last_update': ugettext('Never'),
                        'detail': None,
                        'can_update': True
                    })

            # Add in apps that have never been synced
            if linked_apps:
                for app in linked_apps.values():
                    update = {
                        'type': 'app',
                        'name': '{} ({})'.format(linked_models['app'],
                                                 app.name),
                        'last_update': None,
                        'detail': AppLinkDetail(app_id=app._id).to_json(),
                        'can_update': True
                    }
                    model_status.append(update)

        return {
            'domain': self.domain,
            'timezone': timezone.localize(datetime.utcnow()).tzname(),
            'view_data': {
                'master_link':
                _link_context(master_link) if master_link else None,
                'model_status':
                sorted(model_status, key=lambda m: m['name']),
                'linked_domains': [
                    _link_context(link)
                    for link in get_linked_domains(self.domain)
                ],
                'models': [{
                    'slug': model[0],
                    'name': model[1]
                } for model in LINKED_MODELS]
            },
        }
예제 #14
0
 def test_multi_master_app_fail(self):
     self._assert_release([
         self._model_status(MODEL_APP,
                            detail=AppLinkDetail(app_id='123').to_json()),
     ],
                          error="Multi master flag is in use")
예제 #15
0
 def test_app_not_found(self):
     self._assert_release([
         self._model_status(MODEL_APP,
                            detail=AppLinkDetail(app_id='123').to_json()),
     ],
                          error="Could not find app")
예제 #16
0
 def test_app_not_found(self):
     self.manager.release([
         self._model_status(MODEL_APP, detail=AppLinkDetail(app_id='123').to_json()),
     ], [self.linked_domain])
     self._assert_domain_outcomes(set(), {self.linked_domain})
     self._assert_error(self.linked_domain, "Could not find app")
예제 #17
0
 def test_multi_master_app_fail(self):
     self.manager.release([
         self._model_status(MODEL_APP, detail=AppLinkDetail(app_id='123').to_json()),
     ], [self.linked_domain])
     self._assert_domain_outcomes(set(), {self.linked_domain})
     self._assert_error(self.linked_domain, "Multi master flag is in use")