almendar / WarszawScala-slick-day

Skeleton of a project for Warsaw Scala User Group meetup
3 stars 3 forks source link

Zmiana API do pobierania kategorii #3

Closed rkrzewski closed 9 years ago

rkrzewski commented 9 years ago

Obecnie zdefiniowane API dla kategorii jest bardzo nieefektywne jeśli chodzi o wyświetlanie drzewka. Znacznie bardziej przyjazne było by coś takiego:

Pobieranie wszystkich kategorii głównych (nie posiadających rodzica)

GET /rest/categories
[
  {
    "id": 1,
    "name" : "Jeden",
    "hasChildren" : true
  }
]

Pobieranie podkategorii:

GET /rest/categories/1
[
  {
    "id": 2,
    "name" : "Dwa",
    "hasChildren" : true
  }, {
    "id" : 5,
    "name" : "Pięć",
    "hasChildren: : false
  }
]

@almendar, czy mógłbyś przerobić backend w taki sposób?

almendar commented 9 years ago

Zrobiłem,

zobacz czy o to Ci chodziło @rkrzewski .

Dorzuciłem parentId:Option[Long] pole do Category. Będzie potrzebne przy tworzeniu kategorii chyba.

rkrzewski commented 9 years ago

Oj, faktycznie. Jeśli uwzględnimy dodawanie, modyfikację i kasowanie kategorii to sprawa się komplikuje. Specem od tworzenia API nie jestem, więc propozycja poniżej jest oczywiście do dalszej dyskusji. Notację wymyśliłem na poczekaniu, ale wierzę się się rozczytasz :)

- fetch a specific category
GET /rest/categories/1
>>> 200, {
  "id" : 1,
  "parentId" : null 
  "name" : "name",
  "hasChildren" : true
}

- fetch subcategories
GET /rest/categories/1/children
>>> 200, [{
  "id" : 2,
  "parentId" : 1 
  "name" : "name",
  "hasChildren" : true
}, {
  "id" : 5,
  "parentId" : 1 
  "name" : "name",
  "hasChildren" : false
}]

- update a category
PUT /rest/categories/1
<<< {
  "id" : 1, // ignored, path parameter (1 here) used instead
  "parentId" : null // updating id moves category subtree
  "name" : "name",
  "hasChildren" : true
}
>>> 204

- delete a category subtree
DELETE /rest/categories/1
>>> 204

- create a new subcategory
POST /rest/categories/1/children
<<< {
  "id" : 1, // ignored, new id generated
  "parentId" : null // ignored, path param (1 here) used instead
  "name" : "name",
  "hasChildren" : true // igored
}
>>> 201, Location: /categories/`new id`

- fetch all root categories
GET /rest/categories/children
>>> 200, [{
  "id" : 1,
  "parentId" : null 
  "name" : "root 1",
  "hasChildren" : true
}]

- create a new root category
POST /rest/categories/children
<<< {
  "id" : 1, // ignored, new id generated
  "parentId" : null // ignored, path param (null here) used instead
  "name" : "name",
  "hasChildren" : true // igored
}
>>> 201, Location: /categories/`new id`

ten URL /rest/categories/children jest trochę dziwaczny ale nie mam na razie pomysłu jak to inaczej ugryźć.

rkrzewski commented 9 years ago

Po chwili zastanowienia (po napisaniu, a przed wysłaniem poprzedniego komentarza) wymyśliłem inną wersję, która ma trochę prostsze URLe (nie wymaga sufiksu children) ale jest moim zdaniem gorsza, ponieważ przy fetchowaniu danych wypycha do klienta więcej informacji niż jest faktycznie potrzebne do "leniwego" renderowania drzewka. Poza tym narusza standardową konwencję REST, że GET /rest/categories powinien zwrócić wszystkie kategorie w systemie, a nie top levelowe. W poprzedniej wersji GET /rest/categories może po prostu odpowiedzieć 405 co jest bardziej poprawnym zachowaniem niż zwrócenie niestndardowej (i potencjalnie mylącej) treści.

