API versioning using accept header (#4239)

main
Maria Khrustaleva 4 years ago committed by GitHub
parent f0b2a75125
commit 83126c7b5a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -73,7 +73,7 @@ jobs:
./opa test cvat/apps/iam/rules
- name: Running REST API tests
env:
API_ABOUT_PAGE: "localhost:8080/api/v1/server/about"
API_ABOUT_PAGE: "localhost:8080/api/server/about"
run: |
docker-compose -f docker-compose.yml -f docker-compose.dev.yml -f components/serverless/docker-compose.serverless.yml up -d
/bin/bash -c 'while [[ "$(curl -s -o /dev/null -w ''%{http_code}'' ${API_ABOUT_PAGE})" != "401" ]]; do sleep 5; done'
@ -182,10 +182,10 @@ jobs:
DJANGO_SU_NAME: 'admin'
DJANGO_SU_EMAIL: 'admin@localhost.company'
DJANGO_SU_PASSWORD: '12qwaszx'
API_ABOUT_PAGE: "localhost:8080/api/v1/server/about"
API_ABOUT_PAGE: "localhost:8080/api/server/about"
run: |
docker-compose -f docker-compose.yml -f docker-compose.dev.yml -f components/serverless/docker-compose.serverless.yml -f tests/docker-compose.file_share.yml up -d
/bin/bash -c 'while [[ "$(curl -s -o /dev/null -w ''%{http_code}'' ${API_ABOUT_PAGE})" != "401" ]]; do sleep 5; done'
/bin/bash -c 'while [[ $(curl -s -o /dev/null -w "%{http_code}" ${API_ABOUT_PAGE}) != "401" ]]; do sleep 5; done'
docker exec -i cvat /bin/bash -c "echo \"from django.contrib.auth.models import User; User.objects.create_superuser('${DJANGO_SU_NAME}', '${DJANGO_SU_EMAIL}', '${DJANGO_SU_PASSWORD}')\" | python3 ~/manage.py shell"
cd ./tests
npm ci

@ -28,11 +28,11 @@ jobs:
DJANGO_SU_NAME: 'admin'
DJANGO_SU_EMAIL: 'admin@localhost.company'
DJANGO_SU_PASSWORD: '12qwaszx'
API_ABOUT_PAGE: "localhost:8080/api/v1/server/about"
API_ABOUT_PAGE: "localhost:8080/api/server/about"
run: |
docker-compose -f docker-compose.yml -f docker-compose.dev.yml build
docker-compose -f docker-compose.yml -f docker-compose.dev.yml -f components/serverless/docker-compose.serverless.yml -f tests/docker-compose.file_share.yml up -d
/bin/bash -c 'while [[ "$(curl -s -o /dev/null -w ''%{http_code}'' ${API_ABOUT_PAGE})" != "401" ]]; do sleep 5; done'
/bin/bash -c 'while [[ $(curl -s -o /dev/null -w "%{http_code}" ${API_ABOUT_PAGE}) != "401" ]]; do sleep 5; done'
docker exec -i cvat /bin/bash -c "echo \"from django.contrib.auth.models import User; User.objects.create_superuser('${DJANGO_SU_NAME}', '${DJANGO_SU_EMAIL}', '${DJANGO_SU_PASSWORD}')\" | python3 ~/manage.py shell"
cd ./tests
npm ci

@ -16,10 +16,10 @@ jobs:
DJANGO_SU_NAME: "admin"
DJANGO_SU_EMAIL: "admin@localhost.company"
DJANGO_SU_PASSWORD: "12qwaszx"
API_ABOUT_PAGE: "localhost:8080/api/v1/server/about"
API_ABOUT_PAGE: "localhost:8080/api/server/about"
run: |
docker-compose -f docker-compose.yml -f docker-compose.dev.yml -f ./tests/docker-compose.email.yml -f tests/docker-compose.file_share.yml -f components/serverless/docker-compose.serverless.yml up -d --build
/bin/bash -c 'while [[ "$(curl -s -o /dev/null -w ''%{http_code}'' ${API_ABOUT_PAGE})" != "401" ]]; do sleep 5; done'
/bin/bash -c 'while [[ $(curl -s -o /dev/null -w "%{http_code}" ${API_ABOUT_PAGE}) != "401" ]]; do sleep 5; done'
docker exec -i cvat /bin/bash -c "echo \"from django.contrib.auth.models import User; User.objects.create_superuser('${DJANGO_SU_NAME}', '${DJANGO_SU_EMAIL}', '${DJANGO_SU_PASSWORD}')\" | python3 ~/manage.py shell"
- name: End-to-end testing
run: |

@ -35,6 +35,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Changed
- Users don't have access to a task object anymore if they are assigneed only on some jobs of the task (<https://github.com/openvinotoolkit/cvat/pull/3788>)
- Different resources (tasks, projects) are not visible anymore for all CVAT instance users by default (<https://github.com/openvinotoolkit/cvat/pull/3788>)
- API versioning scheme: using accept header versioning instead of namespace versioning (<https://github.com/openvinotoolkit/cvat/pull/4239>)
- Replaced 'django_sendfile' with 'django_sendfile2' (<https://github.com/openvinotoolkit/cvat/pull/4267>)
### Deprecated

@ -1,9 +1,9 @@
// Copyright (C) 2019-2021 Intel Corporation
// Copyright (C) 2019-2022 Intel Corporation
//
// SPDX-License-Identifier: MIT
module.exports = {
backendAPI: '/api/v1',
backendAPI: '/api',
proxy: false,
organizationID: null,
origin: '',

@ -1,4 +1,4 @@
// Copyright (C) 2019-2021 Intel Corporation
// Copyright (C) 2022 Intel Corporation
//
// SPDX-License-Identifier: MIT

@ -55,7 +55,7 @@ const usersDummyData = {
previous: null,
results: [
{
url: 'http://localhost:7000/api/v1/users/1',
url: 'http://localhost:7000/api/users/1',
id: 1,
username: 'admin',
first_name: '',
@ -69,7 +69,7 @@ const usersDummyData = {
date_joined: '2019-05-13T15:33:17.833200+03:00',
},
{
url: 'http://localhost:7000/api/v1/users/2',
url: 'http://localhost:7000/api/users/2',
id: 2,
username: 'bsekache',
first_name: '',
@ -149,18 +149,18 @@ const projectsDummyData = {
previous: null,
results: [
{
url: 'http://192.168.0.139:7000/api/v1/projects/6',
url: 'http://192.168.0.139:7000/api/projects/6',
id: 6,
name: 'Some empty project',
labels: [],
tasks: [],
owner: {
url: 'http://localhost:7000/api/v1/users/2',
url: 'http://localhost:7000/api/users/2',
id: 2,
username: 'bsekache',
},
assignee: {
url: 'http://localhost:7000/api/v1/users/2',
url: 'http://localhost:7000/api/users/2',
id: 2,
username: 'bsekache',
},
@ -170,7 +170,7 @@ const projectsDummyData = {
status: 'annotation',
},
{
url: 'http://192.168.0.139:7000/api/v1/projects/1',
url: 'http://192.168.0.139:7000/api/projects/1',
id: 2,
name: 'Test project with roads',
labels: [
@ -198,13 +198,13 @@ const projectsDummyData = {
],
tasks: [
{
url: 'http://192.168.0.139:7000/api/v1/tasks/2',
url: 'http://192.168.0.139:7000/api/tasks/2',
id: 2,
name: 'road 1',
project_id: 1,
mode: 'interpolation',
owner: {
url: 'http://localhost:7000/api/v1/users/1',
url: 'http://localhost:7000/api/users/1',
id: 1,
username: 'admin',
},
@ -239,7 +239,7 @@ const projectsDummyData = {
stop_frame: 99,
jobs: [
{
url: 'http://192.168.0.139:7000/api/v1/jobs/1',
url: 'http://192.168.0.139:7000/api/jobs/1',
id: 1,
assignee: null,
status: 'completed',
@ -253,7 +253,7 @@ const projectsDummyData = {
stop_frame: 194,
jobs: [
{
url: 'http://192.168.0.139:7000/api/v1/jobs/2',
url: 'http://192.168.0.139:7000/api/jobs/2',
id: 2,
assignee: null,
status: 'completed',
@ -267,7 +267,7 @@ const projectsDummyData = {
stop_frame: 289,
jobs: [
{
url: 'http://192.168.0.139:7000/api/v1/jobs/3',
url: 'http://192.168.0.139:7000/api/jobs/3',
id: 3,
assignee: null,
status: 'completed',
@ -281,7 +281,7 @@ const projectsDummyData = {
stop_frame: 384,
jobs: [
{
url: 'http://192.168.0.139:7000/api/v1/jobs/4',
url: 'http://192.168.0.139:7000/api/jobs/4',
id: 4,
assignee: null,
status: 'completed',
@ -295,7 +295,7 @@ const projectsDummyData = {
stop_frame: 431,
jobs: [
{
url: 'http://192.168.0.139:7000/api/v1/jobs/5',
url: 'http://192.168.0.139:7000/api/jobs/5',
id: 5,
assignee: null,
status: 'completed',
@ -314,7 +314,7 @@ const projectsDummyData = {
},
],
owner: {
url: 'http://localhost:7000/api/v1/users/1',
url: 'http://localhost:7000/api/users/1',
id: 1,
username: 'admin',
},
@ -333,13 +333,13 @@ const tasksDummyData = {
previous: null,
results: [
{
url: 'http://localhost:7000/api/v1/tasks/102',
url: 'http://localhost:7000/api/tasks/102',
id: 102,
name: 'Test',
size: 1,
mode: 'annotation',
owner: {
url: 'http://localhost:7000/api/v1/users/1',
url: 'http://localhost:7000/api/users/1',
id: 1,
username: 'admin',
},
@ -366,7 +366,7 @@ const tasksDummyData = {
stop_frame: 0,
jobs: [
{
url: 'http://localhost:7000/api/v1/jobs/112',
url: 'http://localhost:7000/api/jobs/112',
id: 112,
assignee: null,
status: 'annotation',
@ -382,13 +382,13 @@ const tasksDummyData = {
frame_filter: '',
},
{
url: 'http://localhost:7000/api/v1/tasks/100',
url: 'http://localhost:7000/api/tasks/100',
id: 100,
name: 'Image Task',
size: 9,
mode: 'annotation',
owner: {
url: 'http://localhost:7000/api/v1/users/1',
url: 'http://localhost:7000/api/users/1',
id: 1,
username: 'admin',
},
@ -420,7 +420,7 @@ const tasksDummyData = {
stop_frame: 8,
jobs: [
{
url: 'http://localhost:7000/api/v1/jobs/100',
url: 'http://localhost:7000/api/jobs/100',
id: 100,
assignee: null,
status: 'annotation',
@ -436,13 +436,13 @@ const tasksDummyData = {
frame_filter: '',
},
{
url: 'http://localhost:7000/api/v1/tasks/10',
url: 'http://localhost:7000/api/tasks/10',
id: 101,
name: 'Video Task',
size: 5002,
mode: 'interpolation',
owner: {
url: 'http://localhost:7000/api/v1/users/1',
url: 'http://localhost:7000/api/users/1',
id: 1,
username: 'admin',
},
@ -628,7 +628,7 @@ const tasksDummyData = {
stop_frame: 499,
jobs: [
{
url: 'http://localhost:7000/api/v1/jobs/10',
url: 'http://localhost:7000/api/jobs/10',
id: 101,
assignee: null,
status: 'annotation',
@ -642,7 +642,7 @@ const tasksDummyData = {
stop_frame: 994,
jobs: [
{
url: 'http://localhost:7000/api/v1/jobs/11',
url: 'http://localhost:7000/api/jobs/11',
id: 102,
assignee: null,
status: 'annotation',
@ -656,7 +656,7 @@ const tasksDummyData = {
stop_frame: 1489,
jobs: [
{
url: 'http://localhost:7000/api/v1/jobs/12',
url: 'http://localhost:7000/api/jobs/12',
id: 103,
assignee: null,
status: 'annotation',
@ -670,7 +670,7 @@ const tasksDummyData = {
stop_frame: 1984,
jobs: [
{
url: 'http://localhost:7000/api/v1/jobs/13',
url: 'http://localhost:7000/api/jobs/13',
id: 104,
assignee: null,
status: 'annotation',
@ -684,7 +684,7 @@ const tasksDummyData = {
stop_frame: 2479,
jobs: [
{
url: 'http://localhost:7000/api/v1/jobs/14',
url: 'http://localhost:7000/api/jobs/14',
id: 105,
assignee: null,
status: 'annotation',
@ -698,7 +698,7 @@ const tasksDummyData = {
stop_frame: 2974,
jobs: [
{
url: 'http://localhost:7000/api/v1/jobs/15',
url: 'http://localhost:7000/api/jobs/15',
id: 106,
assignee: null,
status: 'annotation',
@ -712,7 +712,7 @@ const tasksDummyData = {
stop_frame: 3469,
jobs: [
{
url: 'http://localhost:7000/api/v1/jobs/16',
url: 'http://localhost:7000/api/jobs/16',
id: 107,
assignee: null,
status: 'annotation',
@ -726,7 +726,7 @@ const tasksDummyData = {
stop_frame: 3964,
jobs: [
{
url: 'http://localhost:7000/api/v1/jobs/17',
url: 'http://localhost:7000/api/jobs/17',
id: 108,
assignee: null,
status: 'annotation',
@ -740,7 +740,7 @@ const tasksDummyData = {
stop_frame: 4459,
jobs: [
{
url: 'http://localhost:7000/api/v1/jobs/18',
url: 'http://localhost:7000/api/jobs/18',
id: 109,
assignee: null,
status: 'annotation',
@ -754,7 +754,7 @@ const tasksDummyData = {
stop_frame: 4954,
jobs: [
{
url: 'http://localhost:7000/api/v1/jobs/19',
url: 'http://localhost:7000/api/jobs/19',
id: 110,
assignee: null,
status: 'annotation',
@ -768,7 +768,7 @@ const tasksDummyData = {
stop_frame: 5001,
jobs: [
{
url: 'http://localhost:7000/api/v1/jobs/20',
url: 'http://localhost:7000/api/jobs/20',
id: 111,
assignee: null,
status: 'annotation',
@ -784,13 +784,13 @@ const tasksDummyData = {
frame_filter: '',
},
{
url: 'http://localhost:7000/api/v1/tasks/3',
url: 'http://localhost:7000/api/tasks/3',
id: 3,
name: 'Test Task',
size: 5002,
mode: 'interpolation',
owner: {
url: 'http://localhost:7000/api/v1/users/2',
url: 'http://localhost:7000/api/users/2',
id: 2,
username: 'bsekache',
},
@ -976,7 +976,7 @@ const tasksDummyData = {
stop_frame: 4999,
jobs: [
{
url: 'http://localhost:7000/api/v1/jobs/3',
url: 'http://localhost:7000/api/jobs/3',
id: 3,
assignee: null,
status: 'annotation',
@ -990,7 +990,7 @@ const tasksDummyData = {
stop_frame: 5001,
jobs: [
{
url: 'http://localhost:7000/api/v1/jobs/4',
url: 'http://localhost:7000/api/jobs/4',
id: 4,
assignee: null,
status: 'annotation',
@ -1003,13 +1003,13 @@ const tasksDummyData = {
image_quality: 50,
},
{
url: 'http://localhost:7000/api/v1/tasks/2',
url: 'http://localhost:7000/api/tasks/2',
id: 2,
name: 'Video',
size: 75,
mode: 'interpolation',
owner: {
url: 'http://localhost:7000/api/v1/users/1',
url: 'http://localhost:7000/api/users/1',
id: 1,
username: 'admin',
},
@ -1196,7 +1196,7 @@ const tasksDummyData = {
stop_frame: 74,
jobs: [
{
url: 'http://localhost:7000/api/v1/jobs/2',
url: 'http://localhost:7000/api/jobs/2',
id: 2,
assignee: null,
status: 'annotation',
@ -1209,13 +1209,13 @@ const tasksDummyData = {
image_quality: 50,
},
{
url: 'http://localhost:7000/api/v1/tasks/1',
url: 'http://localhost:7000/api/tasks/1',
id: 1,
name: 'Labels Set',
size: 9,
mode: 'annotation',
owner: {
url: 'http://localhost:7000/api/v1/users/1',
url: 'http://localhost:7000/api/users/1',
id: 1,
username: 'admin',
},
@ -1401,7 +1401,7 @@ const tasksDummyData = {
stop_frame: 8,
jobs: [
{
url: 'http://localhost:7000/api/v1/jobs/1',
url: 'http://localhost:7000/api/jobs/1',
id: 1,
assignee: null,
status: 'annotation',
@ -2695,7 +2695,7 @@ const cloudStoragesDummyData = {
{
id: 3,
owner: {
url: 'http://localhost:7000/api/v1/users/1',
url: 'http://localhost:7000/api/users/1',
id: 1,
username: 'maya',
first_name: '',
@ -2716,7 +2716,7 @@ const cloudStoragesDummyData = {
{
id: 2,
owner: {
url: 'http://localhost:7000/api/v1/users/1',
url: 'http://localhost:7000/api/users/1',
id: 1,
username: 'maya',
first_name: '',
@ -2737,7 +2737,7 @@ const cloudStoragesDummyData = {
{
id: 1,
owner: {
url: 'http://localhost:7000/api/v1/users/1',
url: 'http://localhost:7000/api/users/1',
id: 1,
username: 'maya',
first_name: '',

@ -113,7 +113,7 @@ class ServerProxy {
const id = Math.max(...projectsDummyData.results.map((el) => el.id)) + 1;
projectsDummyData.results.push({
id,
url: `http://localhost:7000/api/v1/projects/${id}`,
url: `http://localhost:7000/api/projects/${id}`,
name: projectData.name,
owner: 1,
assignee: null,
@ -179,7 +179,7 @@ class ServerProxy {
const id = Math.max(...tasksDummyData.results.map((el) => el.id)) + 1;
tasksDummyData.results.push({
id,
url: `http://localhost:7000/api/v1/tasks/${id}`,
url: `http://localhost:7000/api/tasks/${id}`,
name: taskData.name,
project_id: taskData.project_id || null,
size: 5000,

@ -1,4 +1,4 @@
// Copyright (C) 2020-2021 Intel Corporation
// Copyright (C) 2020-2022 Intel Corporation
//
// SPDX-License-Identifier: MIT
@ -6,7 +6,7 @@ import _cvat from 'cvat-core/src/api';
const cvat: any = _cvat;
cvat.config.backendAPI = '/api/v1';
cvat.config.backendAPI = '/api';
cvat.config.origin = window.location.origin;
cvat.config.uploadChunkSize = 100;
(globalThis as any).cvat = cvat;

@ -1,5 +1,5 @@
# Copyright (C) 2020 Intel Corporation
# Copyright (C) 2020-2022 Intel Corporation
#
# SPDX-License-Identifier: MIT
@ -69,38 +69,38 @@ class _DbTestBase(APITestCase):
cls.user = admin
def _put_api_v1_task_id_annotations(self, tid, data):
def _put_api_v2_task_id_annotations(self, tid, data):
with ForceLogin(self.user, self.client):
response = self.client.put("/api/v1/tasks/%s/annotations" % tid,
response = self.client.put("/api/tasks/%s/annotations" % tid,
data=data, format="json")
return response
def _put_api_v1_job_id_annotations(self, jid, data):
def _put_api_v2_job_id_annotations(self, jid, data):
with ForceLogin(self.user, self.client):
response = self.client.put("/api/v1/jobs/%s/annotations" % jid,
response = self.client.put("/api/jobs/%s/annotations" % jid,
data=data, format="json")
return response
def _create_task(self, data, image_data):
with ForceLogin(self.user, self.client):
response = self.client.post('/api/v1/tasks', data=data, format="json")
response = self.client.post('/api/tasks', data=data, format="json")
assert response.status_code == status.HTTP_201_CREATED, response.status_code
tid = response.data["id"]
response = self.client.post("/api/v1/tasks/%s/data" % tid,
response = self.client.post("/api/tasks/%s/data" % tid,
data=image_data)
assert response.status_code == status.HTTP_202_ACCEPTED, response.status_code
response = self.client.get("/api/v1/tasks/%s" % tid)
response = self.client.get("/api/tasks/%s" % tid)
task = response.data
return task
class TaskExportTest(_DbTestBase):
def _generate_custom_annotations(self, annotations, task):
self._put_api_v1_task_id_annotations(task["id"], annotations)
self._put_api_v2_task_id_annotations(task["id"], annotations)
return annotations
def _generate_annotations(self, task):
@ -497,7 +497,7 @@ class TaskExportTest(_DbTestBase):
},
]
}
self._put_api_v1_job_id_annotations(
self._put_api_v2_job_id_annotations(
task["segments"][2]["jobs"][0]["id"], annotations)
task_ann = TaskAnnotation(task["id"])
@ -602,7 +602,7 @@ class FrameMatchingTest(_DbTestBase):
class TaskAnnotationsImportTest(_DbTestBase):
def _generate_custom_annotations(self, annotations, task):
self._put_api_v1_task_id_annotations(task["id"], annotations)
self._put_api_v2_task_id_annotations(task["id"], annotations)
return annotations
def _generate_task_images(self, count, name="image", **image_params):

@ -1,4 +1,4 @@
# Copyright (C) 2021 Intel Corporation
# Copyright (C) 2021-2022 Intel Corporation
#
# SPDX-License-Identifier: MIT
@ -122,16 +122,16 @@ class _DbTestBase(APITestCase):
cls.admin = user_admin
cls.user = user_dummy
def _put_api_v1_task_id_annotations(self, tid, data):
def _put_api_v2_task_id_annotations(self, tid, data):
with ForceLogin(self.admin, self.client):
response = self.client.put("/api/v1/tasks/%s/annotations" % tid,
response = self.client.put("/api/tasks/%s/annotations" % tid,
data=data, format="json")
return response
def _put_api_v1_job_id_annotations(self, jid, data):
def _put_api_v2_job_id_annotations(self, jid, data):
with ForceLogin(self.admin, self.client):
response = self.client.put("/api/v1/jobs/%s/annotations" % jid,
response = self.client.put("/api/jobs/%s/annotations" % jid,
data=data, format="json")
return response
@ -150,22 +150,22 @@ class _DbTestBase(APITestCase):
def _create_task(self, data, image_data):
with ForceLogin(self.user, self.client):
response = self.client.post('/api/v1/tasks', data=data, format="json")
response = self.client.post('/api/tasks', data=data, format="json")
assert response.status_code == status.HTTP_201_CREATED, response.status_code
tid = response.data["id"]
response = self.client.post("/api/v1/tasks/%s/data" % tid,
response = self.client.post("/api/tasks/%s/data" % tid,
data=image_data)
assert response.status_code == status.HTTP_202_ACCEPTED, response.status_code
response = self.client.get("/api/v1/tasks/%s" % tid)
response = self.client.get("/api/tasks/%s" % tid)
task = response.data
return task
def _create_project(self, data):
with ForceLogin(self.user, self.client):
response = self.client.post('/api/v1/projects', data=data, format="json")
response = self.client.post('/api/projects', data=data, format="json")
assert response.status_code == status.HTTP_201_CREATED, response.status_code
project = response.data
@ -173,7 +173,7 @@ class _DbTestBase(APITestCase):
def _get_jobs(self, task_id):
with ForceLogin(self.admin, self.client):
response = self.client.get("/api/v1/tasks/{}/jobs".format(task_id))
response = self.client.get("/api/tasks/{}/jobs".format(task_id))
return response.data
def _get_request(self, path, user):
@ -237,7 +237,7 @@ class _DbTestBase(APITestCase):
"spec_id": spec_id,
"value": value,
})
response = self._put_api_v1_task_id_annotations(task["id"], tmp_annotations)
response = self._put_api_v2_task_id_annotations(task["id"], tmp_annotations)
self.assertEqual(response.status_code, status.HTTP_200_OK)
def _create_annotations_in_job(self, task, job_id, name_ann, key_get_values):
@ -274,7 +274,7 @@ class _DbTestBase(APITestCase):
"spec_id": spec_id,
"value": value,
})
response = self._put_api_v1_job_id_annotations(job_id, tmp_annotations)
response = self._put_api_v2_job_id_annotations(job_id, tmp_annotations)
self.assertEqual(response.status_code, status.HTTP_200_OK)
def _download_file(self, url, data, user, file_name):
@ -298,25 +298,25 @@ class _DbTestBase(APITestCase):
raise FileNotFoundError(f"File '{file_name}' was not downloaded")
def _generate_url_dump_tasks_annotations(self, task_id):
return f"/api/v1/tasks/{task_id}/annotations"
return f"/api/tasks/{task_id}/annotations"
def _generate_url_upload_tasks_annotations(self, task_id, upload_format_name):
return f"/api/v1/tasks/{task_id}/annotations?format={upload_format_name}"
return f"/api/tasks/{task_id}/annotations?format={upload_format_name}"
def _generate_url_dump_job_annotations(self, job_id):
return f"/api/v1/jobs/{job_id}/annotations"
return f"/api/jobs/{job_id}/annotations"
def _generate_url_upload_job_annotations(self, job_id, upload_format_name):
return f"/api/v1/jobs/{job_id}/annotations?format={upload_format_name}"
return f"/api/jobs/{job_id}/annotations?format={upload_format_name}"
def _generate_url_dump_task_dataset(self, task_id):
return f"/api/v1/tasks/{task_id}/dataset"
return f"/api/tasks/{task_id}/dataset"
def _generate_url_dump_project_annotations(self, project_id, format_name):
return f"/api/v1/projects/{project_id}/annotations?format={format_name}"
return f"/api/projects/{project_id}/annotations?format={format_name}"
def _generate_url_dump_project_dataset(self, project_id, format_name):
return f"/api/v1/projects/{project_id}/dataset?format={format_name}"
return f"/api/projects/{project_id}/dataset?format={format_name}"
def _remove_annotations(self, url, user):
response = self._delete_request(url, user)
@ -324,13 +324,13 @@ class _DbTestBase(APITestCase):
return response
def _delete_project(self, project_id, user):
response = self._delete_request(f'/api/v1/projects/{project_id}', user)
response = self._delete_request(f'/api/projects/{project_id}', user)
self.assertEqual(response.status_code, status.HTTP_204_NO_CONTENT)
return response
class TaskDumpUploadTest(_DbTestBase):
def test_api_v1_dump_and_upload_annotations_with_objects_type_is_shape(self):
def test_api_v2_dump_and_upload_annotations_with_objects_type_is_shape(self):
test_name = self._testMethodName
dump_formats = dm.views.get_export_formats()
upload_formats = dm.views.get_import_formats()
@ -435,7 +435,7 @@ class TaskDumpUploadTest(_DbTestBase):
response = self._put_request_with_data(url, {}, user)
self.assertEqual(response.status_code, edata['create code'])
def test_api_v1_dump_annotations_with_objects_type_is_track(self):
def test_api_v2_dump_annotations_with_objects_type_is_track(self):
test_name = self._testMethodName
dump_formats = dm.views.get_export_formats()
@ -539,7 +539,7 @@ class TaskDumpUploadTest(_DbTestBase):
response = self._put_request_with_data(url, {}, user)
self.assertEqual(response.status_code, edata['create code'])
def test_api_v1_dump_tag_annotations(self):
def test_api_v2_dump_tag_annotations(self):
dump_format_name = "CVAT for images 1.1"
data = {
"format": dump_format_name,
@ -592,7 +592,7 @@ class TaskDumpUploadTest(_DbTestBase):
f.write(content.getvalue())
self.assertEqual(osp.exists(file_zip_name), edata['file_exists'])
def test_api_v1_dump_and_upload_annotations_with_objects_are_different_images(self):
def test_api_v2_dump_and_upload_annotations_with_objects_are_different_images(self):
test_name = self._testMethodName
dump_format_name = "CVAT for images 1.1"
upload_types = ["task", "job"]
@ -628,11 +628,11 @@ class TaskDumpUploadTest(_DbTestBase):
with open(file_zip_name, 'rb') as binary_file:
self._upload_file(url_upload, binary_file, self.admin)
response = self._get_request(f"/api/v1/tasks/{task_id}/annotations", self.admin)
response = self._get_request(f"/api/tasks/{task_id}/annotations", self.admin)
self.assertEqual(len(response.data["shapes"]), 2)
self.assertEqual(len(response.data["tracks"]), 0)
def test_api_v1_dump_and_upload_annotations_with_objects_are_different_video(self):
def test_api_v2_dump_and_upload_annotations_with_objects_are_different_video(self):
test_name = self._testMethodName
dump_format_name = "CVAT for video 1.1"
upload_types = ["task", "job"]
@ -670,11 +670,11 @@ class TaskDumpUploadTest(_DbTestBase):
self._upload_file(url_upload, binary_file, self.admin)
self.assertEqual(osp.exists(file_zip_name), True)
response = self._get_request(f"/api/v1/tasks/{task_id}/annotations", self.admin)
response = self._get_request(f"/api/tasks/{task_id}/annotations", self.admin)
self.assertEqual(len(response.data["shapes"]), 0)
self.assertEqual(len(response.data["tracks"]), 2)
def test_api_v1_dump_and_upload_with_objects_type_is_track_and_outside_property(self):
def test_api_v2_dump_and_upload_with_objects_type_is_track_and_outside_property(self):
test_name = self._testMethodName
dump_format_name = "CVAT for video 1.1"
video = self._generate_task_videos(1)
@ -696,7 +696,7 @@ class TaskDumpUploadTest(_DbTestBase):
url = self._generate_url_upload_tasks_annotations(task_id, "CVAT 1.1")
self._upload_file(url, binary_file, self.admin)
def test_api_v1_dump_and_upload_with_objects_type_is_track_and_keyframe_property(self):
def test_api_v2_dump_and_upload_with_objects_type_is_track_and_keyframe_property(self):
test_name = self._testMethodName
dump_format_name = "CVAT for video 1.1"
@ -720,7 +720,7 @@ class TaskDumpUploadTest(_DbTestBase):
url = self._generate_url_upload_tasks_annotations(task_id, "CVAT 1.1")
self._upload_file(url, binary_file, self.admin)
def test_api_v1_dump_upload_annotations_from_several_jobs(self):
def test_api_v2_dump_upload_annotations_from_several_jobs(self):
test_name = self._testMethodName
dump_format_name = "CVAT for images 1.1"
@ -747,7 +747,7 @@ class TaskDumpUploadTest(_DbTestBase):
with open(file_zip_name, 'rb') as binary_file:
self._upload_file(url, binary_file, self.admin)
def test_api_v1_dump_annotations_with_objects_type_is_shape_from_several_jobs(self):
def test_api_v2_dump_annotations_with_objects_type_is_shape_from_several_jobs(self):
test_name = self._testMethodName
dump_format_name = "CVAT for images 1.1"
test_cases = ['all', 'first']
@ -781,7 +781,7 @@ class TaskDumpUploadTest(_DbTestBase):
with open(file_zip_name, 'rb') as binary_file:
self._upload_file(url, binary_file, self.admin)
def test_api_v1_export_dataset(self):
def test_api_v2_export_dataset(self):
test_name = self._testMethodName
dump_formats = dm.views.get_export_formats()
@ -837,7 +837,7 @@ class TaskDumpUploadTest(_DbTestBase):
self.assertEqual(response.status_code, edata['code'])
self.assertEqual(osp.exists(file_zip_name), edata['file_exists'])
def test_api_v1_dump_empty_frames(self):
def test_api_v2_dump_empty_frames(self):
dump_formats = dm.views.get_export_formats()
upload_formats = dm.views.get_import_formats()
@ -889,7 +889,7 @@ class TaskDumpUploadTest(_DbTestBase):
self.assertEqual(response.status_code, status.HTTP_201_CREATED)
self.assertIsNone(response.data)
def test_api_v1_rewriting_annotations(self):
def test_api_v2_rewriting_annotations(self):
test_name = self._testMethodName
dump_formats = dm.views.get_export_formats()
with TestDir() as test_dir:
@ -955,7 +955,7 @@ class TaskDumpUploadTest(_DbTestBase):
task_ann_data = task_ann.data
self.assertEqual(len(task_ann_data["shapes"]), len(task_ann_prev_data["shapes"]))
def test_api_v1_tasks_annotations_dump_and_upload_many_jobs_with_datumaro(self):
def test_api_v2_tasks_annotations_dump_and_upload_many_jobs_with_datumaro(self):
test_name = self._testMethodName
upload_format_name = "CVAT 1.1"
include_images_params = (False, True)
@ -995,7 +995,7 @@ class TaskDumpUploadTest(_DbTestBase):
data_from_task_after_upload = self._get_data_from_task(task_id, include_images)
compare_datasets(self, data_from_task_before_upload, data_from_task_after_upload)
def test_api_v1_tasks_annotations_dump_and_upload_with_datumaro(self):
def test_api_v2_tasks_annotations_dump_and_upload_with_datumaro(self):
test_name = self._testMethodName
# get formats
dump_formats = dm.views.get_export_formats()
@ -1072,7 +1072,7 @@ class TaskDumpUploadTest(_DbTestBase):
data_from_task_after_upload = self._get_data_from_task(task_id, include_images)
compare_datasets(self, data_from_task_before_upload, data_from_task_after_upload)
def test_api_v1_check_duplicated_polygon_points(self):
def test_api_v2_check_duplicated_polygon_points(self):
test_name = self._testMethodName
images = self._generate_task_images(10)
task = self._create_task(tasks["main"], images)
@ -1102,7 +1102,7 @@ class TaskDumpUploadTest(_DbTestBase):
polygon_points = [float(p) for p in polygon_points.split(";")]
self.assertEqual(polygon_points, annotation_points)
def test_api_v1_check_widerface_with_all_attributes(self):
def test_api_v2_check_widerface_with_all_attributes(self):
test_name = self._testMethodName
dump_format_name = "WiderFace 1.0"
upload_format_name = "WiderFace 1.0"
@ -1140,7 +1140,7 @@ class TaskDumpUploadTest(_DbTestBase):
data_from_task_after_upload = self._get_data_from_task(task_id, include_images)
compare_datasets(self, data_from_task_before_upload, data_from_task_after_upload)\
def test_api_v1_check_attribute_import_in_tracks(self):
def test_api_v2_check_attribute_import_in_tracks(self):
test_name = self._testMethodName
dump_format_name = "CVAT for video 1.1"
upload_format_name = "CVAT 1.1"
@ -1179,7 +1179,7 @@ class TaskDumpUploadTest(_DbTestBase):
compare_datasets(self, data_from_task_before_upload, data_from_task_after_upload)
class ProjectDump(_DbTestBase):
def test_api_v1_export_dataset(self):
def test_api_v2_export_dataset(self):
test_name = self._testMethodName
dump_formats = dm.views.get_export_formats()
@ -1231,7 +1231,7 @@ class ProjectDump(_DbTestBase):
self.assertEqual(response.status_code, edata['code'])
self.assertEqual(osp.exists(file_zip_name), edata['file_exists'])
def test_api_v1_export_annotatios(self):
def test_api_v2_export_annotatios(self):
test_name = self._testMethodName
dump_formats = dm.views.get_export_formats()

@ -1,4 +1,4 @@
# Copyright (C) 2018 Intel Corporation
# Copyright (C) 2018-2022 Intel Corporation
#
# SPDX-License-Identifier: MIT
@ -50,7 +50,6 @@ class ForceLogin:
if self.user:
self.client.logout()
class GitRemote:
def pull(self, refspec=None, progress=None, **kwargs):
_ = (refspec, progress, *kwargs)
@ -141,7 +140,6 @@ class TestGit(Git):
def reclone(self):
self._reclone()
class GitDatasetRepoTest(APITestCase):
class FakeGit:
def __init__(self, url):
@ -191,16 +189,16 @@ class GitDatasetRepoTest(APITestCase):
]
}
def _run_api_v1_job_id_annotation(self, jid, data, user):
def _run_api_v2_job_id_annotation(self, jid, data, user):
with ForceLogin(user, self.client):
response = self.client.patch('/api/v1/jobs/{}/annotations?action=create'.format(jid),
response = self.client.patch('/api/jobs/{}/annotations?action=create'.format(jid),
data=data, format="json")
return response
def _get_jobs(self, task_id):
with ForceLogin(self.admin, self.client):
response = self.client.get("/api/v1/tasks/{}/jobs".format(task_id))
response = self.client.get("/api/tasks/{}/jobs".format(task_id))
return response.data
def _create_task(self, init_repos=False):
@ -224,15 +222,15 @@ class GitDatasetRepoTest(APITestCase):
images["image_quality"] = 75
with ForceLogin(self.user, self.client):
response = self.client.post('/api/v1/tasks', data=data, format="json")
response = self.client.post('/api/tasks', data=data, format="json")
assert response.status_code == status.HTTP_201_CREATED, response.status_code
tid = response.data["id"]
response = self.client.post("/api/v1/tasks/%s/data" % tid,
response = self.client.post("/api/tasks/%s/data" % tid,
data=images)
assert response.status_code == status.HTTP_202_ACCEPTED, response.status_code
response = self.client.get("/api/v1/tasks/%s" % tid)
response = self.client.get("/api/tasks/%s" % tid)
task = response.data
db_task = Task.objects.get(pk=task["id"])
@ -509,6 +507,6 @@ class GitDatasetRepoTest(APITestCase):
],
"tracks": []
}
self._run_api_v1_job_id_annotation(jobs[0]["id"], ann, self.user)
self._run_api_v2_job_id_annotation(jobs[0]["id"], ann, self.user)
db_git = GitData()
self.assertEqual(db_git.status, GitStatusChoice.NON_SYNCED)

@ -708,7 +708,7 @@ def export(db_instance, request):
"Unexpected type of db_isntance: {}".format(type(db_instance)))
queue = django_rq.get_queue("default")
rq_id = "/api/v1/{}s/{}/backup".format(filename_prefix, db_instance.pk)
rq_id = "/api/{}s/{}/backup".format(filename_prefix, db_instance.pk)
rq_job = queue.fetch_job(rq_id)
if rq_job:
last_project_update_time = timezone.localtime(db_instance.updated_date)
@ -801,7 +801,7 @@ def import_project(request):
if 'rq_id' in request.data:
rq_id = request.data['rq_id']
else:
rq_id = "{}@/api/v1/projects/{}/import".format(request.user, uuid.uuid4())
rq_id = "{}@/api/projects/{}/import".format(request.user, uuid.uuid4())
Serializer = ProjectFileSerializer
file_field_name = 'project_file'
@ -817,7 +817,7 @@ def import_task(request):
if 'rq_id' in request.data:
rq_id = request.data['rq_id']
else:
rq_id = "{}@/api/v1/tasks/{}/import".format(request.user, uuid.uuid4())
rq_id = "{}@/api/tasks/{}/import".format(request.user, uuid.uuid4())
Serializer = TaskFileSerializer
file_field_name = 'task_file'

@ -0,0 +1,8 @@
# Copyright (C) 2022 Intel Corporation
#
# SPDX-License-Identifier: MIT
from rest_framework.renderers import JSONRenderer
class CVATAPIRenderer(JSONRenderer):
media_type = 'application/vnd.cvat+json'

@ -35,7 +35,7 @@ def create(tid, data):
"""Schedule the task"""
q = django_rq.get_queue('default')
q.enqueue_call(func=_create_thread, args=(tid, data),
job_id="/api/v1/tasks/{}".format(tid))
job_id="/api/tasks/{}".format(tid))
@transaction.atomic
def rq_handler(job, exc_type, exc_value, traceback):

File diff suppressed because it is too large Load Diff

@ -1,4 +1,4 @@
# Copyright (C) 2020 Intel Corporation
# Copyright (C) 2020-2022 Intel Corporation
#
# SPDX-License-Identifier: MIT
@ -67,47 +67,47 @@ class _DbTestBase(APITestCase):
cls.admin = user_admin
cls.user = user_dummy
def _put_api_v1_task_id_annotations(self, tid, data):
def _put_api_v2_task_id_annotations(self, tid, data):
with ForceLogin(self.admin, self.client):
response = self.client.put("/api/v1/tasks/%s/annotations" % tid,
response = self.client.put("/api/tasks/%s/annotations" % tid,
data=data, format="json")
return response
def _put_api_v1_job_id_annotations(self, jid, data):
def _put_api_v2_job_id_annotations(self, jid, data):
with ForceLogin(self.admin, self.client):
response = self.client.put("/api/v1/jobs/%s/annotations" % jid,
response = self.client.put("/api/jobs/%s/annotations" % jid,
data=data, format="json")
return response
def _patch_api_v1_task_id_annotations(self, tid, data, action, user):
def _patch_api_v2_task_id_annotations(self, tid, data, action, user):
with ForceLogin(user, self.client):
response = self.client.patch(
"/api/v1/tasks/{}/annotations?action={}".format(tid, action),
"/api/tasks/{}/annotations?action={}".format(tid, action),
data=data, format="json")
return response
def _patch_api_v1_job_id_annotations(self, jid, data, action, user):
def _patch_api_v2_job_id_annotations(self, jid, data, action, user):
with ForceLogin(user, self.client):
response = self.client.patch(
"/api/v1/jobs/{}/annotations?action={}".format(jid, action),
"/api/jobs/{}/annotations?action={}".format(jid, action),
data=data, format="json")
return response
def _create_task(self, data, image_data):
with ForceLogin(self.user, self.client):
response = self.client.post('/api/v1/tasks', data=data, format="json")
response = self.client.post('/api/tasks', data=data, format="json")
assert response.status_code == status.HTTP_201_CREATED, response.status_code
tid = response.data["id"]
response = self.client.post("/api/v1/tasks/%s/data" % tid,
response = self.client.post("/api/tasks/%s/data" % tid,
data=image_data)
assert response.status_code == status.HTTP_202_ACCEPTED, response.status_code
response = self.client.get("/api/v1/tasks/%s" % tid)
response = self.client.get("/api/tasks/%s" % tid)
task = response.data
return task
@ -140,7 +140,7 @@ class _DbTestBase(APITestCase):
def _get_jobs(self, task_id):
with ForceLogin(self.admin, self.client):
response = self.client.get("/api/v1/tasks/{}/jobs".format(task_id))
response = self.client.get("/api/tasks/{}/jobs".format(task_id))
return response.data
def _get_request(self, path, user):
@ -175,22 +175,22 @@ class _DbTestBase(APITestCase):
self.assertEqual(response.status_code, status.HTTP_201_CREATED)
def _generate_url_dump_tasks_annotations(self, task_id):
return f"/api/v1/tasks/{task_id}/annotations"
return f"/api/tasks/{task_id}/annotations"
def _generate_url_upload_tasks_annotations(self, task_id, upload_format_name):
return f"/api/v1/tasks/{task_id}/annotations?format={upload_format_name}"
return f"/api/tasks/{task_id}/annotations?format={upload_format_name}"
def _generate_url_dump_job_annotations(self, job_id):
return f"/api/v1/jobs/{job_id}/annotations"
return f"/api/jobs/{job_id}/annotations"
def _generate_url_upload_job_annotations(self, job_id, upload_format_name):
return f"/api/v1/jobs/{job_id}/annotations?format={upload_format_name}"
return f"/api/jobs/{job_id}/annotations?format={upload_format_name}"
def _generate_url_dump_dataset(self, task_id):
return f"/api/v1/tasks/{task_id}/dataset"
return f"/api/tasks/{task_id}/dataset"
def _remove_annotations(self, tid):
response = self._delete_request(f"/api/v1/tasks/{tid}/annotations", self.admin)
response = self._delete_request(f"/api/tasks/{tid}/annotations", self.admin)
self.assertEqual(response.status_code, status.HTTP_204_NO_CONTENT)
return response
@ -200,7 +200,7 @@ class _DbTestBase(APITestCase):
return response
def _delete_task(self, tid):
response = self._delete_request('/api/v1/tasks/{}'.format(tid), self.admin)
response = self._delete_request('/api/tasks/{}'.format(tid), self.admin)
self.assertEqual(response.status_code, status.HTTP_204_NO_CONTENT)
return response
@ -383,7 +383,7 @@ class Task3DTest(_DbTestBase):
}
return task_data
def test_api_v1_create_annotation_in_job(self):
def test_api_v2_create_annotation_in_job(self):
with TestDir() as test_dir:
task_data = self.copy_pcd_file_and_get_task_data(test_dir)
task = self._create_task(self.task, task_data)
@ -391,7 +391,7 @@ class Task3DTest(_DbTestBase):
annotation = self._get_tmp_annotation(task, self.cuboid_example)
for user, edata in list(self.expected_action.items()):
with self.subTest(format=edata["name"]):
response = self._patch_api_v1_task_id_annotations(task_id, annotation, CREATE_ACTION, user)
response = self._patch_api_v2_task_id_annotations(task_id, annotation, CREATE_ACTION, user)
self.assertEqual(response.status_code, edata["code"])
if edata["annotation_changed"]:
task_ann = TaskAnnotation(task_id)
@ -401,13 +401,13 @@ class Task3DTest(_DbTestBase):
self.assertEqual(task_shape, annotation["shapes"][0])
self._remove_annotations(task_id)
def test_api_v1_update_annotation_in_task(self):
def test_api_v2_update_annotation_in_task(self):
with TestDir() as test_dir:
task_data = self.copy_pcd_file_and_get_task_data(test_dir)
task = self._create_task(self.task, task_data)
task_id = task["id"]
annotation = self._get_tmp_annotation(task, self.cuboid_example)
response = self._put_api_v1_task_id_annotations(task_id, annotation)
response = self._put_api_v2_task_id_annotations(task_id, annotation)
self.assertEqual(response.status_code, status.HTTP_200_OK)
for user, edata in list(self.expected_action.items()):
@ -416,7 +416,7 @@ class Task3DTest(_DbTestBase):
task_ann_prev.init_from_db()
annotation["shapes"][0]["points"] = [x + 0.1 for x in annotation["shapes"][0]["points"]]
annotation["shapes"][0]["id"] = task_ann_prev.data["shapes"][0]["id"]
response = self._patch_api_v1_task_id_annotations(task_id, annotation, UPDATE_ACTION, user)
response = self._patch_api_v2_task_id_annotations(task_id, annotation, UPDATE_ACTION, user)
self.assertEqual(response.status_code, edata["code"], task_id)
if edata["annotation_changed"]:
@ -424,7 +424,7 @@ class Task3DTest(_DbTestBase):
task_ann.init_from_db()
self.assertEqual(task_ann.data["shapes"], annotation["shapes"])
def test_api_v1_remove_annotation_in_task(self):
def test_api_v2_remove_annotation_in_task(self):
with TestDir() as test_dir:
task_data = self.copy_pcd_file_and_get_task_data(test_dir)
task = self._create_task(self.task, task_data)
@ -433,13 +433,13 @@ class Task3DTest(_DbTestBase):
for user, edata in list(self.expected_action.items()):
with self.subTest(format=edata["name"]):
response = self._patch_api_v1_task_id_annotations(task_id, annotation, CREATE_ACTION, self.admin)
response = self._patch_api_v2_task_id_annotations(task_id, annotation, CREATE_ACTION, self.admin)
self.assertEqual(response.status_code, status.HTTP_200_OK)
task_ann_prev = TaskAnnotation(task_id)
task_ann_prev.init_from_db()
annotation["shapes"][0]["id"] = task_ann_prev.data["shapes"][0]["id"]
response = self._patch_api_v1_task_id_annotations(task_id, annotation, DELETE_ACTION, user)
response = self._patch_api_v2_task_id_annotations(task_id, annotation, DELETE_ACTION, user)
self.assertEqual(response.status_code, edata["code"])
if edata["annotation_changed"]:
@ -447,7 +447,7 @@ class Task3DTest(_DbTestBase):
task_ann.init_from_db()
self.assertTrue(len(task_ann.data["shapes"]) == 0)
def test_api_v1_create_annotation_in_jobs(self):
def test_api_v2_create_annotation_in_jobs(self):
with TestDir() as test_dir:
task_data = self.copy_pcd_file_and_get_task_data(test_dir)
task = self._create_task(self.task, task_data)
@ -456,7 +456,7 @@ class Task3DTest(_DbTestBase):
jobs = self._get_jobs(task_id)
for user, edata in list(self.expected_action.items()):
with self.subTest(format=edata["name"]):
response = self._patch_api_v1_job_id_annotations(jobs[0]["id"], annotation, CREATE_ACTION, user)
response = self._patch_api_v2_job_id_annotations(jobs[0]["id"], annotation, CREATE_ACTION, user)
self.assertEqual(response.status_code, edata["code"])
task_ann = TaskAnnotation(task_id)
@ -467,7 +467,7 @@ class Task3DTest(_DbTestBase):
self.assertEqual(task_shape, annotation["shapes"][0])
self._remove_annotations(task_id)
def test_api_v1_update_annotation_in_job(self):
def test_api_v2_update_annotation_in_job(self):
with TestDir() as test_dir:
task_data = self.copy_pcd_file_and_get_task_data(test_dir)
task = self._create_task(self.task, task_data)
@ -475,7 +475,7 @@ class Task3DTest(_DbTestBase):
jobs = self._get_jobs(task_id)
annotation = self._get_tmp_annotation(task, self.cuboid_example)
response = self._put_api_v1_task_id_annotations(task_id, annotation)
response = self._put_api_v2_task_id_annotations(task_id, annotation)
self.assertEqual(response.status_code, status.HTTP_200_OK)
for user, edata in list(self.expected_action.items()):
@ -486,7 +486,7 @@ class Task3DTest(_DbTestBase):
annotation["shapes"][0]["points"] = [x + 0.1 for x in annotation["shapes"][0]["points"]]
annotation["shapes"][0]["id"] = task_ann_prev.data["shapes"][0]["id"]
response = self._patch_api_v1_job_id_annotations(jobs[0]["id"], annotation, UPDATE_ACTION, user)
response = self._patch_api_v2_job_id_annotations(jobs[0]["id"], annotation, UPDATE_ACTION, user)
self.assertEqual(response.status_code, edata["code"])
if edata["annotation_changed"]:
@ -494,7 +494,7 @@ class Task3DTest(_DbTestBase):
task_ann.init_from_db()
self.assertEqual(task_ann.data["shapes"], annotation["shapes"])
def test_api_v1_remove_annotation_in_job(self):
def test_api_v2_remove_annotation_in_job(self):
with TestDir() as test_dir:
task_data = self.copy_pcd_file_and_get_task_data(test_dir)
task = self._create_task(self.task, task_data)
@ -504,13 +504,13 @@ class Task3DTest(_DbTestBase):
for user, edata in list(self.expected_action.items()):
with self.subTest(format=edata["name"]):
response = self._patch_api_v1_job_id_annotations(jobs[0]["id"], annotation, CREATE_ACTION, self.admin)
response = self._patch_api_v2_job_id_annotations(jobs[0]["id"], annotation, CREATE_ACTION, self.admin)
self.assertEqual(response.status_code, status.HTTP_200_OK)
task_ann_prev = TaskAnnotation(task_id)
task_ann_prev.init_from_db()
annotation["shapes"][0]["id"] = task_ann_prev.data["shapes"][0]["id"]
response = self._patch_api_v1_job_id_annotations(jobs[0]["id"], annotation, DELETE_ACTION, user)
response = self._patch_api_v2_job_id_annotations(jobs[0]["id"], annotation, DELETE_ACTION, user)
self.assertEqual(response.status_code, edata["code"])
if edata["annotation_changed"]:
@ -518,7 +518,7 @@ class Task3DTest(_DbTestBase):
task_ann.init_from_db()
self.assertTrue(len(task_ann.data["shapes"]) == 0)
def test_api_v1_dump_and_upload_annotation(self):
def test_api_v2_dump_and_upload_annotation(self):
with TestDir() as test_dir:
task_data = self.copy_pcd_file_and_get_task_data(test_dir)
task = self._create_task(self.task, task_data)
@ -526,7 +526,7 @@ class Task3DTest(_DbTestBase):
for format_name in self.format_names:
annotation = self._get_tmp_annotation(task, self.cuboid_example)
response = self._put_api_v1_task_id_annotations(task_id, annotation)
response = self._put_api_v2_task_id_annotations(task_id, annotation)
self.assertEqual(response.status_code, status.HTTP_200_OK)
task_ann_prev = TaskAnnotation(task_id)
task_ann_prev.init_from_db()
@ -571,7 +571,7 @@ class Task3DTest(_DbTestBase):
self.assertEqual(len(task_ann_prev.data["shapes"]), len(task_ann.data["shapes"]))
self.assertEqual(task_ann_prev.data["shapes"], task_ann.data["shapes"])
def test_api_v1_rewrite_annotation(self):
def test_api_v2_rewrite_annotation(self):
with TestDir() as test_dir:
task_data = self.copy_pcd_file_and_get_task_data(test_dir)
task = self._create_task(self.task, task_data)
@ -579,7 +579,7 @@ class Task3DTest(_DbTestBase):
for format_name in self.format_names:
with self.subTest(format=f"{format_name}"):
annotation = self._get_tmp_annotation(task, self.cuboid_example)
response = self._put_api_v1_task_id_annotations(task_id, annotation)
response = self._put_api_v2_task_id_annotations(task_id, annotation)
self.assertEqual(response.status_code, status.HTTP_200_OK)
task_ann_prev = TaskAnnotation(task_id)
task_ann_prev.init_from_db()
@ -596,7 +596,7 @@ class Task3DTest(_DbTestBase):
# rewrite annotation
annotation_copy = copy.deepcopy(annotation)
annotation_copy["shapes"][0]["points"] = [0] * 16
response = self._put_api_v1_task_id_annotations(task_id, annotation_copy)
response = self._put_api_v2_task_id_annotations(task_id, annotation_copy)
self.assertEqual(response.status_code, status.HTTP_200_OK)
file_name = osp.join(test_dir, f"{format_name}.zip")
@ -612,7 +612,7 @@ class Task3DTest(_DbTestBase):
self.assertEqual(len(task_ann_prev.data["shapes"]), len(task_ann.data["shapes"]))
self.assertEqual(task_ann_prev.data["shapes"], task_ann.data["shapes"])
def test_api_v1_dump_and_upload_empty_annotation(self):
def test_api_v2_dump_and_upload_empty_annotation(self):
with TestDir() as test_dir:
task_data = self.copy_pcd_file_and_get_task_data(test_dir)
task = self._create_task(self.task, task_data)
@ -643,7 +643,7 @@ class Task3DTest(_DbTestBase):
self.assertEqual(len(task_ann.data["shapes"]), 0)
self.assertEqual(task_ann_prev.data["shapes"], task_ann.data["shapes"])
def test_api_v1_dump_and_upload_several_jobs(self):
def test_api_v2_dump_and_upload_several_jobs(self):
job_test_cases = ["first", "all"]
with TestDir() as test_dir:
task_data = self.copy_pcd_file_and_get_task_data(test_dir)
@ -656,10 +656,10 @@ class Task3DTest(_DbTestBase):
jobs = self._get_jobs(task_id)
if job_test_case == "all":
for job in jobs:
response = self._put_api_v1_job_id_annotations(job["id"], annotation)
response = self._put_api_v2_job_id_annotations(job["id"], annotation)
self.assertEqual(response.status_code, status.HTTP_200_OK)
else:
response = self._put_api_v1_job_id_annotations(jobs[1]["id"], annotation)
response = self._put_api_v2_job_id_annotations(jobs[1]["id"], annotation)
self.assertEqual(response.status_code, status.HTTP_200_OK)
task_ann_prev = TaskAnnotation(task_id)
task_ann_prev.init_from_db()
@ -673,7 +673,7 @@ class Task3DTest(_DbTestBase):
self._remove_annotations(task_id)
def test_api_v1_upload_annotation_with_attributes(self):
def test_api_v2_upload_annotation_with_attributes(self):
with TestDir() as test_dir:
task_data = self.copy_pcd_file_and_get_task_data(test_dir)
task = self._create_task(self.task_with_attributes, task_data)
@ -681,7 +681,7 @@ class Task3DTest(_DbTestBase):
for format_name in self.format_names:
annotation = self._get_tmp_annotation(task, self.cuboid_example)
response = self._put_api_v1_task_id_annotations(task_id, annotation)
response = self._put_api_v2_task_id_annotations(task_id, annotation)
self.assertEqual(response.status_code, status.HTTP_200_OK)
task_ann_prev = TaskAnnotation(task_id)
task_ann_prev.init_from_db()
@ -711,7 +711,7 @@ class Task3DTest(_DbTestBase):
self.assertEqual(task_ann_prev.data["shapes"][0]["attributes"],
task_ann.data["shapes"][0]["attributes"])
def test_api_v1_export_dataset(self):
def test_api_v2_export_dataset(self):
with TestDir() as test_dir:
task_data = self.copy_pcd_file_and_get_task_data(test_dir)
task = self._create_task(self.task, task_data)
@ -719,7 +719,7 @@ class Task3DTest(_DbTestBase):
for format_name in self.format_names:
annotation = self._get_tmp_annotation(task, self.cuboid_example)
response = self._put_api_v1_task_id_annotations(task_id, annotation)
response = self._put_api_v2_task_id_annotations(task_id, annotation)
self.assertEqual(response.status_code, status.HTTP_200_OK)
task_ann_prev = TaskAnnotation(task_id)
task_ann_prev.init_from_db()

@ -70,7 +70,7 @@ urlpatterns = [
schema_view.with_ui('redoc', cache_timeout=0)), name='schema-redoc'),
# entry point for API
path('api/v1/', include('cvat.apps.iam.urls')),
path('api/v1/', include('cvat.apps.organizations.urls')),
path('api/v1/', include(router.urls)),
path('api/', include('cvat.apps.iam.urls')),
path('api/', include('cvat.apps.organizations.urls')),
path('api/', include(router.urls)),
]

@ -326,7 +326,7 @@ class ProjectViewSet(viewsets.ModelViewSet):
return _import_project_dataset(
request=request,
rq_id=f"/api/v1/project/{pk}/dataset_import",
rq_id=f"/api/project/{pk}/dataset_import",
rq_func=dm.project.import_dataset_as_project,
pk=pk,
format_name=format_name,
@ -335,7 +335,7 @@ class ProjectViewSet(viewsets.ModelViewSet):
action = request.query_params.get("action", "").lower()
if action in ("import_status",):
queue = django_rq.get_queue("default")
rq_job = queue.fetch_job(f"/api/v1/project/{pk}/dataset_import")
rq_job = queue.fetch_job(f"/api/project/{pk}/dataset_import")
if rq_job is None:
return Response(status=status.HTTP_404_NOT_FOUND)
elif rq_job.is_finished:
@ -353,14 +353,14 @@ class ProjectViewSet(viewsets.ModelViewSet):
)
else:
return Response(
data=self._get_rq_response('default', f'/api/v1/project/{pk}/dataset_import'),
data=self._get_rq_response('default', f'/api/project/{pk}/dataset_import'),
status=status.HTTP_202_ACCEPTED
)
else:
format_name = request.query_params.get("format", "")
return _export_annotations(
db_instance=db_project,
rq_id="/api/v1/project/{}/dataset/{}".format(pk, format_name),
rq_id="/api/project/{}/dataset/{}".format(pk, format_name),
request=request,
action=action,
callback=dm.views.export_project_as_dataset,
@ -395,7 +395,7 @@ class ProjectViewSet(viewsets.ModelViewSet):
format_name = request.query_params.get('format')
if format_name:
return _export_annotations(db_instance=db_project,
rq_id="/api/v1/projects/{}/annotations/{}".format(pk, format_name),
rq_id="/api/projects/{}/annotations/{}".format(pk, format_name),
request=request,
action=request.query_params.get("action", "").lower(),
callback=dm.views.export_project_annotations,
@ -744,7 +744,7 @@ class TaskViewSet(UploadMixin, viewsets.ModelViewSet):
format_name = request.query_params.get('format')
if format_name:
return _export_annotations(db_instance=db_task,
rq_id="/api/v1/tasks/{}/annotations/{}".format(pk, format_name),
rq_id="/api/tasks/{}/annotations/{}".format(pk, format_name),
request=request,
action=request.query_params.get("action", "").lower(),
callback=dm.views.export_task_annotations,
@ -761,7 +761,7 @@ class TaskViewSet(UploadMixin, viewsets.ModelViewSet):
if format_name:
return _import_annotations(
request=request,
rq_id="{}@/api/v1/tasks/{}/annotations/upload".format(request.user, pk),
rq_id="{}@/api/tasks/{}/annotations/upload".format(request.user, pk),
rq_func=dm.task.import_task_annotations,
pk=pk,
format_name=format_name,
@ -791,7 +791,7 @@ class TaskViewSet(UploadMixin, viewsets.ModelViewSet):
@action(detail=True, methods=['GET'], serializer_class=RqStatusSerializer)
def status(self, request, pk):
self.get_object() # force to call check_object_permissions
response = self._get_rq_response(queue="default", job_id=f"/api/v1/tasks/{pk}")
response = self._get_rq_response(queue="default", job_id=f"/api/tasks/{pk}")
serializer = RqStatusSerializer(data=response)
if serializer.is_valid(raise_exception=True):
@ -871,7 +871,7 @@ class TaskViewSet(UploadMixin, viewsets.ModelViewSet):
format_name = request.query_params.get("format", "")
return _export_annotations(db_instance=db_task,
rq_id="/api/v1/tasks/{}/dataset/{}".format(pk, format_name),
rq_id="/api/tasks/{}/dataset/{}".format(pk, format_name),
request=request,
action=request.query_params.get("action", "").lower(),
callback=dm.views.export_task_as_dataset,
@ -934,7 +934,7 @@ class JobViewSet(viewsets.GenericViewSet, mixins.ListModelMixin,
if format_name:
return _import_annotations(
request=request,
rq_id="{}@/api/v1/jobs/{}/annotations/upload".format(request.user, pk),
rq_id="{}@/api/jobs/{}/annotations/upload".format(request.user, pk),
rq_func=dm.task.import_job_annotations,
pk=pk,
format_name=format_name

@ -1,10 +1,10 @@
# Copyright (C) 2021 Intel Corporation
# Copyright (C) 2021-2022 Intel Corporation
#
# SPDX-License-Identifier: MIT
from django.urls import reverse
from rest_framework import status
from rest_framework.test import APITestCase
from rest_framework.test import APITestCase, APIClient
from rest_framework.authtoken.models import Token
from django.test import override_settings
from cvat.apps.iam.urls import urlpatterns as iam_url_patterns
@ -19,14 +19,16 @@ urlpatterns = iam_url_patterns + [
name='account_email_verification_sent'),
]
class UserRegisterAPITestCase(APITestCase):
user_data = {'first_name': 'test_first', 'last_name': 'test_last', 'username': 'test_username',
'email': 'test_email@test.com', 'password1': '$Test357Test%', 'password2': '$Test357Test%',
'confirmations': []}
def _run_api_v1_user_register(self, data):
def setUp(self):
self.client = APIClient()
def _run_api_v2_user_register(self, data):
url = reverse('rest_register')
response = self.client.post(url, data, format='json')
return response
@ -36,11 +38,11 @@ class UserRegisterAPITestCase(APITestCase):
self.assertEqual(response.data, data)
@override_settings(ACCOUNT_EMAIL_VERIFICATION='none')
def test_api_v1_user_register_with_email_verification_none(self):
def test_api_v2_user_register_with_email_verification_none(self):
"""
Ensure we can register a user and get auth token key when email verification is none
"""
response = self._run_api_v1_user_register(self.user_data)
response = self._run_api_v2_user_register(self.user_data)
user_token = Token.objects.get(user__username=response.data['username'])
self._check_response(response, {'first_name': 'test_first', 'last_name': 'test_last',
'username': 'test_username', 'email': 'test_email@test.com',
@ -50,11 +52,11 @@ class UserRegisterAPITestCase(APITestCase):
# the tests and pass it using ROOT_URLCONF in the override settings decorator
@override_settings(ACCOUNT_EMAIL_VERIFICATION='optional', ROOT_URLCONF=__name__)
def test_api_v1_user_register_with_email_verification_optional(self):
def test_api_v2_user_register_with_email_verification_optional(self):
"""
Ensure we can register a user and get auth token key when email verification is optional
"""
response = self._run_api_v1_user_register(self.user_data)
response = self._run_api_v2_user_register(self.user_data)
user_token = Token.objects.get(user__username=response.data['username'])
self._check_response(response, {'first_name': 'test_first', 'last_name': 'test_last',
'username': 'test_username', 'email': 'test_email@test.com',
@ -66,7 +68,7 @@ class UserRegisterAPITestCase(APITestCase):
"""
Ensure we can register a user and it does not return auth token key when email verification is mandatory
"""
response = self._run_api_v1_user_register(self.user_data)
response = self._run_api_v2_user_register(self.user_data)
self._check_response(response, {'first_name': 'test_first', 'last_name': 'test_last',
'username': 'test_username', 'email': 'test_email@test.com',
'email_verification_required': True, 'key': None})

@ -1,5 +1,5 @@
# Copyright (C) 2021 Intel Corporation
# Copyright (C) 2021-2022 Intel Corporation
#
# SPDX-License-Identifier: MIT
@ -16,7 +16,7 @@ from PIL import Image
from rest_framework import status
from rest_framework.test import APIClient, APITestCase
LAMBDA_ROOT_PATH = '/api/v1/lambda'
LAMBDA_ROOT_PATH = '/api/lambda'
LAMBDA_FUNCTIONS_PATH = f'{LAMBDA_ROOT_PATH}/functions'
LAMBDA_REQUESTS_PATH = f'{LAMBDA_ROOT_PATH}/requests'
@ -71,7 +71,6 @@ class ForceLogin:
if self.user:
self.client.logout()
class LambdaTestCase(APITestCase):
def setUp(self):
self.client = APIClient()
@ -154,15 +153,15 @@ class LambdaTestCase(APITestCase):
def _create_task(self, data, image_data):
with ForceLogin(self.admin, self.client):
response = self.client.post('/api/v1/tasks', data=data, format="json")
response = self.client.post('/api/tasks', data=data, format="json")
assert response.status_code == status.HTTP_201_CREATED, response.status_code
tid = response.data["id"]
response = self.client.post("/api/v1/tasks/%s/data" % tid,
response = self.client.post("/api/tasks/%s/data" % tid,
data=image_data)
assert response.status_code == status.HTTP_202_ACCEPTED, response.status_code
response = self.client.get("/api/v1/tasks/%s" % tid)
response = self.client.get("/api/tasks/%s" % tid)
task = response.data
return task
@ -214,7 +213,7 @@ class LambdaTestCase(APITestCase):
self.assertIn(key, data)
def test_api_v1_lambda_functions_list(self):
def test_api_v2_lambda_functions_list(self):
response = self._get_request(LAMBDA_FUNCTIONS_PATH, self.admin)
self.assertEqual(response.status_code, status.HTTP_200_OK)
for data in response.data:
@ -230,7 +229,7 @@ class LambdaTestCase(APITestCase):
@mock.patch('cvat.apps.lambda_manager.views.LambdaGateway._http', return_value = {})
def test_api_v1_lambda_functions_list_empty(self, mock_http):
def test_api_v2_lambda_functions_list_empty(self, mock_http):
response = self._get_request(LAMBDA_FUNCTIONS_PATH, self.admin)
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertEqual(len(response.data), 0)
@ -244,12 +243,12 @@ class LambdaTestCase(APITestCase):
@mock.patch('cvat.apps.lambda_manager.views.LambdaGateway._http', return_value = functions["negative"])
def test_api_v1_lambda_functions_list_wrong(self, mock_http):
def test_api_v2_lambda_functions_list_wrong(self, mock_http):
response = self._get_request(LAMBDA_FUNCTIONS_PATH, self.admin)
self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND)
def test_api_v1_lambda_functions_read(self):
def test_api_v2_lambda_functions_read(self):
ids_functions = [id_function_detector, id_function_interactor,\
id_function_tracker, id_function_reid_response_data, \
id_function_non_type, id_function_wrong_type, id_function_unknown_type]
@ -269,7 +268,7 @@ class LambdaTestCase(APITestCase):
self.assertEqual(response.status_code, status.HTTP_401_UNAUTHORIZED)
def test_api_v1_lambda_functions_read_wrong_id(self):
def test_api_v2_lambda_functions_read_wrong_id(self):
id_wrong_function = "test-functions-wrong-id"
response = self._get_request(f'{LAMBDA_FUNCTIONS_PATH}/{id_wrong_function}', self.admin)
self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND)
@ -282,13 +281,13 @@ class LambdaTestCase(APITestCase):
@mock.patch('cvat.apps.lambda_manager.views.LambdaGateway._http', return_value = functions["negative"][id_function_non_unique_labels])
def test_api_v1_lambda_functions_read_non_unique_labels(self, mock_http):
def test_api_v2_lambda_functions_read_non_unique_labels(self, mock_http):
response = self._get_request(f'{LAMBDA_FUNCTIONS_PATH}/{id_function_non_unique_labels}', self.admin)
self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND)
@skip("Fail: add mock")
def test_api_v1_lambda_requests_list(self):
def test_api_v2_lambda_requests_list(self):
response = self._get_request(LAMBDA_REQUESTS_PATH, self.admin)
self.assertEqual(response.status_code, status.HTTP_200_OK)
for key in expected_keys_in_response_requests:
@ -303,7 +302,7 @@ class LambdaTestCase(APITestCase):
self.assertEqual(response.status_code, status.HTTP_401_UNAUTHORIZED)
def test_api_v1_lambda_requests_list_empty(self):
def test_api_v2_lambda_requests_list_empty(self):
response = self._get_request(LAMBDA_REQUESTS_PATH, self.admin)
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertEqual(len(response.data), 0)
@ -316,7 +315,7 @@ class LambdaTestCase(APITestCase):
self.assertEqual(response.status_code, status.HTTP_401_UNAUTHORIZED)
def test_api_v1_lambda_requests_read(self):
def test_api_v2_lambda_requests_read(self):
# create request
data_main_task = {
"function": id_function_detector,
@ -346,7 +345,7 @@ class LambdaTestCase(APITestCase):
self.assertEqual(response.status_code, status.HTTP_401_UNAUTHORIZED)
def test_api_v1_lambda_requests_read_wrong_id(self):
def test_api_v2_lambda_requests_read_wrong_id(self):
id_request = "cf343b95-afeb-475e-ab53-8d7e64991d30-wrong-id"
response = self._get_request(f'{LAMBDA_REQUESTS_PATH}/{id_request}', self.admin)
@ -359,7 +358,7 @@ class LambdaTestCase(APITestCase):
self.assertEqual(response.status_code, status.HTTP_401_UNAUTHORIZED)
def test_api_v1_lambda_requests_delete_finished_request(self):
def test_api_v2_lambda_requests_delete_finished_request(self):
data = {
"function": id_function_detector,
"task": self.main_task["id"],
@ -388,11 +387,11 @@ class LambdaTestCase(APITestCase):
@skip("Fail: add mock")
def test_api_v1_lambda_requests_delete_not_finished_request(self):
def test_api_v2_lambda_requests_delete_not_finished_request(self):
pass
def test_api_v1_lambda_requests_create(self):
def test_api_v2_lambda_requests_create(self):
ids_functions = [id_function_detector, id_function_interactor, id_function_tracker, \
id_function_reid_response_data, id_function_detector, id_function_reid_response_no_data, \
id_function_non_type, id_function_wrong_type, id_function_unknown_type]
@ -437,7 +436,7 @@ class LambdaTestCase(APITestCase):
@mock.patch('cvat.apps.lambda_manager.views.LambdaGateway._http', return_value = functions["negative"]["test-model-has-non-unique-labels"])
def test_api_v1_lambda_requests_create_non_unique_labels(self, mock_http):
def test_api_v2_lambda_requests_create_non_unique_labels(self, mock_http):
data = {
"function": id_function_non_unique_labels,
"task": self.main_task["id"],
@ -451,13 +450,13 @@ class LambdaTestCase(APITestCase):
self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND)
def test_api_v1_lambda_requests_create_empty_data(self):
def test_api_v2_lambda_requests_create_empty_data(self):
data = {}
response = self._post_request(LAMBDA_REQUESTS_PATH, self.admin, data)
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
def test_api_v1_lambda_requests_create_without_function(self):
def test_api_v2_lambda_requests_create_without_function(self):
data = {
"task": self.main_task["id"],
"cleanup": True,
@ -469,7 +468,7 @@ class LambdaTestCase(APITestCase):
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
def test_api_v1_lambda_requests_create_wrong_id_function(self):
def test_api_v2_lambda_requests_create_wrong_id_function(self):
data = {
"function": "test-requests-wrong-id",
"task": self.main_task["id"],
@ -483,7 +482,7 @@ class LambdaTestCase(APITestCase):
@skip("Fail: add mock")
def test_api_v1_lambda_requests_create_two_requests(self):
def test_api_v2_lambda_requests_create_two_requests(self):
data = {
"function": id_function_detector,
"task": self.main_task["id"],
@ -497,7 +496,7 @@ class LambdaTestCase(APITestCase):
self.assertEqual(response.status_code, status.HTTP_409_CONFLICT)
def test_api_v1_lambda_requests_create_empty_mapping(self):
def test_api_v2_lambda_requests_create_empty_mapping(self):
data = {
"function": id_function_detector,
"task": self.main_task["id"],
@ -510,7 +509,7 @@ class LambdaTestCase(APITestCase):
self.assertIn(key, response.data)
def test_api_v1_lambda_requests_create_without_cleanup(self):
def test_api_v2_lambda_requests_create_without_cleanup(self):
data = {
"function": id_function_detector,
"task": self.main_task["id"],
@ -524,7 +523,7 @@ class LambdaTestCase(APITestCase):
self.assertIn(key, response.data)
def test_api_v1_lambda_requests_create_without_mapping(self):
def test_api_v2_lambda_requests_create_without_mapping(self):
data = {
"function": id_function_detector,
"task": self.main_task["id"],
@ -536,7 +535,7 @@ class LambdaTestCase(APITestCase):
self.assertIn(key, response.data)
def test_api_v1_lambda_requests_create_without_task(self):
def test_api_v2_lambda_requests_create_without_task(self):
data = {
"function": id_function_detector,
"cleanup": True,
@ -548,7 +547,7 @@ class LambdaTestCase(APITestCase):
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
def test_api_v1_lambda_requests_create_wrong_id_task(self):
def test_api_v2_lambda_requests_create_wrong_id_task(self):
data = {
"function": id_function_detector,
"task": 12345,
@ -561,7 +560,7 @@ class LambdaTestCase(APITestCase):
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
def test_api_v1_lambda_requests_create_is_not_ready(self):
def test_api_v2_lambda_requests_create_is_not_ready(self):
ids_functions = [id_function_state_building, id_function_state_error]
for id_func in ids_functions:
@ -578,7 +577,7 @@ class LambdaTestCase(APITestCase):
self.assertEqual(response.status_code, status.HTTP_500_INTERNAL_SERVER_ERROR)
def test_api_v1_lambda_functions_create_detector(self):
def test_api_v2_lambda_functions_create_detector(self):
data_main_task = {
"task": self.main_task["id"],
"frame": 0,
@ -606,8 +605,8 @@ class LambdaTestCase(APITestCase):
response = self._post_request(f"{LAMBDA_FUNCTIONS_PATH}/{id_function_detector}", None, data_main_task)
self.assertEqual(response.status_code, status.HTTP_401_UNAUTHORIZED)
@skip("Fail: expected result != actual result") # TODO move test to test_api_v1_lambda_functions_create
def test_api_v1_lambda_functions_create_user_assigned_to_no_user(self):
@skip("Fail: expected result != actual result") # TODO move test to test_api_v2_lambda_functions_create
def test_api_v2_lambda_functions_create_user_assigned_to_no_user(self):
data = {
"task": self.main_task["id"],
"frame": 0,
@ -620,7 +619,7 @@ class LambdaTestCase(APITestCase):
self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN)
def test_api_v1_lambda_functions_create_interactor(self):
def test_api_v2_lambda_functions_create_interactor(self):
data_main_task = {
"task": self.main_task["id"],
"frame": 0,
@ -665,7 +664,7 @@ class LambdaTestCase(APITestCase):
self.assertEqual(response.status_code, status.HTTP_401_UNAUTHORIZED)
def test_api_v1_lambda_functions_create_tracker(self):
def test_api_v2_lambda_functions_create_tracker(self):
data_main_task = {
"task": self.main_task["id"],
"frame": 0,
@ -697,7 +696,7 @@ class LambdaTestCase(APITestCase):
self.assertEqual(response.status_code, status.HTTP_401_UNAUTHORIZED)
def test_api_v1_lambda_functions_create_reid(self):
def test_api_v2_lambda_functions_create_reid(self):
data_main_task = {
"task": self.main_task["id"],
"frame0": 0,
@ -748,7 +747,7 @@ class LambdaTestCase(APITestCase):
self.assertEqual(response.status_code, status.HTTP_401_UNAUTHORIZED)
def test_api_v1_lambda_functions_create_non_type(self):
def test_api_v2_lambda_functions_create_non_type(self):
data = {
"task": self.main_task["id"],
"frame": 0,
@ -762,7 +761,7 @@ class LambdaTestCase(APITestCase):
self.assertEqual(response.status_code, status.HTTP_500_INTERNAL_SERVER_ERROR)
def test_api_v1_lambda_functions_create_wrong_type(self):
def test_api_v2_lambda_functions_create_wrong_type(self):
data = {
"task": self.main_task["id"],
"frame": 0,
@ -776,7 +775,7 @@ class LambdaTestCase(APITestCase):
self.assertEqual(response.status_code, status.HTTP_500_INTERNAL_SERVER_ERROR)
def test_api_v1_lambda_functions_create_unknown_type(self):
def test_api_v2_lambda_functions_create_unknown_type(self):
data = {
"task": self.main_task["id"],
"frame": 0,
@ -791,7 +790,7 @@ class LambdaTestCase(APITestCase):
@mock.patch('cvat.apps.lambda_manager.views.LambdaGateway._http', return_value = functions["negative"]["test-model-has-non-unique-labels"])
def test_api_v1_lambda_functions_create_non_unique_labels(self, mock_http):
def test_api_v2_lambda_functions_create_non_unique_labels(self, mock_http):
data = {
"task": self.main_task["id"],
"frame": 0,
@ -805,7 +804,7 @@ class LambdaTestCase(APITestCase):
self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND)
def test_api_v1_lambda_functions_create_quality(self):
def test_api_v2_lambda_functions_create_quality(self):
qualities = [None, "original", "compressed"]
for quality in qualities:
@ -836,13 +835,13 @@ class LambdaTestCase(APITestCase):
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
def test_api_v1_lambda_functions_create_empty_data(self):
def test_api_v2_lambda_functions_create_empty_data(self):
data = {}
response = self._post_request(f"{LAMBDA_FUNCTIONS_PATH}/{id_function_detector}", self.admin, data)
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
def test_api_v1_lambda_functions_create_detector_empty_mapping(self):
def test_api_v2_lambda_functions_create_detector_empty_mapping(self):
data = {
"task": self.main_task["id"],
"frame": 0,
@ -853,7 +852,7 @@ class LambdaTestCase(APITestCase):
self.assertEqual(response.status_code, status.HTTP_200_OK)
def test_api_v1_lambda_functions_create_detector_without_cleanup(self):
def test_api_v2_lambda_functions_create_detector_without_cleanup(self):
data = {
"task": self.main_task["id"],
"frame": 0,
@ -865,7 +864,7 @@ class LambdaTestCase(APITestCase):
self.assertEqual(response.status_code, status.HTTP_200_OK)
def test_api_v1_lambda_functions_create_detector_without_mapping(self):
def test_api_v2_lambda_functions_create_detector_without_mapping(self):
data = {
"task": self.main_task["id"],
"frame": 0,
@ -875,7 +874,7 @@ class LambdaTestCase(APITestCase):
self.assertEqual(response.status_code, status.HTTP_200_OK)
def test_api_v1_lambda_functions_create_detector_without_task(self):
def test_api_v2_lambda_functions_create_detector_without_task(self):
data = {
"frame": 0,
"cleanup": True,
@ -887,7 +886,7 @@ class LambdaTestCase(APITestCase):
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
def test_api_v1_lambda_functions_create_detector_without_id_frame(self):
def test_api_v2_lambda_functions_create_detector_without_id_frame(self):
data = {
"task": self.main_task["id"],
"cleanup": True,
@ -899,7 +898,7 @@ class LambdaTestCase(APITestCase):
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
def test_api_v1_lambda_functions_create_wrong_id_function(self):
def test_api_v2_lambda_functions_create_wrong_id_function(self):
data = {
"task": self.main_task["id"],
"frame": 0,
@ -912,7 +911,7 @@ class LambdaTestCase(APITestCase):
self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND)
def test_api_v1_lambda_functions_create_wrong_id_task(self):
def test_api_v2_lambda_functions_create_wrong_id_task(self):
data = {
"task": 12345,
"frame": 0,
@ -926,7 +925,7 @@ class LambdaTestCase(APITestCase):
@skip("Fail: expected result != actual result, issue #2770")
def test_api_v1_lambda_functions_create_detector_wrong_id_frame(self):
def test_api_v2_lambda_functions_create_detector_wrong_id_frame(self):
data = {
"task": self.main_task["id"],
"frame": 12345,
@ -940,7 +939,7 @@ class LambdaTestCase(APITestCase):
@skip("Fail: add mock and expected result != actual result")
def test_api_v1_lambda_functions_create_two_functions(self):
def test_api_v2_lambda_functions_create_two_functions(self):
data = {
"task": self.main_task["id"],
"frame": 0,
@ -954,7 +953,7 @@ class LambdaTestCase(APITestCase):
self.assertEqual(response.status_code, status.HTTP_409_CONFLICT)
def test_api_v1_lambda_functions_create_function_is_not_ready(self):
def test_api_v2_lambda_functions_create_function_is_not_ready(self):
data = {
"task": self.main_task["id"],
"frame": 0,

@ -16,14 +16,14 @@ router.routes[2].mapping.update({'post': 'call'})
router.register('functions', views.FunctionViewSet, basename='function')
router.register('requests', views.RequestViewSet, basename='request')
# GET /api/v1/lambda/functions - get list of functions
# GET /api/v1/lambda/functions/<int:fid> - get information about the function
# POST /api/v1/lambda/requests - call a function
# GET /api/lambda/functions - get list of functions
# GET /api/lambda/functions/<int:fid> - get information about the function
# POST /api/lambda/requests - call a function
# { "function": "<id>", "mode": "online|offline", "job": "<jid>", "frame": "<n>",
# "points": [...], }
# GET /api/v1/lambda/requests - get list of requests
# GET /api/v1/lambda/requests/<int:rid> - get status of the request
# DEL /api/v1/lambda/requests/<int:rid> - cancel a request (don't delete)
# GET /api/lambda/requests - get list of requests
# GET /api/lambda/requests/<int:rid> - get status of the request
# DEL /api/lambda/requests/<int:rid> - cancel a request (don't delete)
urlpatterns = [
path('api/v1/lambda/', include((router.urls, 'cvat'), namespace='v1'))
path('api/lambda/', include((router.urls, 'cvat'), namespace='v1'))
]

@ -1,4 +1,4 @@
# Copyright (C) 2020 Intel Corporation
# Copyright (C) 2020-2022 Intel Corporation
#
# SPDX-License-Identifier: MIT
@ -7,7 +7,6 @@ from rest_framework.test import APITestCase, APIClient
from rest_framework import status
from django.conf import settings
class UserAgreementsTest(APITestCase):
def setUp(self):
self.client = APIClient()
@ -37,7 +36,7 @@ class UserAgreementsTest(APITestCase):
settings.RESTRICTIONS['user_agreements'] = self.user_agreements
def _get_user_agreements(self):
response = self.client.get('/api/v1/restrictions/user-agreements')
response = self.client.get('/api/restrictions/user-agreements')
assert response.status_code == status.HTTP_200_OK
for agreements in response.data:
assert 'name' in agreements, agreements['name']
@ -46,7 +45,7 @@ class UserAgreementsTest(APITestCase):
return response.data
def _register_user(self, data):
response = self.client.post('/api/v1/auth/register', data=data, format="json")
response = self.client.post('/api/auth/register', data=data, format="json")
return response

@ -139,6 +139,10 @@ REST_FRAMEWORK = {
'rest_framework.parsers.MultiPartParser',
'cvat.apps.engine.parsers.TusUploadParser',
],
'DEFAULT_RENDERER_CLASSES': [
'cvat.apps.engine.renderers.CVATAPIRenderer',
'rest_framework.renderers.BrowsableAPIRenderer',
],
'DEFAULT_PERMISSION_CLASSES': [
'rest_framework.permissions.IsAuthenticated',
'cvat.apps.iam.permissions.IsMemberInOrganization',
@ -151,12 +155,10 @@ REST_FRAMEWORK = {
'rest_framework.authentication.BasicAuthentication'
],
'DEFAULT_VERSIONING_CLASS':
# Don't try to use URLPathVersioning. It will give you /api/{version}
# in path and '/api/docs' will not collapse similar items (flat list
# of all possible methods isn't readable).
'rest_framework.versioning.NamespaceVersioning',
# Need to add 'api-docs' here as a workaround for include_docs_urls.
'ALLOWED_VERSIONS': ('v1', 'api-docs'),
'rest_framework.versioning.AcceptHeaderVersioning',
'ALLOWED_VERSIONS': ('1.0', '2.0'),
'DEFAULT_VERSION': '2.0',
'VERSION_PARAM': 'version',
'DEFAULT_PAGINATION_CLASS':
'cvat.apps.engine.pagination.CustomPagination',
'PAGE_SIZE': 10,

@ -44,4 +44,4 @@ if apps.is_installed('silk'):
urlpatterns.append(path('profiler/', include('silk.urls')))
if apps.is_installed('cvat.apps.training'):
urlpatterns.append(path('api/v1/predict/', include('cvat.apps.training.urls')))
urlpatterns.append(path('api/predict/', include('cvat.apps.training.urls')))

@ -89,7 +89,7 @@ section = ["HTML", "print"]
[params]
intel_terms_of_use = "https://www.intel.com/content/www/us/en/legal/terms-of-use.html"
intel_privacy_notice = "https://www.intel.com/content/www/us/en/privacy/intel-privacy-notice.html"
cvat_terms_of_use = "https://cvat.org/api/v1/restrictions/terms-of-use"
cvat_terms_of_use = "https://cvat.org/api/restrictions/terms-of-use"
# First one is picked as the Twitter card image if not set on page.
# images = ["images/project-illustration.png"]

@ -21,7 +21,7 @@ Common scheme for our REST API is `<VERB> [namespace] <objects> <id> <action>`.
## Design principles
- Use nouns instead of verbs in endpoint paths. For example,
`POST /api/v1/tasks` instead of `POST /api/v1/tasks/create`.
`POST /api/tasks` instead of `POST /api/tasks/create`.
- Accept and respond with JSON whenever it is possible
- Name collections with plural nouns (e.g. `/tasks`, `/projects`)
- Try to keep the API structure flat. Prefer two separate endpoints
@ -36,8 +36,8 @@ Common scheme for our REST API is `<VERB> [namespace] <objects> <id> <action>`.
- Allow filtering, sorting, and pagination
- Maintain good security practices
- Cache data to improve performance
- Versioning our APIs (e.g. `/api/v1`, `/api/v2`). It should be done when you
delete an endpoint or modify its behaviors.
- Versioning our APIs. It should be done when you delete an endpoint or modify
its behaviors. Versioning uses a schema with `Accept` header with vendor media type.
## Links

@ -54,7 +54,7 @@ As a result, you'll get a task containing data, parameters, and annotations of t
### Create from backup API
- endpoint: `/api/v1/tasks/backup` or `/api/v1/projects/backup`
- endpoint: `/api/tasks/backup` or `/api/projects/backup`
- method: `POST`
- Content-Type: `multipart/form-data`
- responses: 202, 201 with json payload

@ -914,10 +914,10 @@ docker logs cvat
```
```
2021-07-06 13:44:54,699 DEBG 'runserver' stderr output:
[Tue Jul 06 13:44:54.699431 2021] [wsgi:error] [pid 625:tid 140010969868032] [remote 172.28.0.3:40972] [2021-07-06 13:44:54,699] ERROR django.request: Internal Server Error: /api/v1/lambda/functions/pth.shiyinzhang.iog
[Tue Jul 06 13:44:54.699431 2021] [wsgi:error] [pid 625:tid 140010969868032] [remote 172.28.0.3:40972] [2021-07-06 13:44:54,699] ERROR django.request: Internal Server Error: /api/lambda/functions/pth.shiyinzhang.iog
2021-07-06 13:44:54,700 DEBG 'runserver' stderr output:
[Tue Jul 06 13:44:54.699712 2021] [wsgi:error] [pid 625:tid 140010969868032] [remote 172.28.0.3:40972] ERROR - 2021-07-06 13:44:54,699 - log - Internal Server Error: /api/v1/lambda/functions/pth.shiyinzhang.iog
[Tue Jul 06 13:44:54.699712 2021] [wsgi:error] [pid 625:tid 140010969868032] [remote 172.28.0.3:40972] ERROR - 2021-07-06 13:44:54,699 - log - Internal Server Error: /api/lambda/functions/pth.shiyinzhang.iog
```
```bash

@ -1,4 +1,4 @@
// Copyright (C) 2021 Intel Corporation
// Copyright (C) 2021-2022 Intel Corporation
//
// SPDX-License-Identifier: MIT
@ -117,7 +117,7 @@ context('Dump/Upload annotation.', { browser: '!firefox' }, () => {
cy.get('input[type=file]').attachFile(annotationArchiveName);
});
confirmUpdate('.cvat-modal-content-load-job-annotation');
cy.intercept('GET', '/api/v1/jobs/**/annotations**').as('uploadAnnotationsGet');
cy.intercept('GET', '/api/jobs/**/annotations**').as('uploadAnnotationsGet');
cy.wait('@uploadAnnotationsGet').its('response.statusCode').should('equal', 200);
cy.get('#cvat_canvas_shape_1').should('exist');
cy.get('#cvat-objects-sidebar-state-item-1').should('exist');

@ -1,4 +1,4 @@
// Copyright (C) 2021 Intel Corporation
// Copyright (C) 2021-2022 Intel Corporation
//
// SPDX-License-Identifier: MIT
@ -65,7 +65,7 @@ context('Import annotations for frames with dots in name.', { browser: '!firefox
describe(`Testing case "${issueId}"`, () => {
it('Save job. Dump annotation to YOLO format. Remove annotation. Save job.', () => {
cy.saveJob('PATCH', 200, 'saveJobDump');
cy.intercept('GET', '/api/v1/tasks/**/annotations**').as('dumpAnnotations');
cy.intercept('GET', '/api/tasks/**/annotations**').as('dumpAnnotations');
cy.interactMenu('Export task dataset');
cy.get('.cvat-modal-export-task').find('.cvat-modal-export-select').click();
cy.get('.ant-select-dropdown')
@ -105,7 +105,7 @@ context('Import annotations for frames with dots in name.', { browser: '!firefox
.get('input[type=file]')
.attachFile(annotationArchiveName);
});
cy.intercept('GET', '/api/v1/jobs/**/annotations?**').as('uploadAnnotationsGet');
cy.intercept('GET', '/api/jobs/**/annotations?**').as('uploadAnnotationsGet');
confirmUpdate('.cvat-modal-content-load-job-annotation');
cy.wait('@uploadAnnotationsGet').its('response.statusCode').should('equal', 200);
cy.get('.cvat-notification-notice-upload-annotations-fail').should('not.exist');

@ -1,4 +1,4 @@
// Copyright (C) 2020-2021 Intel Corporation
// Copyright (C) 2020-2022 Intel Corporation
//
// SPDX-License-Identifier: MIT
@ -10,7 +10,7 @@ context('Search task feature.', () => {
const caseId = '35';
function searchTask(option, result) {
cy.intercept('GET', '/api/v1/tasks**').as('searchTask');
cy.intercept('GET', '/api/tasks**').as('searchTask');
cy.get('.cvat-search-field').find('[placeholder="Search"]').clear().type(`${option}{Enter}`);
cy.wait('@searchTask').its('response.statusCode').should('equal', 200);
cy.get('.cvat-spinner').should('not.exist');

@ -1,4 +1,4 @@
// Copyright (C) 2021 Intel Corporation
// Copyright (C) 2021-2022 Intel Corporation
//
// SPDX-License-Identifier: MIT
@ -93,7 +93,7 @@ context('Export, import an annotation task.', { browser: '!firefox' }, () => {
});
it('Import the task. Check id, labels, shape.', () => {
cy.intercept('POST', '/api/v1/tasks/backup?**').as('importTask');
cy.intercept('POST', '/api/tasks/backup?**').as('importTask');
cy.get('.cvat-import-task').click().find('input[type=file]').attachFile(taskBackupArchiveFullName);
cy.wait('@importTask', { timeout: 5000 }).its('response.statusCode').should('equal', 202);
cy.wait('@importTask').its('response.statusCode').should('equal', 201);

@ -1,4 +1,4 @@
// Copyright (C) 2021 Intel Corporation
// Copyright (C) 2021-2022 Intel Corporation
//
// SPDX-License-Identifier: MIT
@ -15,7 +15,7 @@ context('Dummy cloud storages.', { browser: '!firefox' }, () => {
const imageFolder = '../integration/actions_tasks3/assets/case_109';
function testListDummyCloudStorages(dummyCS) {
cy.intercept('GET', 'api/v1/cloudstorages?**', dummyCS).as('listCS');
cy.intercept('GET', 'api/cloudstorages?**', dummyCS).as('listCS');
cy.contains('.cvat-header-button', 'Cloud Storages').should('be.visible').click();
cy.wait('@listCS').its('response.statusCode').should('eq', 200);
cy.get('.cvat-cloud-storage-item-empty-preview').should('have.length', 1);
@ -57,10 +57,10 @@ context('Dummy cloud storages.', { browser: '!firefox' }, () => {
}
function testCSSetStatusPreview(id, status, image) {
cy.intercept('GET', `api/v1/cloudstorages/${id}/status?**`, status).as('csStatus');
cy.intercept('GET', `api/cloudstorages/${id}/status?**`, status).as('csStatus');
cy.intercept(
'GET',
`api/v1/cloudstorages/${id}/preview?**`,
`api/cloudstorages/${id}/preview?**`,
{ fixture: `${imageFolder}/${image}` },
).as('csPreview');

@ -1,4 +1,4 @@
// Copyright (C) 2021 Intel Corporation
// Copyright (C) 2021-2022 Intel Corporation
//
// SPDX-License-Identifier: MIT
@ -33,14 +33,14 @@ context('Changing a default value for an attribute.', () => {
describe(`Testing case "${caseId}", issue 2968`, () => {
it('Add a label, add text (leave its value empty by default) & checkbox attributes.', () => {
cy.intercept('PATCH', '/api/v1/tasks/**').as('patchTask');
cy.intercept('PATCH', '/api/tasks/**').as('patchTask');
cy.addNewLabel(additionalLabel, additionalAttrsLabel);
cy.wait('@patchTask').its('response.statusCode').should('equal', 200);
cy.get('.cvat-constructor-viewer').should('exist').and('be.visible');
});
it('Open label editor. Change default values for text & checkbox attributes, press Done.', () => {
cy.intercept('PATCH', '/api/v1/tasks/**').as('patchTask');
cy.intercept('PATCH', '/api/tasks/**').as('patchTask');
cy.get('.cvat-constructor-viewer').within(() => {
// eslint-disable-next-line security/detect-non-literal-regexp
cy.contains(new RegExp(`^${additionalLabel}$`))

@ -1,4 +1,4 @@
// Copyright (C) 2020-2021 Intel Corporation
// Copyright (C) 2020-2022 Intel Corporation
//
// SPDX-License-Identifier: MIT
@ -59,7 +59,7 @@ context('When clicking on the Logout button, get the user session closed.', () =
// get token and login to task
cy.request({
method: 'POST',
url: '/api/v1/auth/login',
url: '/api/auth/login',
body: {
username: Cypress.env('user'),
email: Cypress.env('email'),

@ -207,7 +207,7 @@ context('Review pipeline feature', () => {
});
it('Second user sends the job to review.', () => {
cy.intercept('POST', '/api/v1/server/logs').as('sendLogs');
cy.intercept('POST', '/api/server/logs').as('sendLogs');
cy.interactMenu('Request a review');
cy.contains('.cvat-modal-content-save-job', 'The job has unsaved annotations')
.should('exist')

@ -48,7 +48,7 @@ context('Multiple users. Assign task, job. Deactivating users.', () => {
function changeCheckUserStatusOpenTask(userName) {
cy.changeUserActiveStatus(authKey, userName, isActive);
cy.checkUserStatuses(authKey, userName, isStaff, isSuperuser, isActive);
cy.intercept('GET', `/api/v1/users*${thirdUserName}*`).as('users');
cy.intercept('GET', `/api/users*${thirdUserName}*`).as('users');
cy.openTask(taskName);
cy.wait('@users');
cy.get('.cvat-global-boundary').should('not.exist');
@ -161,7 +161,7 @@ context('Multiple users. Assign task, job. Deactivating users.', () => {
it('First user login. Getting authKey.', () => {
cy.visit('/');
cy.intercept('POST', '/api/v1/auth/login**').as('login');
cy.intercept('POST', '/api/auth/login**').as('login');
cy.login();
cy.wait('@login').then((response) => {
authKey = response.response.body.key;

@ -1,4 +1,4 @@
// Copyright (C) 2021 Intel Corporation
// Copyright (C) 2021-2022 Intel Corporation
//
// SPDX-License-Identifier: MIT
@ -75,7 +75,7 @@ context('Canvas 3D functionality. Dump/upload annotation. "Point Cloud" format',
});
});
confirmUpdate('.cvat-modal-content-load-job-annotation');
cy.intercept('GET', '/api/v1/jobs/**/annotations**').as('uploadAnnotationsGet');
cy.intercept('GET', '/api/jobs/**/annotations**').as('uploadAnnotationsGet');
cy.wait('@uploadAnnotationsGet').its('response.statusCode').should('equal', 200);
cy.get('#cvat-objects-sidebar-state-item-1').should('exist');
cy.removeAnnotations();

@ -1,4 +1,4 @@
// Copyright (C) 2021 Intel Corporation
// Copyright (C) 2021-2022 Intel Corporation
//
// SPDX-License-Identifier: MIT
@ -75,7 +75,7 @@ context('Canvas 3D functionality. Dump/upload annotation. "Velodyne Points" form
});
});
confirmUpdate('.cvat-modal-content-load-job-annotation');
cy.intercept('GET', '/api/v1/jobs/**/annotations**').as('uploadAnnotationsGet');
cy.intercept('GET', '/api/jobs/**/annotations**').as('uploadAnnotationsGet');
cy.wait('@uploadAnnotationsGet').its('response.statusCode').should('equal', 200);
cy.get('#cvat-objects-sidebar-state-item-1').should('exist');
cy.removeAnnotations();

@ -1,4 +1,4 @@
// Copyright (C) 2020-2021 Intel Corporation
// Copyright (C) 2020-2022 Intel Corporation
//
// SPDX-License-Identifier: MIT
@ -28,7 +28,7 @@ context('Check email verification system', () => {
describe(`Case: "${caseId}"`, () => {
it('Register user. Notification exist. The response status is successful.', () => {
cy.intercept('POST', '/api/v1/auth/register?**').as('userRegister');
cy.intercept('POST', '/api/auth/register?**').as('userRegister');
cy.userRegistration(firstName, lastName, userName, emailAddr, password);
cy.get('.ant-notification-topRight')
.contains(`We have sent an email with a confirmation link to ${emailAddr}.`)

@ -1,4 +1,4 @@
// Copyright (C) 2020-2021 Intel Corporation
// Copyright (C) 2020-2022 Intel Corporation
//
// SPDX-License-Identifier: MIT
@ -11,7 +11,7 @@ context('Check error сannot read property at saving job', () => {
const createRectangleShape2Points = {
points: 'By 2 Points',
type: 'Shape',
labelName: labelName,
labelName,
firstX: 100,
firstY: 100,
secondX: 300,
@ -38,7 +38,7 @@ context('Check error сannot read property at saving job', () => {
});
it('Save job and go to previous frame at saving job', () => {
cy.intercept('PATCH', '/api/v1/jobs/**').as('saveJob');
cy.intercept('PATCH', '/api/jobs/**').as('saveJob');
cy.saveJob();
cy.get('body').type('d');
cy.wait('@saveJob').its('response.statusCode').should('equal', 200);

@ -10,7 +10,7 @@ describe('Delete users, tasks, projects, organizations created during the tests
it('Get token', () => {
cy.request({
method: 'POST',
url: '/api/v1/auth/login',
url: '/api/auth/login',
body: {
username: Cypress.env('user'),
email: Cypress.env('email'),
@ -23,7 +23,7 @@ describe('Delete users, tasks, projects, organizations created during the tests
it('Get a list of tasks and delete them all', () => {
cy.request({
url: '/api/v1/tasks?page_size=1000',
url: '/api/tasks?page_size=1000',
headers: {
Authorization: `Token ${authKey}`,
},
@ -33,7 +33,7 @@ describe('Delete users, tasks, projects, organizations created during the tests
const { id } = task;
cy.request({
method: 'DELETE',
url: `/api/v1/tasks/${id}`,
url: `/api/tasks/${id}`,
headers: {
Authorization: `Token ${authKey}`,
},
@ -44,7 +44,7 @@ describe('Delete users, tasks, projects, organizations created during the tests
it('Get a list of projects and delete them all', () => {
cy.request({
url: '/api/v1/projects?page_size=all',
url: '/api/projects?page_size=all',
headers: {
Authorization: `Token ${authKey}`,
},
@ -54,7 +54,7 @@ describe('Delete users, tasks, projects, organizations created during the tests
const { id } = project;
cy.request({
method: 'DELETE',
url: `/api/v1/projects/${id}`,
url: `/api/projects/${id}`,
headers: {
Authorization: `Token ${authKey}`,
},
@ -65,7 +65,7 @@ describe('Delete users, tasks, projects, organizations created during the tests
it('Get a list of organizations and delete them all', () => {
cy.request({
url: '/api/v1/organizations?page_size=all',
url: '/api/organizations?page_size=all',
headers: {
Authorization: `Token ${authKey}`,
},
@ -75,7 +75,7 @@ describe('Delete users, tasks, projects, organizations created during the tests
const { id } = org;
cy.request({
method: 'DELETE',
url: `/api/v1/organizations/${id}`,
url: `/api/organizations/${id}`,
headers: {
Authorization: `Token ${authKey}`,
},
@ -86,7 +86,7 @@ describe('Delete users, tasks, projects, organizations created during the tests
it('Get a list of users and delete all except id:1', () => {
cy.request({
url: '/api/v1/users',
url: '/api/users',
headers: {
Authorization: `Token ${authKey}`,
},
@ -97,7 +97,7 @@ describe('Delete users, tasks, projects, organizations created during the tests
if (id !== 1) {
cy.request({
method: 'DELETE',
url: `/api/v1/users/${id}`,
url: `/api/users/${id}`,
headers: {
Authorization: `Token ${authKey}`,
},

@ -55,7 +55,7 @@ Cypress.Commands.add('userRegistration', (firstName, lastName, userName, emailAd
Cypress.Commands.add('getAuthKey', () => {
cy.request({
method: 'POST',
url: '/api/v1/auth/login',
url: '/api/auth/login',
body: {
username: Cypress.env('user'),
email: Cypress.env('email'),
@ -67,7 +67,7 @@ Cypress.Commands.add('getAuthKey', () => {
Cypress.Commands.add('deleteUsers', (authResponse, accountsToDelete) => {
const authKey = authResponse.body.key;
cy.request({
url: '/api/v1/users?page_size=all',
url: '/api/users?page_size=all',
headers: {
Authorization: `Token ${authKey}`,
},
@ -79,7 +79,7 @@ Cypress.Commands.add('deleteUsers', (authResponse, accountsToDelete) => {
if (username === account) {
cy.request({
method: 'DELETE',
url: `/api/v1/users/${id}`,
url: `/api/users/${id}`,
headers: {
Authorization: `Token ${authKey}`,
},
@ -92,7 +92,7 @@ Cypress.Commands.add('deleteUsers', (authResponse, accountsToDelete) => {
Cypress.Commands.add('changeUserActiveStatus', (authKey, accountsToChangeActiveStatus, isActive) => {
cy.request({
url: '/api/v1/users?page_size=all',
url: '/api/users?page_size=all',
headers: {
Authorization: `Token ${authKey}`,
},
@ -104,7 +104,7 @@ Cypress.Commands.add('changeUserActiveStatus', (authKey, accountsToChangeActiveS
if (userName.includes(accountsToChangeActiveStatus)) {
cy.request({
method: 'PATCH',
url: `/api/v1/users/${userId}`,
url: `/api/users/${userId}`,
headers: {
Authorization: `Token ${authKey}`,
},
@ -119,7 +119,7 @@ Cypress.Commands.add('changeUserActiveStatus', (authKey, accountsToChangeActiveS
Cypress.Commands.add('checkUserStatuses', (authKey, userName, staffStatus, superuserStatus, activeStatus) => {
cy.request({
url: '/api/v1/users?page_size=all',
url: '/api/users?page_size=all',
headers: {
Authorization: `Token ${authKey}`,
},
@ -138,7 +138,7 @@ Cypress.Commands.add('checkUserStatuses', (authKey, userName, staffStatus, super
Cypress.Commands.add('deleteTasks', (authResponse, tasksToDelete) => {
const authKey = authResponse.body.key;
cy.request({
url: '/api/v1/tasks?page_size=all',
url: '/api/tasks?page_size=all',
headers: {
Authorization: `Token ${authKey}`,
},
@ -150,7 +150,7 @@ Cypress.Commands.add('deleteTasks', (authResponse, tasksToDelete) => {
if (name === taskToDelete) {
cy.request({
method: 'DELETE',
url: `/api/v1/tasks/${id}`,
url: `/api/tasks/${id}`,
headers: {
Authorization: `Token ${authKey}`,
},
@ -234,7 +234,7 @@ Cypress.Commands.add('openTask', (taskName, projectSubsetFieldValue) => {
});
Cypress.Commands.add('saveJob', (method = 'PATCH', status = 200, as = 'saveJob') => {
cy.intercept(method, '/api/v1/jobs/**').as(as);
cy.intercept(method, '/api/jobs/**').as(as);
cy.get('button').contains('Save').click({ force: true }).trigger('mouseout');
cy.wait(`@${as}`).its('response.statusCode').should('equal', status);
});

@ -22,7 +22,7 @@ Cypress.Commands.add('createOrganization', (organizationParams) => {
cy.get('#email').type(organizationParams.email);
cy.get('#phoneNumber').type(organizationParams.phoneNumber);
cy.get('#location').type(organizationParams.location);
cy.intercept('POST', '/api/v1/organizations**').as('createOrganizations');
cy.intercept('POST', '/api/organizations**').as('createOrganizations');
cy.get('[type="submit"]').click();
cy.wait('@createOrganizations').its('response.statusCode').should('equal', 201);
});
@ -31,7 +31,7 @@ Cypress.Commands.add('createOrganization', (organizationParams) => {
Cypress.Commands.add('deleteOrganizations', (authResponse, otrganizationsToDelete) => {
const authKey = authResponse.body.key;
cy.request({
url: '/api/v1/organizations?page_size=all',
url: '/api/organizations?page_size=all',
headers: {
Authorization: `Token ${authKey}`,
},
@ -43,7 +43,7 @@ Cypress.Commands.add('deleteOrganizations', (authResponse, otrganizationsToDelet
if (slug === organizationToDelete) {
cy.request({
method: 'DELETE',
url: `/api/v1/organizations/${id}`,
url: `/api/organizations/${id}`,
headers: {
Authorization: `Token ${authKey}`,
},

@ -41,7 +41,7 @@ Cypress.Commands.add(
Cypress.Commands.add('deleteProjects', (authResponse, projectsToDelete) => {
const authKey = authResponse.body.key;
cy.request({
url: '/api/v1/projects?page_size=all',
url: '/api/projects?page_size=all',
headers: {
Authorization: `Token ${authKey}`,
},
@ -53,7 +53,7 @@ Cypress.Commands.add('deleteProjects', (authResponse, projectsToDelete) => {
if (name === projectToDelete) {
cy.request({
method: 'DELETE',
url: `/api/v1/projects/${id}`,
url: `/api/projects/${id}`,
headers: {
Authorization: `Token ${authKey}`,
},
@ -139,7 +139,7 @@ Cypress.Commands.add('backupProject', (projectName) => {
});
Cypress.Commands.add('restoreProject', (archiveWithBackup) => {
cy.intercept('POST', '/api/v1/projects/backup?**').as('restoreProject');
cy.intercept('POST', '/api/projects/backup?**').as('restoreProject');
cy.get('.cvat-import-project').click().find('input[type=file]').attachFile(archiveWithBackup);
cy.wait('@restoreProject', { timeout: 5000 }).its('response.statusCode').should('equal', 202);
cy.wait('@restoreProject').its('response.statusCode').should('equal', 201);

@ -23,7 +23,7 @@ Cypress.Commands.add('assignJobToUser', (jobID, user) => {
.click();
});
cy.intercept('PATCH', '/api/v1/jobs/**').as('patchJobAssignee');
cy.intercept('PATCH', '/api/jobs/**').as('patchJobAssignee');
cy.get('.ant-select-dropdown')
.should('be.visible')
.not('.ant-select-dropdown-hidden')
@ -146,7 +146,7 @@ Cypress.Commands.add('createIssueFromControlButton', (createIssueParams) => {
.trigger('mousedown', createIssueParams.firstX, createIssueParams.firstY, { button: 0 })
.trigger('mouseup');
}
cy.intercept('POST', '/api/v1/issues?*').as('issues');
cy.intercept('POST', '/api/issues?*').as('issues');
cy.get('.cvat-create-issue-dialog').within(() => {
cy.get('#issue_description').type(createIssueParams.description);
cy.get('[type="submit"]').click();
@ -157,8 +157,8 @@ Cypress.Commands.add('createIssueFromControlButton', (createIssueParams) => {
Cypress.Commands.add('resolveReopenIssue', (issueLabel, resolveText, reopen) => {
cy.get(issueLabel).click();
cy.intercept('POST', '/api/v1/comments').as('postComment');
cy.intercept('PATCH', '/api/v1/issues/**').as('resolveReopenIssue');
cy.intercept('POST', '/api/comments').as('postComment');
cy.intercept('PATCH', '/api/issues/**').as('resolveReopenIssue');
cy.get('.cvat-issue-dialog-input').type(resolveText);
cy.get('.cvat-issue-dialog-footer').within(() => {
cy.contains('button', 'Comment').click();
@ -175,7 +175,7 @@ Cypress.Commands.add('resolveReopenIssue', (issueLabel, resolveText, reopen) =>
Cypress.Commands.add('removeIssue', (issueLabel, submitRemove) => {
cy.get(issueLabel).click();
cy.intercept('DELETE', '/api/v1/issues/**').as('removeIssue');
cy.intercept('DELETE', '/api/issues/**').as('removeIssue');
cy.get('.cvat-issue-dialog-footer').within(() => {
cy.contains('button', 'Remove').click();
});
@ -194,7 +194,7 @@ Cypress.Commands.add('submitReview', (decision, user) => {
cy.get('.cvat-submit-review-dialog').within(() => {
cy.contains(new RegExp(`^${decision}$`, 'g')).click();
if (decision === 'Review next') {
cy.intercept('GET', `/api/v1/users?search=${user}&limit=10&is_active=true`).as('searchUsers');
cy.intercept('GET', `/api/users?search=${user}&limit=10&is_active=true`).as('searchUsers');
cy.get('.cvat-user-search-field').within(() => {
cy.get('input[type="search"]').clear().type(`${user}`);
cy.wait('@searchUsers').its('response.statusCode').should('equal', 200);

@ -1,10 +1,10 @@
// Copyright (C) 2020 Intel Corporation
// Copyright (C) 2020-2022 Intel Corporation
//
// SPDX-License-Identifier: MIT
/// <reference types="cypress" />
export const labelName = `Main task`;
export const labelName = 'Main task';
export const taskName = `New annotation task for ${labelName}`;
export const attrName = `Attr for ${labelName}`;
export const textDefaultValue = 'Some default value for type Text';
@ -28,8 +28,8 @@ export const advancedConfigurationParams = {
frameStep: 2,
};
export const multiAttrParams = {
additionalAttrName: `Attr 2`,
additionalValue: `Attr value 2`,
additionalAttrName: 'Attr 2',
additionalValue: 'Attr value 2',
typeAttribute: 'Text',
};
@ -37,14 +37,14 @@ it('Prepare to testing', () => {
cy.visit('/');
cy.login();
cy.get('.cvat-tasks-page').should('exist');
let listItems = [];
const listItems = [];
cy.document().then((doc) => {
const collection = Array.from(doc.querySelectorAll('.cvat-item-task-name'));
for (let i = 0; i < collection.length; i++) {
listItems.push(collection[i].innerText);
}
if (listItems.indexOf(taskName) === -1) {
cy.task('log', "A task doesn't exist. Creating.");
cy.task('log', 'A task doesn\'t exist. Creating.');
cy.imageGenerator(imagesFolder, imageFileName, width, height, color, posX, posY, labelName, imagesCount);
cy.createZipArchive(directoryToArchive, archivePath);
cy.createAnnotationTask(

@ -1,4 +1,4 @@
// Copyright (C) 2021 Intel Corporation
// Copyright (C) 2021-2022 Intel Corporation
//
// SPDX-License-Identifier: MIT
@ -10,7 +10,7 @@ const dummyGoogleStorage = {
{
id: 3,
owner: {
url: 'http://localhost:7000/api/v1/users/1',
url: 'http://localhost:7000/api/users/1',
id: 1,
username: 'maya',
first_name: '',
@ -39,7 +39,7 @@ const dummyAzureContainer = {
{
id: 2,
owner: {
url: 'http://localhost:7000/api/v1/users/1',
url: 'http://localhost:7000/api/users/1',
id: 1,
username: 'maya',
first_name: '',
@ -68,7 +68,7 @@ const dummyAWSBucket = {
{
id: 1,
owner: {
url: 'http://localhost:7000/api/v1/users/1',
url: 'http://localhost:7000/api/users/1',
id: 1,
username: 'maya',
first_name: '',

@ -67,7 +67,7 @@ docker exec cvat_db pg_dump -c -Fp -U root -d cvat > assets/cvat_db/cvat_db.sql
docker run --rm --volumes-from cvat ubuntu tar -cjv /home/django/data > assets/cvat_data.tar.bz2
```
# How to update *.json files in the assets directory?
## How to update *.json files in the assets directory?
If you have updated the test database and want to update the assets/*.json
files as well, run the appropriate script:
@ -76,7 +76,7 @@ files as well, run the appropriate script:
python utils/dump_objects.py
```
# How to restore DB and data volume?
## How to restore DB and data volume?
To restore DB and data volume, please use commands below.

@ -11,7 +11,7 @@
"first_name": "Business",
"id": 10,
"last_name": "First",
"url": "http://localhost:8080/api/v1/users/10",
"url": "http://localhost:8080/api/users/10",
"username": "business1"
},
"role": "maintainer",
@ -19,7 +19,7 @@
"first_name": "User",
"id": 5,
"last_name": "Fourth",
"url": "http://localhost:8080/api/v1/users/5",
"url": "http://localhost:8080/api/users/5",
"username": "user4"
}
},
@ -31,7 +31,7 @@
"first_name": "Business",
"id": 10,
"last_name": "First",
"url": "http://localhost:8080/api/v1/users/10",
"url": "http://localhost:8080/api/users/10",
"username": "business1"
},
"role": "supervisor",
@ -39,7 +39,7 @@
"first_name": "User",
"id": 4,
"last_name": "Third",
"url": "http://localhost:8080/api/v1/users/4",
"url": "http://localhost:8080/api/users/4",
"username": "user3"
}
},
@ -51,7 +51,7 @@
"first_name": "Business",
"id": 10,
"last_name": "First",
"url": "http://localhost:8080/api/v1/users/10",
"url": "http://localhost:8080/api/users/10",
"username": "business1"
},
"role": "supervisor",
@ -59,7 +59,7 @@
"first_name": "User",
"id": 3,
"last_name": "Second",
"url": "http://localhost:8080/api/v1/users/3",
"url": "http://localhost:8080/api/users/3",
"username": "user2"
}
},
@ -71,7 +71,7 @@
"first_name": "Business",
"id": 10,
"last_name": "First",
"url": "http://localhost:8080/api/v1/users/10",
"url": "http://localhost:8080/api/users/10",
"username": "business1"
},
"role": "worker",
@ -79,7 +79,7 @@
"first_name": "Worker",
"id": 8,
"last_name": "Third",
"url": "http://localhost:8080/api/v1/users/8",
"url": "http://localhost:8080/api/users/8",
"username": "worker3"
}
},
@ -91,7 +91,7 @@
"first_name": "Business",
"id": 10,
"last_name": "First",
"url": "http://localhost:8080/api/v1/users/10",
"url": "http://localhost:8080/api/users/10",
"username": "business1"
},
"role": "worker",
@ -99,7 +99,7 @@
"first_name": "Worker",
"id": 7,
"last_name": "Second",
"url": "http://localhost:8080/api/v1/users/7",
"url": "http://localhost:8080/api/users/7",
"username": "worker2"
}
},
@ -111,7 +111,7 @@
"first_name": "Business",
"id": 10,
"last_name": "First",
"url": "http://localhost:8080/api/v1/users/10",
"url": "http://localhost:8080/api/users/10",
"username": "business1"
},
"role": "maintainer",
@ -119,7 +119,7 @@
"first_name": "Business",
"id": 11,
"last_name": "Second",
"url": "http://localhost:8080/api/v1/users/11",
"url": "http://localhost:8080/api/users/11",
"username": "business2"
}
},
@ -131,7 +131,7 @@
"first_name": "User",
"id": 2,
"last_name": "First",
"url": "http://localhost:8080/api/v1/users/2",
"url": "http://localhost:8080/api/users/2",
"username": "user1"
},
"role": "maintainer",
@ -139,7 +139,7 @@
"first_name": "Business",
"id": 10,
"last_name": "First",
"url": "http://localhost:8080/api/v1/users/10",
"url": "http://localhost:8080/api/users/10",
"username": "business1"
}
},
@ -151,7 +151,7 @@
"first_name": "User",
"id": 2,
"last_name": "First",
"url": "http://localhost:8080/api/v1/users/2",
"url": "http://localhost:8080/api/users/2",
"username": "user1"
},
"role": "worker",
@ -159,7 +159,7 @@
"first_name": "Worker",
"id": 7,
"last_name": "Second",
"url": "http://localhost:8080/api/v1/users/7",
"url": "http://localhost:8080/api/users/7",
"username": "worker2"
}
},
@ -171,7 +171,7 @@
"first_name": "User",
"id": 2,
"last_name": "First",
"url": "http://localhost:8080/api/v1/users/2",
"url": "http://localhost:8080/api/users/2",
"username": "user1"
},
"role": "worker",
@ -179,7 +179,7 @@
"first_name": "Worker",
"id": 6,
"last_name": "First",
"url": "http://localhost:8080/api/v1/users/6",
"url": "http://localhost:8080/api/users/6",
"username": "worker1"
}
}

@ -32,14 +32,14 @@
"status": "annotation",
"stop_frame": 129,
"task_id": 1,
"url": "http://localhost:8080/api/v1/jobs/1"
"url": "http://localhost:8080/api/jobs/1"
},
{
"assignee": {
"first_name": "Worker",
"id": 6,
"last_name": "First",
"url": "http://localhost:8080/api/v1/users/6",
"url": "http://localhost:8080/api/users/6",
"username": "worker1"
},
"bug_tracker": null,
@ -69,7 +69,7 @@
"status": "annotation",
"stop_frame": 22,
"task_id": 2,
"url": "http://localhost:8080/api/v1/jobs/2"
"url": "http://localhost:8080/api/jobs/2"
},
{
"assignee": null,
@ -113,7 +113,7 @@
"status": "annotation",
"stop_frame": 49,
"task_id": 3,
"url": "http://localhost:8080/api/v1/jobs/3"
"url": "http://localhost:8080/api/jobs/3"
},
{
"assignee": null,
@ -157,7 +157,7 @@
"status": "validation",
"stop_frame": 99,
"task_id": 3,
"url": "http://localhost:8080/api/v1/jobs/4"
"url": "http://localhost:8080/api/jobs/4"
},
{
"assignee": null,
@ -201,14 +201,14 @@
"status": "validation",
"stop_frame": 147,
"task_id": 3,
"url": "http://localhost:8080/api/v1/jobs/5"
"url": "http://localhost:8080/api/jobs/5"
},
{
"assignee": {
"first_name": "Worker",
"id": 7,
"last_name": "Second",
"url": "http://localhost:8080/api/v1/users/7",
"url": "http://localhost:8080/api/users/7",
"username": "worker2"
},
"bug_tracker": "",
@ -238,7 +238,7 @@
"status": "annotation",
"stop_frame": 57,
"task_id": 4,
"url": "http://localhost:8080/api/v1/jobs/6"
"url": "http://localhost:8080/api/jobs/6"
}
]
}

@ -14,7 +14,7 @@
"first_name": "User",
"id": 5,
"last_name": "Fourth",
"url": "http://localhost:8080/api/v1/users/5",
"url": "http://localhost:8080/api/users/5",
"username": "user4"
}
},
@ -29,7 +29,7 @@
"first_name": "User",
"id": 4,
"last_name": "Third",
"url": "http://localhost:8080/api/v1/users/4",
"url": "http://localhost:8080/api/users/4",
"username": "user3"
}
},
@ -44,7 +44,7 @@
"first_name": "User",
"id": 3,
"last_name": "Second",
"url": "http://localhost:8080/api/v1/users/3",
"url": "http://localhost:8080/api/users/3",
"username": "user2"
}
},
@ -59,7 +59,7 @@
"first_name": "Worker",
"id": 8,
"last_name": "Third",
"url": "http://localhost:8080/api/v1/users/8",
"url": "http://localhost:8080/api/users/8",
"username": "worker3"
}
},
@ -74,7 +74,7 @@
"first_name": "Worker",
"id": 7,
"last_name": "Second",
"url": "http://localhost:8080/api/v1/users/7",
"url": "http://localhost:8080/api/users/7",
"username": "worker2"
}
},
@ -89,7 +89,7 @@
"first_name": "Business",
"id": 11,
"last_name": "Second",
"url": "http://localhost:8080/api/v1/users/11",
"url": "http://localhost:8080/api/users/11",
"username": "business2"
}
},
@ -104,7 +104,7 @@
"first_name": "Business",
"id": 10,
"last_name": "First",
"url": "http://localhost:8080/api/v1/users/10",
"url": "http://localhost:8080/api/users/10",
"username": "business1"
}
},
@ -119,7 +119,7 @@
"first_name": "Business",
"id": 10,
"last_name": "First",
"url": "http://localhost:8080/api/v1/users/10",
"url": "http://localhost:8080/api/users/10",
"username": "business1"
}
},
@ -134,7 +134,7 @@
"first_name": "Worker",
"id": 7,
"last_name": "Second",
"url": "http://localhost:8080/api/v1/users/7",
"url": "http://localhost:8080/api/users/7",
"username": "worker2"
}
},
@ -149,7 +149,7 @@
"first_name": "Worker",
"id": 6,
"last_name": "First",
"url": "http://localhost:8080/api/v1/users/6",
"url": "http://localhost:8080/api/users/6",
"username": "worker1"
}
},
@ -164,7 +164,7 @@
"first_name": "User",
"id": 2,
"last_name": "First",
"url": "http://localhost:8080/api/v1/users/2",
"url": "http://localhost:8080/api/users/2",
"username": "user1"
}
}

@ -11,7 +11,7 @@
"first_name": "Business",
"id": 10,
"last_name": "First",
"url": "http://localhost:8080/api/v1/users/10",
"url": "http://localhost:8080/api/users/10",
"username": "business1"
},
"slug": "org2",
@ -29,7 +29,7 @@
"first_name": "User",
"id": 2,
"last_name": "First",
"url": "http://localhost:8080/api/v1/users/2",
"url": "http://localhost:8080/api/users/2",
"username": "user1"
},
"slug": "org1",

@ -8,7 +8,7 @@
"first_name": "User",
"id": 3,
"last_name": "Second",
"url": "http://localhost:8080/api/v1/users/3",
"url": "http://localhost:8080/api/users/3",
"username": "user2"
},
"bug_tracker": "",
@ -35,7 +35,7 @@
"first_name": "Business",
"id": 10,
"last_name": "First",
"url": "http://localhost:8080/api/v1/users/10",
"url": "http://localhost:8080/api/users/10",
"username": "business1"
},
"status": "annotation",
@ -47,7 +47,7 @@
],
"training_project": null,
"updated_date": "2021-12-14T19:55:57.483506Z",
"url": "http://localhost:8080/api/v1/projects/2"
"url": "http://localhost:8080/api/projects/2"
},
{
"assignee": null,
@ -88,7 +88,7 @@
"first_name": "Business",
"id": 10,
"last_name": "First",
"url": "http://localhost:8080/api/v1/users/10",
"url": "http://localhost:8080/api/users/10",
"username": "business1"
},
"status": "annotation",
@ -100,7 +100,7 @@
],
"training_project": null,
"updated_date": "2021-12-14T19:48:33.103265Z",
"url": "http://localhost:8080/api/v1/projects/1"
"url": "http://localhost:8080/api/projects/1"
}
]
}

@ -36,7 +36,7 @@
"first_name": "Business",
"id": 10,
"last_name": "First",
"url": "http://localhost:8080/api/v1/users/10",
"url": "http://localhost:8080/api/users/10",
"username": "business1"
},
"project_id": 2,
@ -49,14 +49,14 @@
"first_name": "Worker",
"id": 7,
"last_name": "Second",
"url": "http://localhost:8080/api/v1/users/7",
"url": "http://localhost:8080/api/users/7",
"username": "worker2"
},
"id": 6,
"stage": "annotation",
"state": "new",
"status": "annotation",
"url": "http://localhost:8080/api/v1/jobs/6"
"url": "http://localhost:8080/api/jobs/6"
}
],
"start_frame": 0,
@ -67,7 +67,7 @@
"status": "annotation",
"subset": "train",
"updated_date": "2021-12-22T07:17:34.836384Z",
"url": "http://localhost:8080/api/v1/tasks/4"
"url": "http://localhost:8080/api/tasks/4"
},
{
"assignee": null,
@ -115,7 +115,7 @@
"first_name": "Business",
"id": 10,
"last_name": "First",
"url": "http://localhost:8080/api/v1/users/10",
"url": "http://localhost:8080/api/users/10",
"username": "business1"
},
"project_id": 1,
@ -129,7 +129,7 @@
"stage": "annotation",
"state": "new",
"status": "annotation",
"url": "http://localhost:8080/api/v1/jobs/3"
"url": "http://localhost:8080/api/jobs/3"
}
],
"start_frame": 0,
@ -143,7 +143,7 @@
"stage": "validation",
"state": "new",
"status": "validation",
"url": "http://localhost:8080/api/v1/jobs/4"
"url": "http://localhost:8080/api/jobs/4"
}
],
"start_frame": 50,
@ -157,7 +157,7 @@
"stage": "acceptance",
"state": "new",
"status": "validation",
"url": "http://localhost:8080/api/v1/jobs/5"
"url": "http://localhost:8080/api/jobs/5"
}
],
"start_frame": 100,
@ -168,7 +168,7 @@
"status": "annotation",
"subset": "Train",
"updated_date": "2021-12-22T07:19:33.854760Z",
"url": "http://localhost:8080/api/v1/tasks/3"
"url": "http://localhost:8080/api/tasks/3"
},
{
"assignee": null,
@ -203,7 +203,7 @@
"first_name": "User",
"id": 2,
"last_name": "First",
"url": "http://localhost:8080/api/v1/users/2",
"url": "http://localhost:8080/api/users/2",
"username": "user1"
},
"project_id": null,
@ -216,14 +216,14 @@
"first_name": "Worker",
"id": 6,
"last_name": "First",
"url": "http://localhost:8080/api/v1/users/6",
"url": "http://localhost:8080/api/users/6",
"username": "worker1"
},
"id": 2,
"stage": "annotation",
"state": "new",
"status": "annotation",
"url": "http://localhost:8080/api/v1/jobs/2"
"url": "http://localhost:8080/api/jobs/2"
}
],
"start_frame": 0,
@ -234,7 +234,7 @@
"status": "annotation",
"subset": "",
"updated_date": "2021-12-22T07:14:15.234748Z",
"url": "http://localhost:8080/api/v1/tasks/2"
"url": "http://localhost:8080/api/tasks/2"
},
{
"assignee": null,
@ -269,7 +269,7 @@
"first_name": "User",
"id": 2,
"last_name": "First",
"url": "http://localhost:8080/api/v1/users/2",
"url": "http://localhost:8080/api/users/2",
"username": "user1"
},
"project_id": null,
@ -283,7 +283,7 @@
"stage": "annotation",
"state": "new",
"status": "annotation",
"url": "http://localhost:8080/api/v1/jobs/1"
"url": "http://localhost:8080/api/jobs/1"
}
],
"start_frame": 0,
@ -294,7 +294,7 @@
"status": "annotation",
"subset": "",
"updated_date": "2021-12-22T07:15:22.942484Z",
"url": "http://localhost:8080/api/v1/tasks/1"
"url": "http://localhost:8080/api/tasks/1"
}
]
}

@ -16,7 +16,7 @@
"is_superuser": true,
"last_login": "2021-12-22T08:11:58.502575Z",
"last_name": "First",
"url": "http://localhost:8080/api/v1/users/1",
"url": "http://localhost:8080/api/users/1",
"username": "admin1"
},
{
@ -32,7 +32,7 @@
"is_superuser": false,
"last_login": "2021-12-22T07:55:35.269206Z",
"last_name": "First",
"url": "http://localhost:8080/api/v1/users/2",
"url": "http://localhost:8080/api/users/2",
"username": "user1"
},
{
@ -48,7 +48,7 @@
"is_superuser": false,
"last_login": null,
"last_name": "Second",
"url": "http://localhost:8080/api/v1/users/3",
"url": "http://localhost:8080/api/users/3",
"username": "user2"
},
{
@ -64,7 +64,7 @@
"is_superuser": false,
"last_login": null,
"last_name": "Third",
"url": "http://localhost:8080/api/v1/users/4",
"url": "http://localhost:8080/api/users/4",
"username": "user3"
},
{
@ -80,7 +80,7 @@
"is_superuser": false,
"last_login": null,
"last_name": "Fourth",
"url": "http://localhost:8080/api/v1/users/5",
"url": "http://localhost:8080/api/users/5",
"username": "user4"
},
{
@ -96,7 +96,7 @@
"is_superuser": false,
"last_login": "2021-12-14T19:11:21.048740Z",
"last_name": "First",
"url": "http://localhost:8080/api/v1/users/6",
"url": "http://localhost:8080/api/users/6",
"username": "worker1"
},
{
@ -112,7 +112,7 @@
"is_superuser": false,
"last_login": null,
"last_name": "Second",
"url": "http://localhost:8080/api/v1/users/7",
"url": "http://localhost:8080/api/users/7",
"username": "worker2"
},
{
@ -128,7 +128,7 @@
"is_superuser": false,
"last_login": null,
"last_name": "Third",
"url": "http://localhost:8080/api/v1/users/8",
"url": "http://localhost:8080/api/users/8",
"username": "worker3"
},
{
@ -144,7 +144,7 @@
"is_superuser": false,
"last_login": null,
"last_name": "Fourth",
"url": "http://localhost:8080/api/v1/users/9",
"url": "http://localhost:8080/api/users/9",
"username": "worker4"
},
{
@ -160,7 +160,7 @@
"is_superuser": false,
"last_login": "2022-01-19T13:52:59.477881Z",
"last_name": "First",
"url": "http://localhost:8080/api/v1/users/10",
"url": "http://localhost:8080/api/users/10",
"username": "business1"
},
{
@ -176,7 +176,7 @@
"is_superuser": false,
"last_login": null,
"last_name": "Second",
"url": "http://localhost:8080/api/v1/users/11",
"url": "http://localhost:8080/api/users/11",
"username": "business2"
},
{
@ -192,7 +192,7 @@
"is_superuser": false,
"last_login": null,
"last_name": "Third",
"url": "http://localhost:8080/api/v1/users/12",
"url": "http://localhost:8080/api/users/12",
"username": "business3"
},
{
@ -208,7 +208,7 @@
"is_superuser": false,
"last_login": null,
"last_name": "Fourth",
"url": "http://localhost:8080/api/v1/users/13",
"url": "http://localhost:8080/api/users/13",
"username": "business4"
},
{
@ -222,7 +222,7 @@
"is_superuser": false,
"last_login": null,
"last_name": "First",
"url": "http://localhost:8080/api/v1/users/14",
"url": "http://localhost:8080/api/users/14",
"username": "dummy1"
},
{
@ -236,7 +236,7 @@
"is_superuser": false,
"last_login": null,
"last_name": "Second",
"url": "http://localhost:8080/api/v1/users/15",
"url": "http://localhost:8080/api/users/15",
"username": "dummy2"
},
{
@ -250,7 +250,7 @@
"is_superuser": false,
"last_login": null,
"last_name": "Third",
"url": "http://localhost:8080/api/v1/users/16",
"url": "http://localhost:8080/api/users/16",
"username": "dummy3"
},
{
@ -264,7 +264,7 @@
"is_superuser": false,
"last_login": null,
"last_name": "Fourth",
"url": "http://localhost:8080/api/v1/users/17",
"url": "http://localhost:8080/api/users/17",
"username": "dummy4"
},
{
@ -280,7 +280,7 @@
"is_superuser": true,
"last_login": null,
"last_name": "Second",
"url": "http://localhost:8080/api/v1/users/18",
"url": "http://localhost:8080/api/users/18",
"username": "admin2"
}
]

@ -9,7 +9,7 @@ ROOT_DIR = osp.dirname(__file__)
ASSETS_DIR = osp.abspath(osp.join(ROOT_DIR, '..', 'assets'))
# Suppress the warning from Bandit about hardcoded passwords
USER_PASS = '!Q@W#E$R' # nosec
BASE_URL = 'http://localhost:8080/api/v1/'
BASE_URL = 'http://localhost:8080/api/'
def get_api_url(endpoint, **kwargs):
return BASE_URL + endpoint + '?' + '&'.join([f'{k}={v}' for k,v in kwargs.items()])

@ -7,4 +7,3 @@ for obj in ['user', 'project', 'task', 'job', 'organization', 'membership',
response = get_method('admin1', obj, page_size='all')
with open(osp.join(ASSETS_DIR, f'{obj}s.json'), 'w') as f:
json.dump(response.json(), f, indent=2, sort_keys=True)

@ -5,7 +5,7 @@ import logging
import requests
import sys
from http.client import HTTPConnection
from core.core import CLI, CVAT_API_V1
from core.core import CLI, CVAT_API_V2
from core.definition import parser
log = logging.getLogger(__name__)
@ -32,7 +32,7 @@ def main():
args = parser.parse_args()
config_log(args.loglevel)
with requests.Session() as session:
api = CVAT_API_V1('%s:%s' % (args.server_host, args.server_port), args.https)
api = CVAT_API_V2('%s:%s' % (args.server_host, args.server_port), args.https)
cli = CLI(session, api, args.auth)
try:
actions[args.action](cli, **args.__dict__)

@ -1,3 +1,3 @@
# SPDX-License-Identifier: MIT
from .definition import parser, ResourceType # noqa
from .core import CLI, CVAT_API_V1 # noqa
from .core import CLI, CVAT_API_V2 # noqa

@ -1,4 +1,4 @@
# Copyright (C) 2020-2021 Intel Corporation
# Copyright (C) 2020-2022 Intel Corporation
#
# SPDX-License-Identifier: MIT
@ -269,7 +269,7 @@ class CLI():
self.session.headers['X-CSRFToken'] = response.cookies['csrftoken']
class CVAT_API_V1():
class CVAT_API_V2():
""" Build parameterized API URLs """
def __init__(self, host, https=False):
@ -279,7 +279,7 @@ class CVAT_API_V1():
host = host.replace('http://', '')
host = host.replace('https://', '')
scheme = 'https' if https else 'http'
self.base = '{}://{}/api/v1/'.format(scheme, host)
self.base = '{}://{}/api/'.format(scheme, host)
self.git = f'{scheme}://{host}/git/repository/'
@property

@ -1,4 +1,4 @@
# Copyright (C) 2020 Intel Corporation
# Copyright (C) 2020-2022 Intel Corporation
#
# SPDX-License-Identifier: MIT
@ -14,7 +14,7 @@ from rest_framework.test import APITestCase, RequestsClient
from cvat.apps.engine.tests.test_rest_api import (create_db_users,
generate_image_file)
from utils.cli.core import CLI, CVAT_API_V1, ResourceType
from utils.cli.core import CLI, CVAT_API_V2, ResourceType
class TestCLI(APITestCase):
@ -22,7 +22,7 @@ class TestCLI(APITestCase):
def setUp(self, mock_stdout):
self.client = RequestsClient()
self.credentials = ('admin', 'admin')
self.api = CVAT_API_V1('testserver')
self.api = CVAT_API_V2('testserver')
self.cli = CLI(self.client, self.api, self.credentials)
self.taskname = 'test_task'
self.cli.tasks_create(self.taskname,

Loading…
Cancel
Save