Merge remote-tracking branch 'upstream/develop' into dkru/cypress-test-check-email-verification
commit
eb3feebbcd
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,37 @@
|
|||||||
|
# REST API design principles
|
||||||
|
|
||||||
|
## REST API scheme
|
||||||
|
|
||||||
|
Common scheme for our REST API is `<VERB> [namespace] <objects> <id> <action>`.
|
||||||
|
- `VERB` can be `POST`, `GET`, `PATCH`, `PUT`, `DELETE`.
|
||||||
|
- `namespace` should scope some specific functionality like `auth`, `lambda`.
|
||||||
|
It is optional in the scheme.
|
||||||
|
- Typical `objects` are `tasks`, `projects`, `jobs`.
|
||||||
|
- When you want to extract a specific object from a collection, just specify its `id`.
|
||||||
|
- An `action` can be used to simplify REST API or provide an endpoint for entities
|
||||||
|
without `objects` endpoint like `annotations`, `data`, `data/meta`. Note: action
|
||||||
|
should not duplicate other endpoints without a reason.
|
||||||
|
|
||||||
|
## Design principles
|
||||||
|
- Use nouns instead of verbs in endpoint paths. For example,
|
||||||
|
`POST /api/v1/tasks` instead of `POST /api/v1/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
|
||||||
|
for `/projects` and `/tasks` instead of `/projects/:id1/tasks/:id2`. Use
|
||||||
|
filters to extract necessary information like `/tasks/:id2?project=:id1`.
|
||||||
|
In some cases it is useful to get all `tasks`. If the structure is
|
||||||
|
hierarchical, it cannot be done easily. Also you have to know both `:id1`
|
||||||
|
and `:id2` to get information about the task.
|
||||||
|
_Note: for now we accept `GET /tasks/:id2/jobs` but it should be replaced
|
||||||
|
by `/jobs?task=:id2` in the future_.
|
||||||
|
- Handle errors gracefully and return standard error codes (e.g. `201`, `400`)
|
||||||
|
- 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.
|
||||||
|
|
||||||
|
## Links
|
||||||
|
- [Best practices for REST API design](https://stackoverflow.blog/2020/03/02/best-practices-for-rest-api-design/)
|
||||||
|
- [Flat vs. nested resources](https://stackoverflow.com/questions/20951419/what-are-best-practices-for-rest-nested-resources)
|
||||||
@ -0,0 +1,31 @@
|
|||||||
|
// Copyright (C) 2020 Intel Corporation
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
/// <reference types="cypress" />
|
||||||
|
|
||||||
|
import { taskName, labelName } from '../../support/const';
|
||||||
|
|
||||||
|
context('Draw a point shape, specify one point', () => {
|
||||||
|
const issueId = '2306';
|
||||||
|
const createPointsShape = {
|
||||||
|
type: 'Shape',
|
||||||
|
labelName: labelName,
|
||||||
|
pointsMap: [{ x: 500, y: 200 }],
|
||||||
|
numberOfPoints: 1,
|
||||||
|
};
|
||||||
|
|
||||||
|
before(() => {
|
||||||
|
cy.openTaskJob(taskName);
|
||||||
|
});
|
||||||
|
|
||||||
|
describe(`Testing case "${issueId}"`, () => {
|
||||||
|
it('Draw a point shape, specify one point. Drag cursor.', () => {
|
||||||
|
cy.createPoint(createPointsShape);
|
||||||
|
cy.get('.cvat-canvas-container').trigger('mousemove');
|
||||||
|
// Test fail before fix with error:
|
||||||
|
// The following error originated from your application code, not from Cypress.
|
||||||
|
// > Cannot read property 'each' of undefined.
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
@ -0,0 +1,110 @@
|
|||||||
|
// Copyright (C) 2020 Intel Corporation
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
/// <reference types="cypress" />
|
||||||
|
|
||||||
|
context('Wrong attribute is removed in label constructor.', () => {
|
||||||
|
const issueId = '2411';
|
||||||
|
const taskRaw = [
|
||||||
|
{
|
||||||
|
name: "person",
|
||||||
|
color: "#ff6037",
|
||||||
|
attributes: [
|
||||||
|
{
|
||||||
|
name: "lower_body",
|
||||||
|
input_type: "select",
|
||||||
|
mutable: true,
|
||||||
|
values: [
|
||||||
|
"__undefined__",
|
||||||
|
"long",
|
||||||
|
"short",
|
||||||
|
"n/a"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "hair_color",
|
||||||
|
input_type: "select",
|
||||||
|
mutable: true,
|
||||||
|
values: [
|
||||||
|
"__undefined__",
|
||||||
|
"black",
|
||||||
|
"brown",
|
||||||
|
"blond",
|
||||||
|
"grey",
|
||||||
|
"other",
|
||||||
|
"n/a"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "cellphone",
|
||||||
|
input_type: "select",
|
||||||
|
mutable: true,
|
||||||
|
values: [
|
||||||
|
"__undefined__",
|
||||||
|
"yes",
|
||||||
|
"no",
|
||||||
|
"n/a"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
before(() => {
|
||||||
|
cy.visit('auth/login');
|
||||||
|
cy.login();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe(`Testing issue "${issueId}"`, () => {
|
||||||
|
it('Open the create task page.', () => {
|
||||||
|
cy.get('#cvat-create-task-button').click({ force: true });
|
||||||
|
});
|
||||||
|
it('Go to Raw labels editor. Insert values.', () => {
|
||||||
|
cy.get('[role="tab"]').contains('Raw').click();
|
||||||
|
cy.get('#labels').clear().type(JSON.stringify(taskRaw), { parseSpecialCharSequences: false });
|
||||||
|
cy.contains('Done').click();
|
||||||
|
});
|
||||||
|
it('Go to constructor tab. The label "person" appeared there.', () => {
|
||||||
|
cy.get('[role="tab"]').contains('Constructor').click();
|
||||||
|
cy.get('.cvat-constructor-viewer-item').should('have.text', 'person').within(() => {
|
||||||
|
cy.get('i[aria-label="icon: edit"]').click();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
it('Remove the average attribute "hair_color". It has been deleted.', () => {
|
||||||
|
cy.get('.cvat-label-constructor-updater').within(() => {
|
||||||
|
cy.get('.ant-row-flex-space-between').eq(1).within(() => {
|
||||||
|
cy.get('[placeholder="Name"]').invoke('val').then(placeholderNameValue => {
|
||||||
|
expect(placeholderNameValue).to.be.equal('hair_color');
|
||||||
|
});
|
||||||
|
cy.get('.cvat-delete-attribute-button').click();
|
||||||
|
});
|
||||||
|
cy.get('.ant-row-flex-space-between').eq(0).within(() => {
|
||||||
|
cy.get('[placeholder="Name"]').invoke('val').then(placeholderNameValue => {
|
||||||
|
expect(placeholderNameValue).to.be.equal('cellphone');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
cy.get('.ant-row-flex-space-between').eq(1).within(() => {
|
||||||
|
cy.get('[placeholder="Name"]').invoke('val').then(placeholderNameValue => {
|
||||||
|
expect(placeholderNameValue).to.be.equal('lower_body');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
it('Remove the latest attribute "lower_body". It has been deleted.', () => {
|
||||||
|
cy.get('.cvat-label-constructor-updater').within(() => {
|
||||||
|
cy.get('.ant-row-flex-space-between').eq(1).within(() => {
|
||||||
|
cy.get('[placeholder="Name"]').invoke('val').then(placeholderNameValue => {
|
||||||
|
expect(placeholderNameValue).to.be.equal('lower_body');
|
||||||
|
});
|
||||||
|
cy.get('.cvat-delete-attribute-button').click();
|
||||||
|
});
|
||||||
|
cy.get('.ant-row-flex-space-between').eq(0).within(() => {
|
||||||
|
cy.get('[placeholder="Name"]').invoke('val').then(placeholderNameValue => {
|
||||||
|
expect(placeholderNameValue).to.be.equal('cellphone');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
Loading…
Reference in New Issue