Z punktu widzenia Twojego miniprojektu to jednak jest dzielenie włosa na czworo :) Wybierz co Ci bardziej pasuje, albo zaproponuj jeszcze coś innego.

- fetch all root categories
GET /rest/categories
>>> 200, [{
  "id" : 1,
  "parentId" : null 
  "name" : "root 1",
  "hasChildren" : true,
  "children" : [{
    "id" : 2,
    "parentId" : 1 
    "name" : "name",
    "hasChildren" : true
  }, {
    "id" : 5,
    "parentId" : 1 
    "name" : "name",
    "hasChildren" : false
  }]
}]

- fetch a specific category
GET /rest/categories/2
>>> 200, {
  "id" : 1,
  "parentId" : null 
  "name" : "cat 2",
  "hasChildren" : true,
  "children" : [{
    "id" : 3,
    "parentId" : 1 
    "name" : "cat 3",
    "hasChildren" : false
  }, {
    "id" : 4,
    "parentId" : 1 
    "name" : "cat 4",
    "hasChildren" : false
  }]
}

- update a category
PUT /rest/categories/1
<<< {
  "id" : 1, // ignored, path parameter (1 here) used instead
  "parentId" : null // updating id moves category subtree
  "name" : "name",
  "hasChildren" : true
}
>>> 204 / 400 if updating parentId would create a circular dependency

- delete a category subtree
DELETE /rest/categories/1
>>> 204

- create a new root category
POST /rest/categories
<<< {
  "id" : 1, // ignored, new id generated
  "parentId" : null // ignored, path param (null here) used instead
  "name" : "name",
  "hasChildren" : true // igored
}
>>> 201, Location: /rest/categories/`new id`

- create a new subcategory
POST /rest/categories/1
<<< {
  "id" : 1, // ignored, new id generated
  "parentId" : null // ignored, path param (1 here) used instead
  "name" : "name",
  "hasChildren" : true // igored
}
>>> 201, Location: /rest/categories/`new id`
almendar commented 9 years ago

Myślę, że sporo się pokrywa z tego co jest z tym o czym piszesz. Zróbmy to najprościej jak się da, żeby specjalnie się nie narobić i tak nikt do tego nie zajrzy ;) Będziemy mało REST-owi, ale who cares!

create a new root or child category

POST /rest/categories
<<< {
  "name" : "name",
  "hasChildren" : true // igored
}
>>> 201, Location: /rest/categories/`new id`

Wystarczy, że podasz te dwie wartości. Jeżeli chcesz podpiąć się pod inną kategorię po prostu dodaj do jsona: parentId: 2 W zwrotce jest header z Location i wygenerowanym id.

update a category

PUT /rest/categories/1
<<< {
  "name" : "name",
  "hasChildren" : true
}

delete a category subtree

DELETE /rest/categories/1
>>> 204

tutaj usuwamy kategorię wraz z dziećmi. Muszę to dorobić bo póki co w ogóle nie ma usuwania.

fetch all root categories

GET /rest/categories
[{
  "id": 1,
  "name": "Great American Novel",
  "hasChildren": true
}]

Zwróci wszystkie kategorie, które nie mają ojca.

fetch a specific category children

GET /rest/categories/1
[{
  "id": 3,
  "name": "Poor people",
  "parentId": 1,
  "hasChildren": false
}, {
  "id": 2,
  "name": "Rich people",
  "parentId": 1,
  "hasChildren": false
}]
rkrzewski commented 9 years ago

:+1: zamknij zadanie kiedy skończysz implementację API (chyba że, już to zrobiłeś?)

almendar commented 9 years ago

Zrobiłem :)

Szczerze nie wiem czy nie przedobrzyłem z ilością rzeczy do dopisania w bazie danych, no ale niech już będzie jak jest.