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()
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!")
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))
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, )
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)
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)
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
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)
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))
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))
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
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
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] }, }
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")
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")
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")
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")