def testComponentsUpdate_Normal(self): self.services.project.TestAddProject( 'test-project', owner_ids=[1], project_id=12345) self.SetUpComponents(12345, 1, 'API') self.SetUpComponents(12345, 2, 'Parent') self.SetUpComponents(12345, 3, 'Parent>Child') cd_dict = { 'componentPath': 'API', 'updates': [ {'field': 'DESCRIPTION', 'description': ''}, {'field': 'CC', 'cc': ['*****@*****.**', '*****@*****.**']}, {'field': 'DEPRECATED', 'deprecated': True}]} self.request.update(cd_dict) _ = self.call_api('components_update', self.request).json_body component_def = tracker_bizobj.FindComponentDef( 'API', self.config) self.assertIsNotNone(component_def) self.assertEqual('', component_def.docstring) self.assertEqual([1L, 2L], component_def.cc_ids) self.assertTrue(component_def.deprecated) cd_dict = { 'componentPath': 'Parent', 'updates': [ {'field': 'LEAF_NAME', 'leafName': 'NewParent'}]} self.request.update(cd_dict) _ = self.call_api('components_update', self.request).json_body cd_parent = tracker_bizobj.FindComponentDef( 'NewParent', self.config) cd_child = tracker_bizobj.FindComponentDef( 'NewParent>Child', self.config) self.assertIsNotNone(cd_parent) self.assertIsNotNone(cd_child)
def components_create(self, request): """Create a component.""" mar = self.mar_factory(request) if not mar.perms.CanUsePerm( permissions.EDIT_PROJECT, mar.auth.effective_ids, mar.project, []): raise permissions.PermissionException( 'User is not allowed to create components for this project') config = self._services.config.GetProjectConfig(mar.cnxn, mar.project_id) leaf_name = request.componentName if not tracker_constants.COMPONENT_NAME_RE.match(leaf_name): raise config_svc.InvalidComponentNameException( 'The component name %s is invalid.' % leaf_name) parent_path = request.parentPath if parent_path: parent_def = tracker_bizobj.FindComponentDef(parent_path, config) if not parent_def: raise config_svc.NoSuchComponentException( 'Parent component %s does not exist.' % parent_path) if not permissions.CanEditComponentDef( mar.auth.effective_ids, mar.perms, mar.project, parent_def, config): raise permissions.PermissionException( 'User is not allowed to add a subcomponent to component %s' % parent_path) path = '%s>%s' % (parent_path, leaf_name) else: path = leaf_name if tracker_bizobj.FindComponentDef(path, config): raise config_svc.InvalidComponentNameException( 'The name %s is already in use.' % path) created = int(time.time()) user_emails = set() user_emails.update([mar.auth.email] + request.admin + request.cc) user_ids_dict = self._services.user.LookupUserIDs( mar.cnxn, list(user_emails), autocreate=False) admin_ids = [user_ids_dict[uname] for uname in request.admin] cc_ids = [user_ids_dict[uname] for uname in request.cc] label_ids = [] # TODO(jrobbins): allow API clients to specify this too. component_id = self._services.config.CreateComponentDef( mar.cnxn, mar.project_id, path, request.description, request.deprecated, admin_ids, cc_ids, created, user_ids_dict[mar.auth.email], label_ids) return api_pb2_v1.Component( componentId=component_id, projectName=request.projectId, componentPath=path, description=request.description, admin=request.admin, cc=request.cc, deprecated=request.deprecated, created=datetime.datetime.fromtimestamp(created), creator=mar.auth.email)
def testProcessEditComponent_RenameWithSubComponents(self): subcd_1 = tracker_bizobj.MakeComponentDef(2, self.project.project_id, 'BackEnd>Worker1', 'doc', False, [], [111L], 0, 125L, 3, 126L) subcd_2 = tracker_bizobj.MakeComponentDef(3, self.project.project_id, 'BackEnd>Worker2', 'doc', False, [], [111L], 0, 125L, 4, 127L) self.config.component_defs.extend([subcd_1, subcd_2]) self.mox.StubOutWithMock(filterrules_helpers, 'RecomputeAllDerivedFields') filterrules_helpers.RecomputeAllDerivedFields(self.mr.cnxn, self.services, self.mr.project, self.config) self.mox.ReplayAll() post_data = fake.PostData( leaf_name=['BackEnds'], docstring=['This is where the magic happens'], deprecated=[True], admins=['*****@*****.**'], cc=['*****@*****.**'], labels=['']) self.servlet._ProcessEditComponent(self.mr, post_data, self.config, self.cd) self.mox.VerifyAll() config = self.services.config.GetProjectConfig(self.mr.cnxn, self.mr.project_id) cd = tracker_bizobj.FindComponentDef('BackEnds', config) self.assertEqual('BackEnds', cd.path) subcd_1 = tracker_bizobj.FindComponentDef('BackEnds>Worker1', config) self.assertEqual('BackEnds>Worker1', subcd_1.path) # Assert that creator and modifier have not changed for subcd_1. self.assertEqual(125L, subcd_1.creator_id) self.assertEqual(0, subcd_1.created) self.assertEqual(126L, subcd_1.modifier_id) self.assertEqual(3, subcd_1.modified) subcd_2 = tracker_bizobj.FindComponentDef('BackEnds>Worker2', config) self.assertEqual('BackEnds>Worker2', subcd_2.path) # Assert that creator and modifier have not changed for subcd_2. self.assertEqual(125L, subcd_2.creator_id) self.assertEqual(0, subcd_2.created) self.assertEqual(127L, subcd_2.modifier_id) self.assertEqual(4, subcd_2.modified)
def components_delete(self, request): """Delete a component.""" mar = self.mar_factory(request) config = self._services.config.GetProjectConfig(mar.cnxn, mar.project_id) component_path = request.componentPath component_def = tracker_bizobj.FindComponentDef( component_path, config) if not component_def: raise config_svc.NoSuchComponentException( 'The component %s does not exist.' % component_path) if not permissions.CanViewComponentDef( mar.auth.effective_ids, mar.perms, mar.project, component_def): raise permissions.PermissionException( 'User is not allowed to view this component %s' % component_path) if not permissions.CanEditComponentDef( mar.auth.effective_ids, mar.perms, mar.project, component_def, config): raise permissions.PermissionException( 'User is not allowed to delete this component %s' % component_path) allow_delete = not tracker_bizobj.FindDescendantComponents( config, component_def) if not allow_delete: raise permissions.PermissionException( 'User tried to delete component that had subcomponents') self._services.issue.DeleteComponentReferences( mar.cnxn, component_def.component_id) self._services.config.DeleteComponentDef( mar.cnxn, mar.project_id, component_def.component_id) return message_types.VoidMessage()
def testProcessEditComponent(self): post_data = fake.PostData( leaf_name=['BackEnd'], docstring=['This is where the magic happens'], deprecated=[True], admins=['*****@*****.**'], cc=['*****@*****.**'], labels=['Hot, Cold']) self.servlet._ProcessEditComponent(self.mr, post_data, self.config, self.cd) self.mox.VerifyAll() config = self.services.config.GetProjectConfig(self.mr.cnxn, self.mr.project_id) cd = tracker_bizobj.FindComponentDef('BackEnd', config) self.assertEqual('BackEnd', cd.path) self.assertEqual('This is where the magic happens', cd.docstring) self.assertEqual(True, cd.deprecated) self.assertEqual([111L], cd.admin_ids) self.assertEqual([111L], cd.cc_ids) # Assert that creator and created were not updated. self.assertEqual(122L, cd.creator_id) self.assertEqual(100000, cd.created) # Assert that modifier and modified were updated. self.assertEqual(122L, cd.modifier_id) self.assertTrue(cd.modified > 10000000)
def _GetComponentDef(self, mr): """Get the config and component definition to be viewed or edited.""" if not mr.component_path: self.abort(404, 'component not specified') config = self.services.config.GetProjectConfig(mr.cnxn, mr.project_id) component_def = tracker_bizobj.FindComponentDef(mr.component_path, config) if not component_def: self.abort(404, 'component not found') return config, component_def
def _GetComponentDefs(self, _mr, post_data, config): """Get the config and component definitions from the request.""" component_defs = [] component_paths = post_data.get('delete_components').split(',') for component_path in component_paths: component_def = tracker_bizobj.FindComponentDef( component_path, config) component_defs.append(component_def) return component_defs
def LookupComponentIDs(component_paths, config, errors): """Look up the IDs of the specified components in the given config.""" component_ids = [] for path in component_paths: if not path: continue cd = tracker_bizobj.FindComponentDef(path, config) if cd: component_ids.append(cd.component_id) else: errors.components = 'Unknown component %s' % path return component_ids
def LeafNameErrorMessage(parent_path, leaf_name, config): """Return an error message for the given component name, or None.""" if not tracker_constants.COMPONENT_NAME_RE.match(leaf_name): return 'Invalid component name' if parent_path: path = '%s>%s' % (parent_path, leaf_name) else: path = leaf_name if tracker_bizobj.FindComponentDef(path, config): return 'That name is already in use.' return None
def testProcessFormData_Normal(self): post_data = fake.PostData(parent_path=['BackEnd'], leaf_name=['DB'], docstring=['A database'], deprecated=[False], admins=[''], cc=[''], labels=['']) url = self.servlet.ProcessFormData(self.mr, post_data) self.assertTrue('/adminComponents?saved=1&' in url) config = self.services.config.GetProjectConfig(self.mr.cnxn, self.mr.project_id) cd = tracker_bizobj.FindComponentDef('BackEnd>DB', config) self.assertEqual('BackEnd>DB', cd.path) self.assertEqual('A database', cd.docstring) self.assertEqual([], cd.admin_ids) self.assertEqual([], cd.cc_ids) self.assertTrue(cd.created > 0) self.assertEqual(122L, cd.creator_id)
def testProcessFormData_Edit(self): post_data = fake.PostData( leaf_name=['BackEnd'], docstring=['This is where the magic happens'], deprecated=[True], admins=['*****@*****.**'], cc=['*****@*****.**'], labels=['Hot, Cold']) url = self.servlet.ProcessFormData(self.mr, post_data) self.mox.VerifyAll() self.assertTrue('/components/detail?component=BackEnd&saved=1&' in url) config = self.services.config.GetProjectConfig(self.mr.cnxn, self.mr.project_id) cd = tracker_bizobj.FindComponentDef('BackEnd', config) self.assertEqual('BackEnd', cd.path) self.assertEqual('This is where the magic happens', cd.docstring) self.assertEqual(True, cd.deprecated) self.assertEqual([111L], cd.admin_ids) self.assertEqual([111L], cd.cc_ids)
def testFindComponentDef_MatchFound(self): config = tracker_bizobj.MakeDefaultProjectIssueConfig(789) cd = tracker_pb2.ComponentDef(path='UI>Splash') config.component_defs.append(cd) actual = tracker_bizobj.FindComponentDef('UI>Splash', config) self.assertEqual(cd, actual)
def testFindComponentDef_Empty(self): config = tracker_bizobj.MakeDefaultProjectIssueConfig(789) actual = tracker_bizobj.FindComponentDef('DB', config) self.assertIsNone(actual)
def _ProcessEditComponent(self, mr, post_data, config, component_def): """The user wants to edit this component definition.""" parsed = component_helpers.ParseComponentRequest( mr, post_data, self.services) if not tracker_constants.COMPONENT_NAME_RE.match(parsed.leaf_name): mr.errors.leaf_name = 'Invalid component name' original_path = component_def.path if mr.component_path and '>' in mr.component_path: parent_path = mr.component_path[:mr.component_path.rindex('>')] new_path = '%s>%s' % (parent_path, parsed.leaf_name) else: new_path = parsed.leaf_name conflict = tracker_bizobj.FindComponentDef(new_path, config) if conflict and conflict.component_id != component_def.component_id: mr.errors.leaf_name = 'That name is already in use.' creator, created = self._GetUserViewAndFormattedTime( mr, component_def.creator_id, component_def.created) modifier, modified = self._GetUserViewAndFormattedTime( mr, component_def.modifier_id, component_def.modified) if mr.errors.AnyErrors(): self.PleaseCorrect( mr, initial_leaf_name=parsed.leaf_name, initial_docstring=parsed.docstring, initial_deprecated=ezt.boolean(parsed.deprecated), initial_admins=parsed.admin_usernames, initial_cc=parsed.cc_usernames, initial_labels=parsed.label_strs, created=created, creator=creator, modified=modified, modifier=modifier, ) return None new_modified = int(time.time()) new_modifier_id = self.services.user.LookupUserID( mr.cnxn, mr.auth.email, autocreate=False) self.services.config.UpdateComponentDef( mr.cnxn, mr.project_id, component_def.component_id, path=new_path, docstring=parsed.docstring, deprecated=parsed.deprecated, admin_ids=parsed.admin_ids, cc_ids=parsed.cc_ids, modified=new_modified, modifier_id=new_modifier_id, label_ids=parsed.label_ids) update_rule = False if new_path != original_path: update_rule = True # If the name changed then update all of its subcomponents as well. subcomponent_ids = tracker_bizobj.FindMatchingComponentIDs( original_path, config, exact=False) for subcomponent_id in subcomponent_ids: if subcomponent_id == component_def.component_id: continue subcomponent_def = tracker_bizobj.FindComponentDefByID( subcomponent_id, config) subcomponent_new_path = subcomponent_def.path.replace( original_path, new_path, 1) self.services.config.UpdateComponentDef( mr.cnxn, mr.project_id, subcomponent_def.component_id, path=subcomponent_new_path) if (set(parsed.cc_ids) != set(component_def.cc_ids) or set(parsed.label_ids) != set(component_def.label_ids)): update_rule = True if update_rule: filterrules_helpers.RecomputeAllDerivedFields( mr.cnxn, self.services, mr.project, config) return framework_helpers.FormatAbsoluteURL( mr, urls.COMPONENT_DETAIL, component=new_path, saved=1, ts=int(time.time()))
def testProcessDeleteComponent(self): self.servlet._ProcessDeleteComponent(self.mr, self.cd) self.assertIsNone( tracker_bizobj.FindComponentDef('BackEnd', self.config))
def testProcessFormData_Delete(self): post_data = fake.PostData(name=['BackEnd'], deletecomponent=['Submit']) url = self.servlet.ProcessFormData(self.mr, post_data) self.assertTrue('/adminComponents?deleted=1&' in url) self.assertIsNone( tracker_bizobj.FindComponentDef('BackEnd', self.config))
def ProcessFormData(self, mr, post_data): """Validate and store the contents of the issues tracker admin page. Args: mr: commonly used info parsed from the request. post_data: HTML form data from the request. Returns: String URL to redirect the user to, or None if response was already sent. """ config = self.services.config.GetProjectConfig(mr.cnxn, mr.project_id) parent_path = post_data.get('parent_path', '') parsed = component_helpers.ParseComponentRequest( mr, post_data, self.services) if parent_path: parent_def = tracker_bizobj.FindComponentDef(parent_path, config) if not parent_def: self.abort(500, 'parent component not found') allow_parent_edit = permissions.CanEditComponentDef( mr.auth.effective_ids, mr.perms, mr.project, parent_def, config) if not allow_parent_edit: raise permissions.PermissionException( 'User is not allowed to add a subcomponent here') path = '%s>%s' % (parent_path, parsed.leaf_name) else: path = parsed.leaf_name leaf_name_error_msg = LeafNameErrorMessage(parent_path, parsed.leaf_name, config) if leaf_name_error_msg: mr.errors.leaf_name = leaf_name_error_msg if mr.errors.AnyErrors(): self.PleaseCorrect( mr, parent_path=parent_path, initial_leaf_name=parsed.leaf_name, initial_docstring=parsed.docstring, initial_deprecated=ezt.boolean(parsed.deprecated), initial_admins=parsed.admin_usernames, initial_cc=parsed.cc_usernames, initial_labels=parsed.label_strs, ) return created = int(time.time()) creator_id = self.services.user.LookupUserID(mr.cnxn, mr.auth.email, autocreate=False) self.services.config.CreateComponentDef(mr.cnxn, mr.project_id, path, parsed.docstring, parsed.deprecated, parsed.admin_ids, parsed.cc_ids, created, creator_id, label_ids=parsed.label_ids) return framework_helpers.FormatAbsoluteURL(mr, urls.ADMIN_COMPONENTS, saved=1, ts=int(time.time()))
def components_update(self, request): """Update a component.""" mar = self.mar_factory(request) config = self._services.config.GetProjectConfig(mar.cnxn, mar.project_id) component_path = request.componentPath component_def = tracker_bizobj.FindComponentDef( component_path, config) if not component_def: raise config_svc.NoSuchComponentException( 'The component %s does not exist.' % component_path) if not permissions.CanViewComponentDef( mar.auth.effective_ids, mar.perms, mar.project, component_def): raise permissions.PermissionException( 'User is not allowed to view this component %s' % component_path) if not permissions.CanEditComponentDef( mar.auth.effective_ids, mar.perms, mar.project, component_def, config): raise permissions.PermissionException( 'User is not allowed to edit this component %s' % component_path) original_path = component_def.path new_path = component_def.path new_docstring = component_def.docstring new_deprecated = component_def.deprecated new_admin_ids = component_def.admin_ids new_cc_ids = component_def.cc_ids update_filterrule = False for update in request.updates: if update.field == api_pb2_v1.ComponentUpdateFieldID.LEAF_NAME: leaf_name = update.leafName if not tracker_constants.COMPONENT_NAME_RE.match(leaf_name): raise config_svc.InvalidComponentNameException( 'The component name %s is invalid.' % leaf_name) if '>' in original_path: parent_path = original_path[:original_path.rindex('>')] new_path = '%s>%s' % (parent_path, leaf_name) else: new_path = leaf_name conflict = tracker_bizobj.FindComponentDef(new_path, config) if conflict and conflict.component_id != component_def.component_id: raise config_svc.InvalidComponentNameException( 'The name %s is already in use.' % new_path) update_filterrule = True elif update.field == api_pb2_v1.ComponentUpdateFieldID.DESCRIPTION: new_docstring = update.description elif update.field == api_pb2_v1.ComponentUpdateFieldID.ADMIN: user_ids_dict = self._services.user.LookupUserIDs( mar.cnxn, list(update.admin), autocreate=True) new_admin_ids = [user_ids_dict[email] for email in update.admin] elif update.field == api_pb2_v1.ComponentUpdateFieldID.CC: user_ids_dict = self._services.user.LookupUserIDs( mar.cnxn, list(update.cc), autocreate=True) new_cc_ids = [user_ids_dict[email] for email in update.cc] update_filterrule = True elif update.field == api_pb2_v1.ComponentUpdateFieldID.DEPRECATED: new_deprecated = update.deprecated else: logging.error('Unknown component field %r', update.field) new_modified = int(time.time()) new_modifier_id = self._services.user.LookupUserID( mar.cnxn, mar.auth.email, autocreate=False) logging.info( 'Updating component id %d: path-%s, docstring-%s, deprecated-%s,' ' admin_ids-%s, cc_ids-%s modified by %s', component_def.component_id, new_path, new_docstring, new_deprecated, new_admin_ids, new_cc_ids, new_modifier_id) self._services.config.UpdateComponentDef( mar.cnxn, mar.project_id, component_def.component_id, path=new_path, docstring=new_docstring, deprecated=new_deprecated, admin_ids=new_admin_ids, cc_ids=new_cc_ids, modified=new_modified, modifier_id=new_modifier_id) # TODO(sheyang): reuse the code in componentdetails if original_path != new_path: # If the name changed then update all of its subcomponents as well. subcomponent_ids = tracker_bizobj.FindMatchingComponentIDs( original_path, config, exact=False) for subcomponent_id in subcomponent_ids: if subcomponent_id == component_def.component_id: continue subcomponent_def = tracker_bizobj.FindComponentDefByID( subcomponent_id, config) subcomponent_new_path = subcomponent_def.path.replace( original_path, new_path, 1) self._services.config.UpdateComponentDef( mar.cnxn, mar.project_id, subcomponent_def.component_id, path=subcomponent_new_path) if update_filterrule: filterrules_helpers.RecomputeAllDerivedFields( mar.cnxn, self._services, mar.project, config) return message_types.VoidMessage()