def post(self, request, env_group_id): status = request.POST.get('status') if status is None: return JsonResponseBadRequest({ 'message': 'Environment group status is missing.' }) if status not in ['0', '1']: return JsonResponseBadRequest({ 'message': f'Environment group status "{status}" is invalid.' }) try: env = TCMSEnvGroup.objects.get(pk=int(env_group_id)) except TCMSEnvGroup.DoesNotExist: return JsonResponseNotFound({ 'message': f'Environment group with id {env_group_id} does not exist.' }) else: new_status = bool(int(status)) if env.is_active != new_status: env.is_active = new_status env.save(update_fields=['is_active']) env.log_action( who=request.user, field='is_active', original_value=env.is_active, new_value=new_status) return JsonResponse({'env_group_id': env_group_id})
def _update_sortkey(self): try: sortkey = int(self.new_value) if sortkey < 0 or sortkey > 32300: return JsonResponseBadRequest( {'message': 'New sortkey is out of range [0, 32300].'}) except ValueError: return JsonResponseBadRequest( {'message': 'New sortkey is not an integer.'}) plan = plan_from_request_or_none(self.request, pk_enough=True) if plan is None: return JsonResponseBadRequest({'message': 'No plan record found.'}) update_targets = self.get_update_targets() # ## # MySQL does not allow to exeucte UPDATE statement that contains # subquery querying from same table. In this case, OperationError will # be raised. offset = 0 step_length = 500 queryset_filter = TestCasePlan.objects.filter data = {self.target_field: sortkey} while 1: sub_cases = update_targets[offset:offset + step_length] case_pks = [case.pk for case in sub_cases] if len(case_pks) == 0:
def get(request): """Get links of specific instance of content type - target: The model name of the instance being searched - target_id: The ID of the instance Only accept GET request from client. """ form = BasicValidationForm(request.GET) if form.is_valid(): model_class = form.cleaned_data['target'] target_id = form.cleaned_data['target_id'] try: model_instance = model_class.objects.get(pk=target_id) links = LinkReference.get_from(model_instance) except Exception as err: return JsonResponseBadRequest({'message': str(err)}) jd = [] for link in links: jd.append({'name': link.name, 'url': link.url}) return JsonResponse(jd, safe=False) else: return JsonResponseBadRequest({'message': form_errors_to_list(form)})
def remove(self): if not self.request.user.has_perm('issuetracker.delete_issue'): return JsonResponseForbidden({'message': 'Permission denied.'}) class RemoveIssueForm(forms.Form): issue_key = forms.CharField() case_run = forms.ModelMultipleChoiceField( queryset=TestCaseRun.objects.all().only('pk'), error_messages={ 'required': 'Case run id is missed.', 'invalid_pk_value': 'Case run %(pk)s does not exist.', }, ) form = RemoveIssueForm(request.GET) if not form.is_valid(): return JsonResponseBadRequest( {'message': form_error_messages_to_list(form)}) issue_key = form.cleaned_data['issue_key'] case_runs = form.cleaned_data['case_run'] for case_run in case_runs: try: case_run.remove_issue(issue_key) except Exception: msg = 'Failed to remove issue {} from case run {}'.format( issue_key, case_run.pk) logger.exception(msg) return JsonResponseBadRequest({'message': msg}) return self.run_issues_info(case_runs)
def update(self): has_perms = check_permission(self.request, self.ctype) if not has_perms: return JsonResponseForbidden({ 'message': "You don't have enough permission to update TestCases." }) action = self.get_update_action() if action is not None: try: resp = action() self._sendmail() except ObjectDoesNotExist as err: return JsonResponseNotFound({'message': str(err)}) except Exception: # TODO: besides this message to users, what happening should be # recorded in the system log. return JsonResponseBadRequest({ 'message': 'Update failed. Please try again or request ' 'support from your organization.' }) else: if resp is None: resp = JsonResponse({}) return resp return JsonResponseBadRequest({'message': 'Not know what to update.'})
def post(self, request, link_id): try: LinkReference.unlink(link_id) except Exception as err: return JsonResponseBadRequest({'message': str(err)}) return JsonResponse({'message': 'Link has been removed successfully.'})
def post(self, request): form = AddLinkReferenceForm(request.POST) if form.is_valid(): name = form.cleaned_data['name'] url = form.cleaned_data['url'] target_id = form.cleaned_data['target_id'] model_class = form.cleaned_data['target'] model_instance = model_class.objects.get(pk=target_id) create_link(name=name, url=url, link_to=model_instance) return JsonResponse({ 'rc': 0, 'response': 'ok', 'data': { 'name': name, 'url': url } }) else: return JsonResponseBadRequest({ 'rc': 1, 'response': form.errors.as_text() })
def post(self, request): property_name = request.POST.get('name') if not property_name: return JsonResponseBadRequest({ 'message': 'Property name is missing.' }) if TCMSEnvProperty.objects.filter(name=property_name).exists(): return JsonResponseBadRequest({ 'message': f'Environment property {property_name} ' f'already exists, please choose another name.' }) new_property = TCMSEnvProperty.objects.create(name=property_name) return JsonResponse({'id': new_property.pk, 'name': new_property.name})
def treeview_add_child_plans(request: HttpRequest, plan_id: int): plan = TestPlan.objects.filter(pk=plan_id).only('pk').first() if plan is None: return JsonResponseNotFound( {'message': f'Plan {plan_id} does not exist.'}) child_plan_ids: List[str] = request.POST.getlist('children') child_plans: List[TestPlan] = [] ancestor_ids = plan.get_ancestor_ids() descendant_ids = plan.get_descendant_ids() for child_plan_id in child_plan_ids: if not child_plan_id.isdigit(): return JsonResponseBadRequest( {'message': f'Child plan id {child_plan_id} is not a number.'}) child_plan: TestPlan = (TestPlan.objects.filter( pk=int(child_plan_id)).only('pk').first()) if child_plan is None: return JsonResponseBadRequest( {'message': f'Child plan {child_plan_id} does not exist.'}) if child_plan.pk in ancestor_ids: return JsonResponseBadRequest({ 'message': f'Plan {child_plan_id} is an ancestor of ' f'plan {plan_id} already.' }) if child_plan.pk in descendant_ids: return JsonResponseBadRequest({ 'message': f'Plan {child_plan_id} is a descendant of ' f'plan {plan_id} already.' }) child_plans.append(child_plan) for child_plan in child_plans: child_plan.parent = plan child_plan.save(update_fields=['parent']) return JsonResponse({ 'parent_plan': plan.pk, 'children_plans': [plan.pk for plan in child_plans] })
def comment_case_runs(request): """ Add comment to one or more caseruns at a time. """ data = request.POST.copy() comment = data.get('comment', None) if not comment: return JsonResponseBadRequest({'message': 'Comments needed'}) run_ids = [int(item) for item in data.getlist('run')] if not run_ids: return JsonResponseBadRequest({'message': 'No runs selected.'}) case_run_ids = (TestCaseRun.objects.filter(pk__in=run_ids).values_list( 'pk', flat=True)) if not case_run_ids: return JsonResponseBadRequest({'message': 'No caserun found.'}) tcms.comments.models.add_comment(request.user, 'testruns.testcaserun', case_run_ids, comment, request.META.get('REMOTE_ADDR')) return JsonResponse({})
def post(request, template_name='comments/comments.html'): """Post a comment""" data = request.POST.copy() try: target, _ = post_comment(data, request.user, request.META.get('REMOTE_ADDR')) except InvalidCommentPostRequest as e: msg = f'Fail to add comment to object {e.target}' log.exception(msg) return JsonResponseBadRequest({'message': msg}) return JsonResponse({})
def post(self, request): group_name = request.POST.get('name') # Get the group name of environment from javascript if not group_name: return JsonResponseBadRequest({ 'message': 'Environment group name is required.' }) if TCMSEnvGroup.objects.filter(name=group_name).exists(): return JsonResponseBadRequest({ 'message': f'Environment group name "{group_name}" already' f' exists, please choose another name.' }) env = TCMSEnvGroup.objects.create( name=group_name, manager_id=request.user.id, modified_by_id=None) env.log_action(who=request.user, new_value=f'Initial env group {env.name}') return JsonResponse({'env_group_id': env.id})
def post(self, request, env_property_id): env_property = TCMSEnvProperty.objects.filter(pk=env_property_id).first() if env_property is None: return JsonResponseBadRequest({ 'message': f'Environment property with id {env_property_id} ' f'does not exist.' }) new_name = request.POST.get('name', env_property.name) env_property.name = new_name env_property.save(update_fields=['name']) return JsonResponse({'id': env_property_id, 'name': new_name})
def add(self): # TODO: make a migration for the permission if not self.request.user.has_perm('issuetracker.add_issue'): return JsonResponseForbidden({'message': 'Permission denied.'}) form = CaseRunIssueForm(request.GET) if not form.is_valid(): return JsonResponseBadRequest( {'message': form_error_messages_to_list(form)}) service = find_service(form.cleaned_data['tracker']) issue_key = form.cleaned_data['issue_key'] link_et = form.cleaned_data['link_external_tracker'] case_runs = form.cleaned_data['case_run'] # FIXME: maybe, make sense to validate in the form. if not all(case_run.run_id == self.run.pk for case_run in case_runs): return JsonResponseBadRequest({ 'message': f'Not all case runs belong to run {self.run.pk}.' }) try: for case_run in case_runs: service.add_issue(issue_key, case_run.case, case_run=case_run, add_case_to_issue=link_et) except ValidationError as e: logger.exception( 'Failed to add issue to case run %s. Error reported: %s', form.case_run.pk, str(e)) return JsonResponseBadRequest({'message': str(e)}) return self.run_issues_info(case_runs)
def remove(request, link_id): """Remove a specific link with ID ``link_id``""" from django.forms import IntegerField from django.forms import ValidationError field = IntegerField(min_value=1) try: value = field.clean(link_id) except ValidationError as err: return JsonResponseBadRequest({ 'rc': 1, 'response': '\n'.join(err.messages) }) try: LinkReference.unlink(value) except Exception as err: return JsonResponseBadRequest({'rc': 1, 'response': str(err)}) return JsonResponse({ 'rc': 0, 'response': 'Link has been removed successfully.' })
def post(self, request): property_ids = request.POST.getlist('id') if not property_ids: return JsonResponseBadRequest({ 'message': 'Missing environment property ids. Nothing changed.' }) invalid_ids = list(filter(lambda item: not item.isdigit(), property_ids)) if invalid_ids: if len(invalid_ids) > 1: msg = f'Environment property ids {", ".join(invalid_ids)} are not number.' else: msg = f'Environment property id {invalid_ids[0]} is not a number.' return JsonResponseBadRequest({'message': msg}) new_status = request.POST.get('status') if new_status is None: return JsonResponseBadRequest({'message': 'Missing status.'}) if new_status not in ['0', '1']: return JsonResponseBadRequest( {'message': f'Invalid status {new_status}.'}) property_ids = list(map(int, property_ids)) env_properties = TCMSEnvProperty.objects.filter(pk__in=property_ids) for env_property in env_properties: env_property.is_active = bool(int(new_status)) env_property.save() # FIXME: why delete such properties? if not env_property.is_active: TCMSEnvGroupPropertyMap.objects.filter( property__id__in=property_ids).delete() return JsonResponse({'property_ids': property_ids})
def _update_case_status(self): exists = TestCaseStatus.objects.filter(pk=self.new_value).exists() if not exists: raise ObjectDoesNotExist('The status you choose does not exist.') self.get_update_targets().update( **{str(self.target_field): self.new_value}) # ### # Case is moved between Cases and Reviewing Cases tabs accoding to the # change of status. Meanwhile, the number of cases with each status # should be updated also. try: plan = plan_from_request_or_none(self.request) except Http404: return JsonResponseBadRequest({'message': 'No plan record found.'}) else: if plan is None: return JsonResponseBadRequest( {'message': 'No plan record found.'}) confirm_status_name = 'CONFIRMED' plan.run_case = plan.case.filter(case_status__name=confirm_status_name) plan.review_case = plan.case.exclude( case_status__name=confirm_status_name) run_case_count = plan.run_case.count() case_count = plan.case.count() # FIXME: why not calculate review_case_count or run_case_count by using # substraction, which saves one SQL query. review_case_count = plan.review_case.count() return http.JsonResponse({ 'run_case_count': run_case_count, 'case_count': case_count, 'review_case_count': review_case_count, })
def post(self, request): comments = django_comments.get_model().objects.filter( pk__in=request.POST.getlist('comment_id'), site__pk=settings.SITE_ID, is_removed=False, user_id=request.user.id) if not comments: return JsonResponseBadRequest( {'message': 'No incoming comment id exists.'}) # Flag the comment as deleted instead of actually deleting it. for comment in comments: perform_delete(request, comment) return JsonResponse({})
def add(request): """Add new link to a specific target The target should be a valid model within TCMS, which are documented in ``LINKREF_TARGET``. Incoming request should be a POST request, and contains following arguments: - target: To which the new link will link to. The avialable target names are documented in the ``LINKREF_TARGET``. - target_id: the ID used to construct the concrete target instance, to which the new link will be linked. - name: a short description to this new link, and accept 64 characters at most. - url: the actual URL. """ form = AddLinkReferenceForm(request.POST) if form.is_valid(): name = form.cleaned_data['name'] url = form.cleaned_data['url'] target_id = form.cleaned_data['target_id'] model_class = form.cleaned_data['target'] model_instance = model_class.objects.get(pk=target_id) create_link(name=name, url=url, link_to=model_instance) return JsonResponse({ 'rc': 0, 'response': 'ok', 'data': { 'name': name, 'url': url } }) else: return JsonResponseBadRequest({ 'rc': 1, 'response': form.errors.as_text() })
def post(self, request, plan_id): # Current we should rewrite all of cases belong to the plan. # Because the cases sortkey in database is chaos, # Most of them are None. if 'case' not in request.POST: return JsonResponseBadRequest( {'message': 'At least one case is required to re-order.'}) plan = get_object_or_404(TestPlan, pk=int(plan_id)) case_ids = [int(id) for id in request.POST.getlist('case')] cases = TestCase.objects.filter(pk__in=case_ids).only('pk') for case in cases: new_sort_key = (case_ids.index(case.pk) + 1) * 10 TestCasePlan.objects.filter(plan=plan, case=case).update(sortkey=new_sort_key) return JsonResponse({})
def post(self, request, plan_id): plan = get_object_or_404(TestPlan.objects.only('pk'), pk=int(plan_id)) if 'case' not in request.POST: return JsonResponseBadRequest( {'message': 'At least one case is required to delete.'}) cases = get_selected_testcases(request).only('pk') # Log Action plan_log = TCMSLog(model=plan) for case in cases: plan_log.make( who=request.user, new_value=f'Remove case {case.pk} from plan {plan.pk}') case.log_action(who=request.user, new_value=f'Remove from plan {plan.pk}') plan.delete_case(case=case) return JsonResponse({})
def form_invalid(self, form): return JsonResponseBadRequest( {'message': form_error_messages_to_list(form.errors)})
def update_case_run_status(request): """Update Case Run status.""" now = datetime.datetime.now() data = request.POST.copy() ctype = data.get("content_type") vtype = data.get('value_type', 'str') object_pk_str = data.get("object_pk") field = data.get('field') value = data.get('value') object_pk = [int(a) for a in object_pk_str.split(',')] if not field or not value or not object_pk or not ctype: return JsonResponseBadRequest({ 'message': 'Following fields are required - ' 'content_type, object_pk, field and value.' }) # Convert the value type # FIXME: Django bug here: update() keywords must be strings field = str(field) value, error = get_value_by_type(value, vtype) if error: return JsonResponseBadRequest({'message': error}) has_perms = check_permission(request, ctype) if not has_perms: return JsonResponseForbidden({'message': 'Permission Denied.'}) model = utils.get_model(ctype) targets = model._default_manager.filter(pk__in=object_pk) if not targets: return JsonResponseBadRequest({'message': 'No record found'}) if not hasattr(targets[0], field): return JsonResponseBadRequest( {'message': f'{ctype} has no field {field}'}) if hasattr(targets[0], 'log_action'): for t in targets: try: t.log_action(who=request.user, field=field, original_value=getattr(t, field), new_value=TestCaseRunStatus.id_to_name(value)) except (AttributeError, User.DoesNotExist): pass objects_update(targets, **{field: value}) if hasattr(model, 'mail_scene'): from tcms.core.mailto import mailto mail_context = model.mail_scene( objects=targets, field=field, value=value, ctype=ctype, object_pk=object_pk, ) if mail_context: mail_context['context']['user'] = request.user mailto(**mail_context) # Special hacking for updating test case run status if ctype == 'testruns.testcaserun' and field == 'case_run_status': for t in targets: field = 'close_date' t.log_action(who=request.user, field=field, original_value=getattr(t, field) or '', new_value=now) if t.tested_by != request.user: field = 'tested_by' t.log_action(who=request.user, field=field, original_value=getattr(t, field) or '', new_value=request.user) field = 'assignee' try: assignee = t.assginee if assignee != request.user: t.log_action(who=request.user, field=field, original_value=getattr(t, field) or '', new_value=request.user) # t.assignee = request.user t.save() except (AttributeError, User.DoesNotExist): pass targets.update(close_date=now, tested_by=request.user) return JsonResponse({})
def info(request): """Return misc information""" class Objects: __all__ = [ 'builds', 'categories', 'components', 'envs', 'env_groups', 'env_properties', 'env_values', 'tags', 'users', 'versions' ] def __init__(self, request, product_ids=None): self.request = request self.product_ids = product_ids self.internal_parameters = ('info_type', 'field', 'format') def builds(self): from tcms.management.models import TestBuild query = { 'product_ids': self.product_ids, 'is_active': self.request.GET.get('is_active') } return TestBuild.list(query) def categories(self): from tcms.testcases.models import TestCaseCategory return TestCaseCategory.objects.filter( product__in=self.product_ids) def components(self): from tcms.management.models import Component return Component.objects.filter(product__in=self.product_ids) def envs(self): from tcms.management.models import TestEnvironment return TestEnvironment.objects.filter(product__in=self.product_ids) def env_groups(self): from tcms.management.models import TCMSEnvGroup return TCMSEnvGroup.objects.all() def env_properties(self): from tcms.management.models import TCMSEnvGroup, TCMSEnvProperty if self.request.GET.get('env_group_id'): env_group = TCMSEnvGroup.objects.get( id=self.request.GET['env_group_id']) return env_group.property.all() else: return TCMSEnvProperty.objects.all() def env_values(self): from tcms.management.models import TCMSEnvValue return TCMSEnvValue.objects.filter( property__id=self.request.GET.get('env_property_id')) def tags(self): query = strip_parameters(request.GET, self.internal_parameters) tags = TestTag.objects # Generate the string combination, because we are using # case sensitive table if query.get('name__startswith'): seq = get_string_combinations(query['name__startswith']) criteria = reduce(operator.or_, (models.Q(name__startswith=item) for item in seq)) tags = tags.filter(criteria) del query['name__startswith'] tags = tags.filter(**query).distinct() return tags def users(self): from django.contrib.auth.models import User query = strip_parameters(self.request.GET, self.internal_parameters) return User.objects.filter(**query) def versions(self): from tcms.management.models import Version return Version.objects.filter(product__in=self.product_ids) product_ids = [] for s in request.GET.getlist('product_id'): if s.isdigit(): product_ids.append(int(s)) else: return JsonResponseBadRequest({ 'message': f'Invalid product id {s}. It must be a positive integer.' }) objects = Objects(request=request, product_ids=product_ids) obj = getattr(objects, request.GET['info_type'], None) if obj: if request.GET.get('format') == 'ulli': field = request.GET.get('field', 'name') response_str = '<ul>' for o in obj(): response_str += '<li>' + getattr(o, field, None) + '</li>' response_str += '</ul>' return HttpResponse(response_str) return JsonResponse(json.loads( serializers.serialize(request.GET.get('format', 'json'), obj(), fields=('name', 'value'))), safe=False) return JsonResponseBadRequest({'message': 'Unrecognizable infotype'})
def manage_case_run_issues(request, run_id): """Process the issues for case runs.""" class CaseRunIssueActions: __all__ = ['add', 'remove'] def __init__(self, request, run): self.request = request self.run = run def add(self): # TODO: make a migration for the permission if not self.request.user.has_perm('issuetracker.add_issue'): return JsonResponseForbidden({'message': 'Permission denied.'}) form = CaseRunIssueForm(request.GET) if not form.is_valid(): return JsonResponseBadRequest( {'message': form_error_messages_to_list(form)}) service = find_service(form.cleaned_data['tracker']) issue_key = form.cleaned_data['issue_key'] link_et = form.cleaned_data['link_external_tracker'] case_runs = form.cleaned_data['case_run'] # FIXME: maybe, make sense to validate in the form. if not all(case_run.run_id == self.run.pk for case_run in case_runs): return JsonResponseBadRequest({ 'message': f'Not all case runs belong to run {self.run.pk}.' }) try: for case_run in case_runs: service.add_issue(issue_key, case_run.case, case_run=case_run, add_case_to_issue=link_et) except ValidationError as e: logger.exception( 'Failed to add issue to case run %s. Error reported: %s', form.case_run.pk, str(e)) return JsonResponseBadRequest({'message': str(e)}) return self.run_issues_info(case_runs) def remove(self): if not self.request.user.has_perm('issuetracker.delete_issue'): return JsonResponseForbidden({'message': 'Permission denied.'}) class RemoveIssueForm(forms.Form): issue_key = forms.CharField() case_run = forms.ModelMultipleChoiceField( queryset=TestCaseRun.objects.all().only('pk'), error_messages={ 'required': 'Case run id is missed.', 'invalid_pk_value': 'Case run %(pk)s does not exist.', }, ) form = RemoveIssueForm(request.GET) if not form.is_valid(): return JsonResponseBadRequest( {'message': form_error_messages_to_list(form)}) issue_key = form.cleaned_data['issue_key'] case_runs = form.cleaned_data['case_run'] for case_run in case_runs: try: case_run.remove_issue(issue_key) except Exception: msg = 'Failed to remove issue {} from case run {}'.format( issue_key, case_run.pk) logger.exception(msg) return JsonResponseBadRequest({'message': msg}) return self.run_issues_info(case_runs) def run_issues_info(self, case_runs): """Return a JSON response including run's issues info""" return JsonResponse({ # The total number of issues this run has 'run_issues_count': self.run.get_issues_count(), # The number of issues each of case run has 'caserun_issues_count': Issue.count_by_case_run(list(map(attrgetter('pk'), case_runs))) }) try: run = get_object_or_404(TestRun, pk=run_id) except Http404: return JsonResponseNotFound( {'message': f'Test run {run_id} does not exist.'}) crba = CaseRunIssueActions(request=request, run=run) if not request.GET.get('a') in crba.__all__: return JsonResponseBadRequest({'message': 'Unrecognizable actions'}) func = getattr(crba, request.GET['a']) return func()