Skip to content

Commit

Permalink
Merge pull request #124 from MerginMaps/rename_project_endpoint
Browse files Browse the repository at this point in the history
Add endpoint to rename a project
  • Loading branch information
luxusko authored Oct 24, 2023
2 parents 8323fee + 83e0ac1 commit 9201e0c
Show file tree
Hide file tree
Showing 5 changed files with 139 additions and 8 deletions.
2 changes: 1 addition & 1 deletion server/mergin/sync/public_api.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -977,7 +977,7 @@ components:
UnsupportedMediaType:
description: Payload format is in an unsupported format.
ConflictResp:
description: Request could not be processed becuase of conflict in resources
description: Request could not be processed because of conflict in resources
UnprocessableEntity:
description: Request was correct and yet server could not process it
ProjectsLimitHitResp:
Expand Down
65 changes: 61 additions & 4 deletions server/mergin/sync/public_api_v2.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -10,22 +10,73 @@ tags:
description: Mergin project
paths:
/projects/{id}:
parameters:
- $ref: "#/components/parameters/ProjectId"
delete:
tags:
- project
summary: Delete project immediately
operationId: delete_project_now
parameters:
- $ref: "#/components/parameters/ProjectId"
responses:
"204":
$ref: "#/components/responses/NoContent"
"400":
$ref: "#/components/responses/BadRequest"
"401":
$ref: "#/components/responses/Unauthorized"
"403":
$ref: "#/components/responses/Forbidden"
"404":
$ref: "#/components/responses/NotFound"
x-openapi-router-controller: mergin.sync.public_api_v2_controller
patch:
tags:
- project
summary: Update project
operationId: update_project
requestBody:
required: true
content:
application/json:
schema:
type: object
required:
- name
properties:
name:
type: string
example: survey
responses:
"204":
$ref: "#/components/responses/NoContent"
"400":
description: Invalid project name
content:
application/json:
schema:
type: object
required:
- code
- detail
properties:
code:
type: string
enum:
- InvalidProjectName
example: InvalidProjectName
detail:
type: string
enum:
- "Entered project name is invalid"
example: "Entered project name is invalid"
"401":
$ref: "#/components/responses/Unauthorized"
"403":
$ref: "#/components/responses/Forbidden"
"404":
$ref: "#/components/responses/NotFound"
"409":
$ref: "#/components/responses/Conflict"
x-openapi-router-controller: mergin.sync.public_api_v2_controller
/projects/{id}/scheduleDelete:
post:
Expand All @@ -40,8 +91,10 @@ paths:
$ref: "#/components/responses/NoContent"
"400":
$ref: "#/components/responses/BadRequest"
"403":
"401":
$ref: "#/components/responses/Unauthorized"
"403":
$ref: "#/components/responses/Forbidden"
"404":
$ref: "#/components/responses/NotFound"
x-openapi-router-controller: mergin.sync.public_api_v2_controller
Expand All @@ -50,11 +103,15 @@ components:
NoContent:
description: No content
BadRequest:
description: Invalid request.
description: Invalid request
Unauthorized:
description: Authentication information is missing or invalid
Forbidden:
description: Invalid token
NotFound:
description: Not found
Conflict:
description: Conflict
parameters:
ProjectId:
name: id
Expand Down
34 changes: 32 additions & 2 deletions server/mergin/sync/public_api_v2_controller.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,15 @@

from datetime import datetime

from connexion import NoContent
from flask import abort
from connexion import NoContent, request
from flask import abort, jsonify
from flask_login import current_user

from mergin import db
from mergin.auth import auth_required
from mergin.sync.models import Project
from mergin.sync.permissions import ProjectPermissions, require_project_by_uuid
from mergin.sync.utils import is_name_allowed


@auth_required
Expand Down Expand Up @@ -43,3 +44,32 @@ def delete_project_now(id_):
db.session.commit()

return NoContent, 204


@auth_required
def update_project(id_):
"""Rename project
:param id_: Project_id
:type id_: str
"""
project = require_project_by_uuid(id_, ProjectPermissions.Update)
new_name = request.json["name"]

if not is_name_allowed(new_name):
return (
jsonify(
code="InvalidProjectName", detail="Entered project name is invalid"
),
400,
)
new_name_exists = Project.query.filter_by(
workspace_id=project.workspace_id, name=new_name
).first()
if new_name_exists:
abort(409, "Name already exist within workspace")

project.name = new_name.strip()
db.session.commit()

return NoContent, 204
24 changes: 23 additions & 1 deletion server/mergin/tests/test_auth.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,13 @@
from ..auth.tasks import prune_removed_users
from .. import db
from ..sync.models import Project
from . import test_workspace_id, json_headers, DEFAULT_USER, test_workspace_name
from . import (
test_workspace_id,
json_headers,
DEFAULT_USER,
test_workspace_name,
test_project,
)
from .utils import add_user, login_as_admin, login


Expand Down Expand Up @@ -718,3 +724,19 @@ def test_admin_login(client, data, expected):
add_user("user", "user")
resp = client.post("/app/admin/login", data=json.dumps(data), headers=json_headers)
assert resp.status_code == expected


def test_update_project_v2(client):
project = Project.query.filter_by(
workspace_id=test_workspace_id, name=test_project
).first()
data = {"name": "new_project_name"}
resp = client.patch(f"v2/projects/{project.id}", json=data)
assert resp.status_code == 401
user = add_user("test", "test")
login(client, "test", "test")
resp = client.patch(f"v2/projects/{project.id}", json=data)
assert resp.status_code == 403
login_as_admin(client)
resp = client.patch(f"v2/projects/{project.id}", json=data)
assert resp.status_code == 204
22 changes: 22 additions & 0 deletions server/mergin/tests/test_public_api_v2.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,3 +37,25 @@ def test_delete_after_schedule(client):
assert response.status_code == 204
response = client.delete(f"v2/projects/{project.id}")
assert response.status_code == 204


def test_rename_project(client):
project = Project.query.filter_by(
workspace_id=test_workspace_id, name=test_project
).first()
data = {"name": "new_project_name"}
response = client.patch(f"v2/projects/{project.id}", json=data)
assert response.status_code == 204
assert project.name == "new_project_name"
# name already exists
response = client.patch(f"v2/projects/{project.id}", json=data)
assert response.status_code == 409
# invalid project name
response = client.patch(f"v2/projects/{project.id}", json={"name": ".new_name"})
assert response.status_code == 400
assert response.json["code"] == "InvalidProjectName"
response = client.patch(
f"v2/projects/{project.id}", json={"name": " new_project_name"}
)
assert response.status_code == 400
assert response.json["code"] == "InvalidProjectName"

0 comments on commit 9201e0c

Please sign in to comment.