Including max_public_projects and max_private_projets in importing process
parent
b8fd768d01
commit
6fbf81c3d6
|
@ -36,6 +36,7 @@ from taiga.projects.models import Project, Membership
|
||||||
from taiga.projects.issues.models import Issue
|
from taiga.projects.issues.models import Issue
|
||||||
from taiga.projects.tasks.models import Task
|
from taiga.projects.tasks.models import Task
|
||||||
from taiga.projects.serializers import ProjectSerializer
|
from taiga.projects.serializers import ProjectSerializer
|
||||||
|
from taiga.users import services as users_service
|
||||||
|
|
||||||
from . import mixins
|
from . import mixins
|
||||||
from . import serializers
|
from . import serializers
|
||||||
|
@ -90,6 +91,10 @@ class ProjectImporterViewSet(mixins.ImportThrottlingPolicyMixin, CreateModelMixi
|
||||||
data = request.DATA.copy()
|
data = request.DATA.copy()
|
||||||
data['owner'] = data.get('owner', request.user.email)
|
data['owner'] = data.get('owner', request.user.email)
|
||||||
|
|
||||||
|
is_private = data.get('is_private', False)
|
||||||
|
if not users_service.has_available_slot_for_project(self.request.user, is_private=is_private):
|
||||||
|
raise exc.BadRequest(_("The user can't have more projects of this type"))
|
||||||
|
|
||||||
# Create Project
|
# Create Project
|
||||||
project_serialized = service.store_project(data)
|
project_serialized = service.store_project(data)
|
||||||
|
|
||||||
|
@ -202,17 +207,22 @@ class ProjectImporterViewSet(mixins.ImportThrottlingPolicyMixin, CreateModelMixi
|
||||||
|
|
||||||
try:
|
try:
|
||||||
dump = json.load(reader(dump))
|
dump = json.load(reader(dump))
|
||||||
|
is_private = dump["is_private"]
|
||||||
except Exception:
|
except Exception:
|
||||||
raise exc.WrongArguments(_("Invalid dump format"))
|
raise exc.WrongArguments(_("Invalid dump format"))
|
||||||
|
|
||||||
if Project.objects.filter(slug=dump['slug']).exists():
|
if Project.objects.filter(slug=dump['slug']).exists():
|
||||||
del dump['slug']
|
del dump['slug']
|
||||||
|
|
||||||
|
user = request.user
|
||||||
|
if not users_service.has_available_slot_for_project(user, is_private=is_private):
|
||||||
|
raise exc.BadRequest(_("The user can't have more projects of this type"))
|
||||||
|
|
||||||
if settings.CELERY_ENABLED:
|
if settings.CELERY_ENABLED:
|
||||||
task = tasks.load_project_dump.delay(request.user, dump)
|
task = tasks.load_project_dump.delay(user, dump)
|
||||||
return response.Accepted({"import_id": task.id})
|
return response.Accepted({"import_id": task.id})
|
||||||
|
|
||||||
project = dump_service.dict_to_project(dump, request.user.email)
|
project = dump_service.dict_to_project(dump, request.user)
|
||||||
response_data = ProjectSerializer(project).data
|
response_data = ProjectSerializer(project).data
|
||||||
return response.Created(response_data)
|
return response.Created(response_data)
|
||||||
|
|
||||||
|
|
|
@ -18,6 +18,7 @@
|
||||||
from django.utils.translation import ugettext as _
|
from django.utils.translation import ugettext as _
|
||||||
|
|
||||||
from taiga.projects.models import Membership
|
from taiga.projects.models import Membership
|
||||||
|
from taiga.users import services as users_service
|
||||||
|
|
||||||
from . import serializers
|
from . import serializers
|
||||||
from . import service
|
from . import service
|
||||||
|
@ -89,7 +90,9 @@ def store_tags_colors(project, data):
|
||||||
|
|
||||||
def dict_to_project(data, owner=None):
|
def dict_to_project(data, owner=None):
|
||||||
if owner:
|
if owner:
|
||||||
data["owner"] = owner
|
data["owner"] = owner.email
|
||||||
|
if not users_service.has_available_slot_for_project(owner, is_private=data["is_private"]):
|
||||||
|
raise TaigaImportError(_("The user can't have more projects of this type"))
|
||||||
|
|
||||||
project_serialized = service.store_project(data)
|
project_serialized = service.store_project(data)
|
||||||
|
|
||||||
|
|
|
@ -25,7 +25,7 @@ from taiga.projects.models import Project
|
||||||
from taiga.export_import.renderers import ExportRenderer
|
from taiga.export_import.renderers import ExportRenderer
|
||||||
from taiga.export_import.dump_service import dict_to_project, TaigaImportError
|
from taiga.export_import.dump_service import dict_to_project, TaigaImportError
|
||||||
from taiga.export_import.service import get_errors
|
from taiga.export_import.service import get_errors
|
||||||
|
from taiga.users.models import User
|
||||||
|
|
||||||
class Command(BaseCommand):
|
class Command(BaseCommand):
|
||||||
args = '<dump_file> <owner-email>'
|
args = '<dump_file> <owner-email>'
|
||||||
|
@ -58,7 +58,9 @@ class Command(BaseCommand):
|
||||||
except Project.DoesNotExist:
|
except Project.DoesNotExist:
|
||||||
pass
|
pass
|
||||||
signals.post_delete.receivers = receivers_back
|
signals.post_delete.receivers = receivers_back
|
||||||
dict_to_project(data, args[1])
|
|
||||||
|
user = User.objects.get(email=args[1])
|
||||||
|
dict_to_project(data, user)
|
||||||
except TaigaImportError as e:
|
except TaigaImportError as e:
|
||||||
print("ERROR:", end=" ")
|
print("ERROR:", end=" ")
|
||||||
print(e.message)
|
print(e.message)
|
||||||
|
|
|
@ -79,7 +79,7 @@ def delete_project_dump(project_id, project_slug, task_id):
|
||||||
@app.task
|
@app.task
|
||||||
def load_project_dump(user, dump):
|
def load_project_dump(user, dump):
|
||||||
try:
|
try:
|
||||||
project = dict_to_project(dump, user.email)
|
project = dict_to_project(dump, user)
|
||||||
except Exception:
|
except Exception:
|
||||||
ctx = {
|
ctx = {
|
||||||
"user": user,
|
"user": user,
|
||||||
|
|
|
@ -23,6 +23,7 @@ from django.core.urlresolvers import reverse
|
||||||
from django.core.files.base import ContentFile
|
from django.core.files.base import ContentFile
|
||||||
|
|
||||||
from taiga.base.utils import json
|
from taiga.base.utils import json
|
||||||
|
from taiga.export_import.dump_service import dict_to_project, TaigaImportError
|
||||||
from taiga.projects.models import Project, Membership
|
from taiga.projects.models import Project, Membership
|
||||||
from taiga.projects.issues.models import Issue
|
from taiga.projects.issues.models import Issue
|
||||||
from taiga.projects.userstories.models import UserStory
|
from taiga.projects.userstories.models import UserStory
|
||||||
|
@ -72,6 +73,85 @@ def test_valid_project_import_without_extra_data(client):
|
||||||
assert response_data["watchers"] == [user.email, user_watching.email]
|
assert response_data["watchers"] == [user.email, user_watching.email]
|
||||||
|
|
||||||
|
|
||||||
|
def test_valid_project_without_enough_public_projects_slots(client):
|
||||||
|
user = f.UserFactory.create(max_public_projects=0)
|
||||||
|
|
||||||
|
url = reverse("importer-list")
|
||||||
|
data = {
|
||||||
|
"slug": "public-project-without-slots",
|
||||||
|
"name": "Imported project",
|
||||||
|
"description": "Imported project",
|
||||||
|
"roles": [{"name": "Role"}],
|
||||||
|
"is_private": False
|
||||||
|
}
|
||||||
|
|
||||||
|
client.login(user)
|
||||||
|
response = client.json.post(url, json.dumps(data))
|
||||||
|
|
||||||
|
assert response.status_code == 400
|
||||||
|
assert "can't have more projects" in response.data["_error_message"]
|
||||||
|
assert Project.objects.filter(slug="public-project-without-slots").count() == 0
|
||||||
|
|
||||||
|
|
||||||
|
def test_valid_project_without_enough_private_projects_slots(client):
|
||||||
|
user = f.UserFactory.create(max_private_projects=0)
|
||||||
|
|
||||||
|
url = reverse("importer-list")
|
||||||
|
data = {
|
||||||
|
"slug": "private-project-without-slots",
|
||||||
|
"name": "Imported project",
|
||||||
|
"description": "Imported project",
|
||||||
|
"roles": [{"name": "Role"}],
|
||||||
|
"is_private": True
|
||||||
|
}
|
||||||
|
|
||||||
|
client.login(user)
|
||||||
|
response = client.json.post(url, json.dumps(data))
|
||||||
|
|
||||||
|
assert response.status_code == 400
|
||||||
|
assert "can't have more projects" in response.data["_error_message"]
|
||||||
|
assert Project.objects.filter(slug="private-project-without-slots").count() == 0
|
||||||
|
|
||||||
|
|
||||||
|
def test_valid_project_with_enough_public_projects_slots(client):
|
||||||
|
user = f.UserFactory.create(max_public_projects=1)
|
||||||
|
|
||||||
|
url = reverse("importer-list")
|
||||||
|
data = {
|
||||||
|
"slug": "public-project-with-slots",
|
||||||
|
"name": "Imported project",
|
||||||
|
"description": "Imported project",
|
||||||
|
"roles": [{"name": "Role"}],
|
||||||
|
"is_private": False
|
||||||
|
}
|
||||||
|
|
||||||
|
client.login(user)
|
||||||
|
response = client.json.post(url, json.dumps(data))
|
||||||
|
|
||||||
|
print(response.content)
|
||||||
|
assert response.status_code == 201
|
||||||
|
assert Project.objects.filter(slug="public-project-with-slots").count() == 1
|
||||||
|
|
||||||
|
|
||||||
|
def test_valid_project_with_enough_private_projects_slots(client):
|
||||||
|
user = f.UserFactory.create(max_private_projects=1)
|
||||||
|
|
||||||
|
url = reverse("importer-list")
|
||||||
|
data = {
|
||||||
|
"slug": "private-project-with-slots",
|
||||||
|
"name": "Imported project",
|
||||||
|
"description": "Imported project",
|
||||||
|
"roles": [{"name": "Role"}],
|
||||||
|
"is_private": True
|
||||||
|
}
|
||||||
|
|
||||||
|
client.login(user)
|
||||||
|
response = client.json.post(url, json.dumps(data))
|
||||||
|
|
||||||
|
assert response.status_code == 201
|
||||||
|
assert Project.objects.filter(slug="private-project-with-slots").count() == 1
|
||||||
|
|
||||||
|
|
||||||
def test_valid_project_import_with_not_existing_memberships(client):
|
def test_valid_project_import_with_not_existing_memberships(client):
|
||||||
user = f.UserFactory.create()
|
user = f.UserFactory.create()
|
||||||
client.login(user)
|
client.login(user)
|
||||||
|
@ -930,6 +1010,22 @@ def test_milestone_import_duplicated_milestone(client):
|
||||||
assert response_data["milestones"][0]["name"][0] == "Name duplicated for the project"
|
assert response_data["milestones"][0]["name"][0] == "Name duplicated for the project"
|
||||||
|
|
||||||
|
|
||||||
|
def test_dict_to_project_with_no_slots_available(client):
|
||||||
|
user = f.UserFactory.create(max_private_projects=0)
|
||||||
|
|
||||||
|
data = {
|
||||||
|
"slug": "valid-project",
|
||||||
|
"name": "Valid project",
|
||||||
|
"description": "Valid project desc",
|
||||||
|
"is_private": True
|
||||||
|
}
|
||||||
|
|
||||||
|
with pytest.raises(TaigaImportError) as excinfo:
|
||||||
|
project = dict_to_project(data, owner=user)
|
||||||
|
|
||||||
|
assert "can't have more projects" in str(excinfo.value)
|
||||||
|
|
||||||
|
|
||||||
def test_invalid_dump_import(client):
|
def test_invalid_dump_import(client):
|
||||||
user = f.UserFactory.create()
|
user = f.UserFactory.create()
|
||||||
client.login(user)
|
client.login(user)
|
||||||
|
@ -986,7 +1082,8 @@ def test_valid_dump_import_with_celery_disabled(client, settings):
|
||||||
data = ContentFile(bytes(json.dumps({
|
data = ContentFile(bytes(json.dumps({
|
||||||
"slug": "valid-project",
|
"slug": "valid-project",
|
||||||
"name": "Valid project",
|
"name": "Valid project",
|
||||||
"description": "Valid project desc"
|
"description": "Valid project desc",
|
||||||
|
"is_private": True
|
||||||
}), "utf-8"))
|
}), "utf-8"))
|
||||||
data.name = "test"
|
data.name = "test"
|
||||||
|
|
||||||
|
@ -1008,7 +1105,8 @@ def test_valid_dump_import_with_celery_enabled(client, settings):
|
||||||
data = ContentFile(bytes(json.dumps({
|
data = ContentFile(bytes(json.dumps({
|
||||||
"slug": "valid-project",
|
"slug": "valid-project",
|
||||||
"name": "Valid project",
|
"name": "Valid project",
|
||||||
"description": "Valid project desc"
|
"description": "Valid project desc",
|
||||||
|
"is_private": True
|
||||||
}), "utf-8"))
|
}), "utf-8"))
|
||||||
data.name = "test"
|
data.name = "test"
|
||||||
|
|
||||||
|
@ -1028,7 +1126,8 @@ def test_dump_import_duplicated_project(client):
|
||||||
data = ContentFile(bytes(json.dumps({
|
data = ContentFile(bytes(json.dumps({
|
||||||
"slug": project.slug,
|
"slug": project.slug,
|
||||||
"name": "Test import",
|
"name": "Test import",
|
||||||
"description": "Valid project desc"
|
"description": "Valid project desc",
|
||||||
|
"is_private": True
|
||||||
}), "utf-8"))
|
}), "utf-8"))
|
||||||
data.name = "test"
|
data.name = "test"
|
||||||
|
|
||||||
|
@ -1051,7 +1150,8 @@ def test_dump_import_throttling(client, settings):
|
||||||
data = ContentFile(bytes(json.dumps({
|
data = ContentFile(bytes(json.dumps({
|
||||||
"slug": project.slug,
|
"slug": project.slug,
|
||||||
"name": "Test import",
|
"name": "Test import",
|
||||||
"description": "Valid project desc"
|
"description": "Valid project desc",
|
||||||
|
"is_private": True
|
||||||
}), "utf-8"))
|
}), "utf-8"))
|
||||||
data.name = "test"
|
data.name = "test"
|
||||||
|
|
||||||
|
@ -1059,3 +1159,43 @@ def test_dump_import_throttling(client, settings):
|
||||||
assert response.status_code == 201
|
assert response.status_code == 201
|
||||||
response = client.post(url, {'dump': data})
|
response = client.post(url, {'dump': data})
|
||||||
assert response.status_code == 429
|
assert response.status_code == 429
|
||||||
|
|
||||||
|
|
||||||
|
def test_valid_dump_import_without_enough_public_projects_slots(client):
|
||||||
|
user = f.UserFactory.create(max_public_projects=0)
|
||||||
|
client.login(user)
|
||||||
|
|
||||||
|
url = reverse("importer-load-dump")
|
||||||
|
|
||||||
|
data = ContentFile(bytes(json.dumps({
|
||||||
|
"slug": "public-project-without-slots",
|
||||||
|
"name": "Valid project",
|
||||||
|
"description": "Valid project desc",
|
||||||
|
"is_private": False
|
||||||
|
}), "utf-8"))
|
||||||
|
data.name = "test"
|
||||||
|
|
||||||
|
response = client.post(url, {'dump': data})
|
||||||
|
assert response.status_code == 400
|
||||||
|
assert "can't have more projects" in response.data["_error_message"]
|
||||||
|
assert Project.objects.filter(slug="public-project-without-slots").count() == 0
|
||||||
|
|
||||||
|
|
||||||
|
def test_valid_dump_import_without_enough_private_projects_slots(client):
|
||||||
|
user = f.UserFactory.create(max_private_projects=0)
|
||||||
|
client.login(user)
|
||||||
|
|
||||||
|
url = reverse("importer-load-dump")
|
||||||
|
|
||||||
|
data = ContentFile(bytes(json.dumps({
|
||||||
|
"slug": "private-project-without-slots",
|
||||||
|
"name": "Valid project",
|
||||||
|
"description": "Valid project desc",
|
||||||
|
"is_private": True
|
||||||
|
}), "utf-8"))
|
||||||
|
data.name = "test"
|
||||||
|
|
||||||
|
response = client.post(url, {'dump': data})
|
||||||
|
assert response.status_code == 400
|
||||||
|
assert "can't have more projects" in response.data["_error_message"]
|
||||||
|
assert Project.objects.filter(slug="private-project-without-slots").count() == 0
|
||||||
|
|
Loading…
Reference in New Issue