BeerPressure
Meeskond ja rollid
- Sigrid Aasma (arendaja)
- Evelin Jõgi (arendaja)
- Martin Kask (projektijuht, arendaja)
- Marko Nõu (arendaja)
Idee
Toidukohtade põhimenüüde ja päevapakkumiste teenus
Soovime pakkuda toidukohtade põhimenüüde ja päevapakkumiste teenust. On olemas mitmeid kodulehti “päevapakkumised”, kus on kirjas toidukohtade päevapraed. Kuid tavaliselt sellistel lehtedel ei ole masinloetavaid andmeid ehk siis varianti, et saad saata API pihta mingi kuupäeva koos päevapraadidega andmete uuendamiseks ja saad sama API käest küsida tänaseid päevapraade. Andmeid võiks saada küsida restorani, kuupäeva, asukoha või otsisõna/otsisõnaosa põhiselt. Selline teenus lihtsustaks ka toidukohtade päevapakkumiste haldamist, kuna praegu peavad teenuse pakkujad vastavat informatsiooni uuendama mitmes erinevas kohas - nii erinevates portaalides, kui ka enda veebileheküljel. Samuti oleks toidukohtade kogu menüü haldamine ühes kohas.
Esialgne prototüüp
Esialgne prototüüp: navigeeritav versioon vol.1
Prototüübi muutus projekti käigus
Prototüüp: navigeeritav versioon vol.2
Kasutatav arendustehnoloogia
Veebiteenuse loomisel kasutame ASP.NET Core tehnoloogiat ja kursusel õpitud arendusmustreid.
Klientrakenduse loomisel kasutame Node.js + React'i.
Veebiteenuse analüüs
Pakume toidukohtade põhimenüüde ja päevapakkumiste teenust, kuhu on kokku koondatud erinevate toidukohtade menüüd üle Eesti. Põhimenüü alla kuuluvad pikaajalised menüüd, mis kehtivad püsivalt, hooajaliselt või mingil konkreetsel nädalapäeval. Päevapakkumiste alla kuuluvad konkreetse kuupäevaga seotud päevapakkumised. Päevapakkumiste korral kuvatakse lisainfona, kas pakkumine kehtib mingis konkreetses ajavahemikus või kogu toidukoha lahtioleku aja.
Eesmärk on koguda ühte kohta kokku erinevate toidukohtade menüüd ja päevapakkumised, et kliendid saaksid mugavalt ja kiiresti oma valikuid teha. Kliendid käivad enamasti päevapakkumisi söömas lõuna ajal ja selleks ettenähtud aeg on piiratud. Sellepärast on oluline, et klient saaks teha oma valiku võimalikult kiiresti. Tänu loodavale veebiteenusele ei pea kliendid käima erinevates portaalides, et tutvuda erinevate toidukohtade pakkumistega. Kuna teenust on mugav kasutada ja klient saab oma valiku kiiresti tehtud, siis tõenäoliselt teeb klient valiku selles keskkonnas olevate toiduasutuste seast. Lisaks võimaldab teenus toiduasutustel paika panna oma pikaajalisem põhimenüü ning seda rakenduses ja võimalusel ka söögikohas kuvada. Sellepärast on ka toidukohtade huvi antud keskkonnas üleval olla ja meiega oma andmeid jagada, sest see suurendab nende klientuuri ning muudab oma menüü haldamise kergemaks.
Veebiteenuse loomisel lähtume eelkõige sellest, et kliendil oleks teenust mugav kasutada ja ta saaks oma valiku kiiresti tehtud. Arvestame, et kliendid on erinevad. Mõni eelistab mõnda kindlat toidukohta, sel juhul saab ta valida oma soovitud kohad ja võrrelda nende kohtade tänase päeva pakkumisi ning menüüsid. Mõnele inimesele on oluline pakutav söök ja ta eelistab pakkumist otsida prae nimetuses oleva sõna või sõnaosa järgi ning lähtuda seejärel valiku tegemisel toidukoha keskmisest hindest või toidu hinnast. Mõne kliendi jaoks on oluline hind ja selleks, et oma valikut teha sobivas hinnavahemikus on tal võimalik sisestada maksimum hinna piirang. Kindlasti on antud teenuse juures kõikide klientide jaoks oluline päevapakkumiste asukohapõhine eristatus. Kliendid, kes soovivad oma konto luua, võivad oma eelistatud valikud meelde jätta ja järgmisel korral kuvatakse neid talle automaatselt.
Teenuse must-have funktsionaalsus tagab teenuse toimimise ja põhifunktsioonid. Teenuse nice-to-have funktsionaalsus annab rakendusele lisandväärtust.
Arvestades pakutava teenusega, siis näeme, et aktiivseim teenuse kasutamine võib olla tööpäevadel vahemikus 11.00-15.00, kui kliendid tutvuvad päevapakkumistega. Teenus peab tekkivale koormusele vastu pidama.
Teenuse pakkumine peab olema turvaline. Selle tagamiseks tuleb sisestatavaid andmeid valideerida ja andmebaasi sisestavate andmete mahtu piirata. Toidukohtade sisestatud andmed peavad olema kaitstud, et toitude koostised ei saaks avalikuks. Põhjendatud juhtudel on võimalik kasutajaid lukku panna.
Kasutajad
- Admin - lehe administraator, kellel on õigus lehte, kasutajaid ja nende õiguseid hallata. Tema huvi on, et teenus toimiks tõrgeteta.
- Toiduasutus (tasuta kasutaja) - saab hoida lehel ühte põhimenüüd ja päevapakkumisi.
- Toiduasutus (tellimusega kasutaja) - saab hoida lehel piiramatu arv põhimenüüsid ja päevapakkumisi.
Toiduasutused on teenusest huvitatud, et nende menüüde haldamine oleks kerge ja mugav ning nad saaksid oma klientide arvu suurendada.
- Külastaja - klient, kes valib söögikohta. Tema on huvitatud eelkõige sellest, et teenust saaks kasutada mugavalt ja kiirelt ning toidukohtade valik oleks võimalikult suur.
Rakenduse must-have funktsionaalsus
- Kasutajad saavad sisse logida
- Kasutajatel on erinevad rollid: admin, toidukoht (tasuta ja tasulise tellimusega), klient
- Kasutajate arvepidamine kasutajate lõikes:
- sisselogimise kuupäev, kellaaeg
- pakkumiste lisamine
- pakkumiste muutmine
- pakkumiste kustutamine
- õiguste kehtimise periood (tasuta ja tellimusega toidukoht)
- pakkumistele klikkimise arv
- Kasutajaid on võimalik hallata:
- kasutajale õiguste andmine (menüüde arvu suurendamine, tellimusega toidukohal), rolli määramine
- Kasutajat on võimalik piirata: õiguseid piirata (piiratud menüüde arv, tasuta liitunud toidukohal) ja lukku panna
- Päevapakkumisi/menüüsid saab küsida:
- toidukoha järgi,
- kuupäeva põhiselt (ainult päevapakkumisi),
- asukoha põhiselt,
- toidu nimetuses esineva otsisõna/otsisõna osa põhiselt,
- hinna põhiselt (max hinna piirang).
- Klientrakenduses realiseeritakse CRUD meetodid (menüüsid/päevapakkumisi saab sisestada, lugeda, uuendada, kustutada)
- Toidukohale saab anda hindeid ja kuvatakse keskmist hinnet.
- Toidule saab anda hindeid ja kuvatakse keskmist hinnet.
Nice-to-have funktsionaalsus
- Info selle kohta, kas päevapakkumine on otsas
- Päevapakkumisi/menüüsid saab kommenteerida ja neid kommentaare kuvatakse avalikult.
- Söögikohtade kodulehtedelt otsimise script, mis genereerib juba olemasolevate kodulehtede baasil päevamenüüsid.
- Toiduvaliku piiramine allergeenide/vegan/jms. koostisosade põhiselt.
- Aruandlused ja võrdlused toidukohtadele, et võrrelda end konkurentidega.
- Teenus soovitab toidukohtadele nende varasemate päevapraadide populaarsuse alusel uue nädala menüüd.
- Erinevad toidukohtade/toitude pingeread klientidele, et anda valikuteks ideid.
- Soovitused klientidele, mida teised kliendid sarnaste eelistustega on veel valinud.
Andmebaasi ülesehitus - projekti algus
Andmebaasi ülesehitus - projekti lõpp
Projekti repository
https://github.com/sikumiku/TaisKohtApi
Zip fail saadaval: (master branchi viimane commit esitamise ajal) https://github.com/sikumiku/TaisKohtApi/archive/61438e187a1a972410c8a3bc6274d69489d00aa6.zip
API v1 dokumentatsioon
Security
POST /api/account/register
Params: RegisterViewModel registerViewModel
Returns: token
POST /api/account/login
Params: LoginViewModel model
Returns: token
POST /api/account/logout
Params: -
Returns: -
Accounts
GET /api/v1/accounts/getAllUsersInRole
Params: string role
Returns: List<UserDTO>
GET /api/v1/accounts/{id}
Params: string id
Returns: UserDTO
PUT /api/v1/accounts/{id}
Params: string id, UpdateUserDTO userDTO
Returns: -
POST /api/v1/accounts/addRole
Params: string role
Returns: -
POST /api/v1/accounts/addRoleToUser
Params: string role, string userId
Returns: UserDTO
DELETE /api/v1/accounts/deactivate/{id}
Params: string id
Returns: -
Promotions
GET /api/v1/promotions
Params: -
Returns: List<PromotionDTO>
GET /api/v1/promotions/{id}
Params: int id
Returns: PromotionDTO
POST /api/v1/promotions
Params: PromotionDTO promotionDTO
Returns: PromotionDTO newPromotion
PUT /api/v1/promotions/{id}
Params: int id, PromotionDTO promotionDTO
Returns: PromotionDTO updatedPromotion
DELETE /api/v1/promotions/{id}
Params: int id
Returns: -
Restaurants
GET /api/v1/restaurants
Params: -
Returns: List<SimpleRestaurantDTO>
GET /api/v1/restaurants/search?name=th
Params: string name
Returns: List<SimpleRestaurantDTO> result
GET /api/v1/restaurants/top
Params: int amount
Returns: List<SimpleRestaurantDTO> result
GET /api/v1/restaurants/{id}
Params: int id
Returns: RestaurantDTO r
POST /api/v1/restaurants/addUserToRestaurant
Params: int id, string userId
Returns: -
POST /api/v1/restaurants
Params: PostRestaurantDTO restaurantDTO
Returns: RestaurantDTO newRestaurant
PUT /api/v1/restaurants/{id}
Params: int id, PostRestaurantDTO restaurantDTO
Returns: PostRestaurantDTO updatedRestaurantDTO
DELETE /api/v1/restaurants/{id}
Params: int id
Returns: -
Menus
GET /api/v1/menus
Params: -
Returns: List<MenuDTO>
GET /api/v1/menus/{id}
Params: int id
Returns: MenuDTO menuDTO
POST /api/v1/menus
Params: PostMenuDTO menuDTO
Returns: MenuDTO newMenu
PUT /api/v1/menus/{id}
Params: int id, PostMenuDTO menuDTO
Returns: MenuDTO updatedMenu
PUT api/v1/Menus/{id}/Dishes
Params: int menuId, int[] dishIds
Returns: -
DELETE /api/v1/menus/{id}
Params: int id
Returns: -
Dishes
GET /api/v1/dishes
Params: -
Returns: List<DishDTO>
GET /api/v1/dishes/daily
Params: bool vegan, bool glutenFree, bool lactoseFree
Returns: List<SimpleDishDTO> result
GET: api/v1/dishes/search?title=th&priceLimit=null
Params: string title, decimal? priceLimit
Returns: List<DishDTO> result
GET /api/v1/dishes/top
Params: int amount
Returns: List<DishDTO> result
GET /api/v1/dishes/{id}
Params: int id
Returns: DishDTO dishDTO
POST /api/v1/dishes
Params: PostDishDTO dishDTO
Returns: DishDTO newDish
PUT /api/v1/dishes/{id}
Params: int id, PostDishDTO dishDTO
Returns: DishDTO updatedDish
PUT api/v1/Dishes/{id}/Ingredients
Params: int dishId, PostIngredientForDishDTO[] ingredients
Returns: -
DELETE /api/v1/dishes/{id}
Params: int id
Returns: -
Ingredients
GET /api/v1/ingredients
Params: -
Returns: List<IngredientDTO>
GET /api/v1/ingredients/{id}
Params: int id
Returns: IngredientDTO i
POST /api/v1/ingredients
Params: PostIngredientDTO ingredientDTO
Returns: IngredientDTO newIngredient
PUT /api/v1/ingredients/{id}
Params: int id, PostIngredientDTO ingredientDTO
Returns: IngredientDTO updatedIngredient
DELETE /api/v1/ingredients/{id}
Params: int id
Returns: -
Ratings
GET /api/v1/ratings
Params: -
Returns: List<RatingLogDTO>
GET /api/v1/ratings/{id}
Params: int id
Returns: RatingLogDTO dto
POST /api/v1/ratings
Params: RatingLogForEntityDTO ratingDTO
Returns: RatingLogDTO newRating
PUT api/v1/ratings/{id}
Params: int id, RatingLogForEntityDTO ratingDTO
Returns: RatingLogDTO updatedRating
DELETE api/v1/ratings/{id}
Params: int id
Returns: -
RequestLog
GET: api/v1/usageData/{userId}
Params: string userId
Returns: List<RequestLogDTO>
Lõpptoote kasutusjuhend
- Kloonida kohalikku arvutisse repository aadressilt https://github.com/sikumiku/TaisKohtApi
- Installida NodeJS
npm install webpack -g
npm install -cli -g
- Frontend rakenduse kasutamiseks valida Startup meetoditest IIS Express, veebiteenuse ja Swaggeri kasutamiseks käivitada Api Swagger
- Esimese admin kasutaja loomiseks tuleb kasutaja registreerida admin@gmail.com e-mailiga
XML/XSD/XSLT
XML
<?xml version="1.0" encoding="utf-8" ?>
<?xml-stylesheet type="text/xsl" href="XMLFileXML.xslt"?>
<restaurants>
<restaurant id="1" url="https://kuuspelmeeni.ee" contactnumber="507783342" email="kuuspelmeeni@gmail.com">
<name><![CDATA[Kuus Pelmeeni]]></name>
<menus>
<menu id="1" repetitioninterval="30">
<name><![CDATA[Päevamenüü]]></name>
<activefrom>2018-05-27T12:00:00</activefrom>
<activeto>2018-05-28T12:00:00</activeto>
<dishes>
<dish id="1" vegan="false" lactosefree="false" glutenfree="false" daily="true" kcal="550" dailyprice="4">
<title><![CDATA[8 pelmeeni]]></title>
<description><![CDATA[8 pelmeeni 6 asemel serveeritud tervisliku koguse hapukoore ja tilliga]]></description>
<availablefrom>2018-05-27T12:00:00</availablefrom>
<availableto>2018-05-28T12:00:00</availableto>
<ingredients>
<ingredient id="1" amountunit="g">
<name>Sealiha</name>
<description>Rakvere sealiha</description>
</ingredient>
<ingredient id="2" amountunit="g">
<name>Hapukoor</name>
<description>Alma hapukoor</description>
</ingredient>
</ingredients>
<rating ratingvalue="9">
<comments>
<comment>
<commenttext><![CDATA[Parim päevaroog viimasel ajal.]]></commenttext>
<username>peeterpakiraam66</username>
<userid>3a9dd653-30fb-40ab-97b1-33cb82d336eb</userid>
</comment>
</comments>
</rating>
<promotion id="1" type="visual" classname="bold-red-border-2px">
<name>2px red border</name>
<description><![CDATA[Displays a 2px wide red border around the component that is promoted.]]></description>
<validto>2018-06-25T12:00:00</validto>
</promotion>
</dish>
<dish id="2" vegan="true" lactosefree="true" glutenfree="true" daily="true" kcal="320" price="4">
<title><![CDATA[Kurgisalat]]></title>
<description><![CDATA[Erinevatel viisidel töödeldud kurk serveeritud koos jõhvikatega. Tervislik amps.]]></description>
<availablefrom>2018-05-27T12:00:00</availablefrom>
<availableto>2018-05-28T12:00:00</availableto>
<ingredients/>
<rating ratingvalue="4">
<comments>
<comment>
<commenttext><![CDATA[Jäin veel väga näljaseks peale selle söömist.]]></commenttext>
<username>ingupingu22</username>
<userid>45b09f05-33fd-4041-995e-ecdb7ec61939</userid>
</comment>
</comments>
</rating>
</dish>
</dishes>
</menu>
<menu id="2">
<name><![CDATA[Põhimenüü]]></name>
<dishes>
<dish id="4" vegan="false" lactosefree="false" glutenfree="false" daily="false" kcal="550" price="5">
<title><![CDATA[6 pelmeeni]]></title>
<description><![CDATA[6 pelmeeni serveeritud tervisliku koguse hapukoore ja tilliga]]></description>
<ingredients>
<ingredient id="1" amountunit="g">
<name>Sealiha</name>
<description>Rakvere sealiha</description>
</ingredient>
</ingredients>
<rating ratingvalue="9.5">
<comments>
<comment>
<commenttext><![CDATA[Käin seda iga päev söömas.]]></commenttext>
<username>ingupingu22</username>
<userid>45b09f05-33fd-4041-995e-ecdb7ec61939</userid>
</comment>
<comment>
<commenttext><![CDATA[Parim toit selles restoranis.]]></commenttext>
<username>peeterpakiraam66</username>
<userid>3a9dd653-30fb-40ab-97b1-33cb82d336eb</userid>
</comment>
</comments>
</rating>
</dish>
<dish id="5" vegan="false" lactosefree="false" glutenfree="true" daily="false" kcal="460" price="3.8">
<title><![CDATA[Kartulisalat]]></title>
<description><![CDATA[Sinki sisaldav traditsiooniline kartulisalat.]]></description>
<ingredients/>
</dish>
<dish id="6" vegan="false" lactosefree="false" glutenfree="false" daily="false" kcal="610" price="3.9">
<title><![CDATA[Šokolaadikook]]></title>
<description><![CDATA[Ahvatlev glasuuritud šokolaadikook.]]></description>
<ingredients/>
<rating ratingvalue="7.8">
<comments>
<comment>
<commenttext><![CDATA[Šokolaadikooki on keeruline metsa keerata.]]></commenttext>
<username>mutikas3</username>
<userid>3a29b94a-3f17-469d-a44d-f7d85e98dd3e</userid>
</comment>
</comments>
</rating>
</dish>
</dishes>
</menu>
</menus>
<dishes/>
<address id="1" country="Eesti">
<addressfirstline>Tatari 12</addressfirstline>
<locality>Tallinn</locality>
<postcode>10132</postcode>
<region>Harjumaa</region>
</address>
<promotion/>
<rating ratingvalue="7.5">
<comments>
<comment>
<commenttext><![CDATA[Parim restoran üldse!]]></commenttext>
<username>peeterpakiraam66</username>
<userid>3a9dd653-30fb-40ab-97b1-33cb82d336eb</userid>
</comment>
<comment>
<commenttext><![CDATA[Ma sain ainult 5 pelmeeni. Ootasin kuute.]]></commenttext>
<username>kyllikekallike1</username>
<userid>6af271dd-0e11-4ae6-98be-111762664318</userid>
</comment>
</comments>
</rating>
</restaurant>
<restaurant id="2" url="https://kolmkokka.ee" contactnumber="6522351" email="kolmkokka@gmail.com">
<name><![CDATA[Kolm kokka]]></name>
<menus/>
<dishes>
<dish id="7" vegan="false" lactosefree="true" glutenfree="false" daily="false" kcal="470" price="14">
<title><![CDATA[Ahjulõhe juurikatega]]></title>
<description><![CDATA[Ahjulõhe serveeritud püreestatud porgandite ja lillkapsaga.]]></description>
<ingredients>
<ingredient id="11" amountunit="tk" amount="1">
<name>Ahjulõhe</name>
<description><![CDATA[Värske ahjulõhe]]></description>
</ingredient>
<ingredient id="12" amountunit="g">
<name>Püreestatud porgand</name>
<description><![CDATA[Eesti porgand, mis on kergelt püreestatud]]></description>
</ingredient>
<ingredient id="13" amountunit="g">
<name>Püreestatud lillkapsas</name>
<description><![CDATA[Eesti lillkapsas, mis on kergelt püreestatud ja maitsestatud]]></description>
</ingredient>
</ingredients>
<rating ratingvalue="8.9">
<comments>
<comment>
<commenttext><![CDATA[Kindlasti minu üks lemmikuid.]]></commenttext>
<username>ingupingu22</username>
<userid>45b09f05-33fd-4041-995e-ecdb7ec61939</userid>
</comment>
<comment>
<commenttext><![CDATA[Pole midagi halba öelda.]]></commenttext>
<username>tiiuviiu</username>
<userid>0730a7b7-3a9c-4e26-962f-da9a8daeab8a</userid>
</comment>
</comments>
</rating>
</dish>
<dish id="8" vegan="true" lactosefree="true" glutenfree="true" daily="true" dailyprice="5">
<title><![CDATA[Ingverisupp]]></title>
<description><![CDATA[Ingveriga vürtsistatud ingverisupp, mis viib keele alla.]]></description>
<availablefrom>2018-05-25T12:00:00</availablefrom>
<availableto>2018-06-25T12:00:00</availableto>
<ingredients>
<ingredient id="14" amountunit="g" amount="20">
<name>Ingver</name>
<description><![CDATA[Tükeldatud ingver]]></description>
</ingredient>
</ingredients>
<rating ratingvalue="6">
<comments>
<comment>
<commenttext><![CDATA[Liiga palju ingverit.]]></commenttext>
<username>kyllikekallike1</username>
<userid>6af271dd-0e11-4ae6-98be-111762664318</userid>
</comment>
</comments>
</rating>
</dish>
<dish id="9" vegan="false" lactosefree="false" glutenfree="true" daily="false" kcal="340" price="4.5">
<title><![CDATA[Apelsinijäätis]]></title>
<description><![CDATA[Apelsinimaitseline jäätis serveeritud riivitud apelsinikoorega.]]></description>
<ingredients/>
<rating ratingvalue="8">
<comments>
<comment>
<commenttext><![CDATA[Väga huvitav magustoit, prooviks veel.]]></commenttext>
<username>mutikas3</username>
<userid>3a29b94a-3f17-469d-a44d-f7d85e98dd3e</userid>
</comment>
<comment>
<commenttext><![CDATA[Üks mu lemmikuid magustoite.]]></commenttext>
<username>ingupingu22</username>
<userid>45b09f05-33fd-4041-995e-ecdb7ec61939</userid>
</comment>
</comments>
</rating>
</dish>
</dishes>
<address id="2" country="Eesti">
<addressfirstline>Laia 12</addressfirstline>
<locality>Tallinn</locality>
<postcode>13022</postcode>
<region>Harjumaa</region>
</address>
<promotion/>
<rating ratingvalue="8.1">
<comments>
<comment>
<commenttext><![CDATA[Hinnad on natuke liiga kallid minu jaoks, aga toit om maitsev.]]></commenttext>
<username>peeterpakiraam66</username>
<userid>3a9dd653-30fb-40ab-97b1-33cb82d336eb</userid>
</comment>
<comment>
<commenttext><![CDATA[Ma kindlasti telliks kala uuesti. Väga mahlakas oli.]]></commenttext>
<username>kyllikekallike1</username>
<userid>6af271dd-0e11-4ae6-98be-111762664318</userid>
</comment>
<comment>
<commenttext><![CDATA[Asukoht oli päris tore. Miljöö ka.]]></commenttext>
<username>mutikas3</username>
<userid>3a29b94a-3f17-469d-a44d-f7d85e98dd3e</userid>
</comment>
</comments>
</rating>
</restaurant>
</restaurants>
XSD
<?xml version="1.0" encoding="utf-8"?>
<xs:schema attributeFormDefault="unqualified" elementFormDefault="qualified" xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="restaurants">
<xs:complexType>
<xs:sequence minOccurs="0" maxOccurs="unbounded">
<xs:element name="restaurant">
<xs:complexType>
<xs:sequence>
<xs:element name="name" type="xs:string" />
<xs:element name="menus">
<xs:complexType>
<xs:sequence minOccurs="0" maxOccurs="unbounded">
<xs:element name="menu">
<xs:complexType>
<xs:sequence>
<xs:element name="name" type="xs:string" />
<xs:element name="activefrom" type="xs:dateTime" minOccurs="0" />
<xs:element name="activeto" type="xs:dateTime" minOccurs="0" />
<xs:element name="dishes">
<xs:complexType>
<xs:sequence minOccurs="0" maxOccurs="unbounded">
<xs:element name="dish">
<xs:complexType>
<xs:sequence>
<xs:element name="title" type="xs:string" />
<xs:element name="description" type="xs:string" />
<xs:element name="availablefrom" type="xs:dateTime" minOccurs="0" />
<xs:element name="availableto" type="xs:dateTime" minOccurs="0" />
<xs:element name="servetime" type="xs:dateTime" minOccurs="0" />
<xs:element name="ingredients">
<xs:complexType>
<xs:sequence minOccurs="0" maxOccurs="unbounded">
<xs:element name="ingredient">
<xs:complexType>
<xs:sequence>
<xs:element name="name" type="xs:string" />
<xs:element name="description" type="xs:string" />
</xs:sequence>
<xs:attribute name="id" type="xs:int" use="required" />
<xs:attribute name="amountunit" type="xs:string" use="required" />
<xs:attribute name="amount" type="xs:decimal" use="optional" />
</xs:complexType>
</xs:element>
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element name="rating" minOccurs="0">
<xs:complexType>
<xs:sequence minOccurs="0" maxOccurs="unbounded">
<xs:element name="comments">
<xs:complexType>
<xs:sequence minOccurs="0" maxOccurs="unbounded">
<xs:element name="comment">
<xs:complexType>
<xs:sequence>
<xs:element name="commenttext" type="xs:string" />
<xs:element name="username" type="xs:string" />
<xs:element name="userid" type="xs:string" />
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:sequence>
<xs:attribute name="ratingvalue" type="xs:decimal" use="required" />
</xs:complexType>
</xs:element>
<xs:element name="promotion" minOccurs="0">
<xs:complexType>
<xs:sequence>
<xs:element name="name" type="xs:string" />
<xs:element name="description" type="xs:string" />
<xs:element name="validto" type="xs:dateTime" minOccurs="0" />
</xs:sequence>
<xs:attribute name="id" type="xs:int" use="required" />
<xs:attribute name="type" type="xs:string" use="required" />
<xs:attribute name="classname" type="xs:string" use="required" />
</xs:complexType>
</xs:element>
</xs:sequence>
<xs:attribute name="id" type="xs:int" use="required" />
<xs:attribute name="vegan" type="xs:boolean" use="optional" />
<xs:attribute name="lactosefree" type="xs:boolean" use="optional" />
<xs:attribute name="glutenfree" type="xs:boolean" use="optional" />
<xs:attribute name="daily" type="xs:boolean" use="optional" />
<xs:attribute name="kcal" type="xs:int" use="optional" />
<xs:attribute name="weightg" type="xs:decimal" use="optional" />
<xs:attribute name="price" type="xs:decimal" use="optional" />
<xs:attribute name="dailyprice" type="xs:decimal" use="optional" />
</xs:complexType>
</xs:element>
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element name="promotion" minOccurs="0">
<xs:complexType>
<xs:sequence>
<xs:element name="name" type="xs:string" />
<xs:element name="description" type="xs:string" />
<xs:element name="validto" type="xs:dateTime" minOccurs="0" />
</xs:sequence>
<xs:attribute name="id" type="xs:int" use="required" />
<xs:attribute name="type" type="xs:string" use="required" />
<xs:attribute name="classname" type="xs:string" use="required" />
</xs:complexType>
</xs:element>
</xs:sequence>
<xs:attribute name="id" type="xs:int" use="required" />
<xs:attribute name="repetitioninterval" type="xs:int" use="optional" />
</xs:complexType>
</xs:element>
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element name="dishes">
<xs:complexType>
<xs:sequence minOccurs="0" maxOccurs="unbounded">
<xs:element name="dish">
<xs:complexType>
<xs:sequence>
<xs:element name="title" type="xs:string" />
<xs:element name="description" type="xs:string" />
<xs:element name="availablefrom" type="xs:dateTime" minOccurs="0" />
<xs:element name="availableto" type="xs:dateTime" minOccurs="0" />
<xs:element name="servetime" type="xs:dateTime" minOccurs="0" />
<xs:element name="ingredients">
<xs:complexType>
<xs:sequence minOccurs="0" maxOccurs="unbounded">
<xs:element name="ingredient">
<xs:complexType>
<xs:sequence>
<xs:element name="name" type="xs:string" />
<xs:element name="description" type="xs:string" />
</xs:sequence>
<xs:attribute name="id" type="xs:int" use="required" />
<xs:attribute name="amountunit" type="xs:string" use="required" />
<xs:attribute name="amount" type="xs:decimal" use="optional" />
</xs:complexType>
</xs:element>
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element name="rating">
<xs:complexType>
<xs:sequence>
<xs:element name="comments">
<xs:complexType>
<xs:sequence minOccurs="0" maxOccurs="unbounded">
<xs:element name="comment">
<xs:complexType>
<xs:sequence>
<xs:element name="commenttext" type="xs:string" />
<xs:element name="username" type="xs:string" />
<xs:element name="userid" type="xs:string" />
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:sequence>
<xs:attribute name="ratingvalue" type="xs:decimal" use="required" />
</xs:complexType>
</xs:element>
<xs:element name="promotion" minOccurs="0">
<xs:complexType>
<xs:sequence>
<xs:element name="name" type="xs:string" />
<xs:element name="description" type="xs:string" />
<xs:element name="validto" type="xs:dateTime" minOccurs="0" />
</xs:sequence>
<xs:attribute name="id" type="xs:int" use="required" />
<xs:attribute name="type" type="xs:string" use="required" />
<xs:attribute name="classname" type="xs:string" use="required" />
</xs:complexType>
</xs:element>
</xs:sequence>
<xs:attribute name="id" type="xs:int" use="required" />
<xs:attribute name="vegan" type="xs:boolean" use="optional" />
<xs:attribute name="lactosefree" type="xs:boolean" use="optional" />
<xs:attribute name="glutenfree" type="xs:boolean" use="optional" />
<xs:attribute name="daily" type="xs:boolean" use="optional" />
<xs:attribute name="kcal" type="xs:int" use="optional" />
<xs:attribute name="weightg" type="xs:decimal" use="optional" />
<xs:attribute name="price" type="xs:decimal" use="optional" />
<xs:attribute name="dailyprice" type="xs:decimal" use="optional" />
</xs:complexType>
</xs:element>
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element name="address">
<xs:complexType>
<xs:sequence>
<xs:element name="addressfirstline" type="xs:string" />
<xs:element name="locality" type="xs:string" />
<xs:element name="postcode" type="xs:string" />
<xs:element name="region" type="xs:string" />
</xs:sequence>
<xs:attribute name="id" type="xs:int" use="required" />
<xs:attribute name="country" type="xs:string" use="required" />
</xs:complexType>
</xs:element>
<xs:element name="promotion" />
<xs:element name="rating" minOccurs="0">
<xs:complexType>
<xs:sequence>
<xs:element name="comments">
<xs:complexType>
<xs:sequence minOccurs="0" maxOccurs="unbounded">
<xs:element name="comment">
<xs:complexType>
<xs:sequence>
<xs:element name="commenttext" type="xs:string" />
<xs:element name="username" type="xs:string" />
<xs:element name="userid" type="xs:string" />
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:sequence>
<xs:attribute name="ratingvalue" type="xs:decimal" use="required" />
</xs:complexType>
</xs:element>
</xs:sequence>
<xs:attribute name="id" type="xs:int" use="required" />
<xs:attribute name="url" type="xs:string" use="required" />
<xs:attribute name="contactnumber" type="xs:string" use="required" />
<xs:attribute name="email" type="xs:string" use="required" />
</xs:complexType>
</xs:element>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:schema>
XSLT(HTML)
<?xml version="1.0" encoding="utf-8" ?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:msxsl="urn:schemas-microsoft-com:xslt" exclude-result-prefixes="msxsl">
<xsl:output method="html" indent="yes"/>
<xsl:template match="/restaurants">
<html>
<head>
<title>Täis Kõht</title>
</head>
<body>
<xsl:for-each select="restaurant">
<h1 style="color:DodgerBlue">
<xsl:value-of select="name"/>
</h1>
<div>
<xsl:value-of select="@url"/>
</div>
<div>
<xsl:text>Kontakt number: </xsl:text>
<xsl:value-of select="@contactnumber"/>
</div>
<div>
<xsl:text>email: </xsl:text>
<xsl:value-of select="@email"/>
</div>
<div>
<xsl:text>Aadress: </xsl:text>
<xsl:value-of select="address/addressfirstline"/>
<xsl:text>, </xsl:text>
<xsl:value-of select="address/locality"/>
<xsl:text>, </xsl:text>
<xsl:value-of select="address/postcode"/>
<xsl:text>, </xsl:text>
<xsl:value-of select="address/@country"/>
</div>
<xsl:choose>
<xsl:when test="menus/menu">
<xsl:for-each select="menus/menu">
<xsl:sort select="name"/>
<h3>
<xsl:value-of select="name"/>
</h3>
<xsl:if test="activefrom != '' and activeto != ''">
<xsl:text> </xsl:text>
<xsl:value-of select="substring(activefrom,1,10)"/>
<xsl:text> kuni </xsl:text>
<xsl:value-of select="substring(activeto,1,10)"/>
</xsl:if>
<ul>
<xsl:for-each select="dishes/dish">
<xsl:sort select="title"/>
<xsl:if test="availablefrom != '' and availableto != ''">
<xsl:value-of select="substring(availablefrom,1,10)"/>
<xsl:text> kuni </xsl:text>
<xsl:value-of select="substring(availableto,1,10)"/>
</xsl:if>
<li>
<span style="font-weight:bold">
<xsl:value-of select="title"/>
</span>
<xsl:text> - </xsl:text>
<xsl:value-of select="description"/>
<xsl:text> </xsl:text>
<span style="font-weight:bold">
<xsl:if test="@glutenfree='true'">
<xsl:text>G </xsl:text>
</xsl:if>
<xsl:if test="@lactosefree='true'">
<xsl:text>L </xsl:text>
</xsl:if>
<xsl:if test="@vegan='true'">
<xsl:text>V </xsl:text>
</xsl:if>
</span>
</li>
<xsl:for-each select="ingredients/ingredient">
<xsl:value-of select="name"/>
<xsl:if test="position()!=last()">
<xsl:text>, </xsl:text>
</xsl:if>
</xsl:for-each>
<p style="font-weight:bold">
<xsl:text>Hind: </xsl:text>
<xsl:choose>
<xsl:when test="@price!=''">
<xsl:value-of select="@price"/>
</xsl:when>
<xsl:otherwise>
<xsl:text>-</xsl:text>
</xsl:otherwise>
</xsl:choose>
<xsl:text> / Päevapakkumise hind: </xsl:text>
<xsl:choose>
<xsl:when test="@dailyprice!=''">
<xsl:value-of select="@dailyprice"/>
</xsl:when>
<xsl:otherwise>
<xsl:text>-</xsl:text>
</xsl:otherwise>
</xsl:choose>
</p>
<div style="background-color:LightGray;">
<xsl:text>Hinnang praele: </xsl:text>
<xsl:choose>
<xsl:when test="rating/@ratingvalue!=''">
<xsl:value-of select="rating/@ratingvalue"/>
</xsl:when>
<xsl:otherwise>
<xsl:text>Ei ole veel hinnatud!</xsl:text>
</xsl:otherwise>
</xsl:choose>
<br/>
<xsl:for-each select="rating/comments/comment">
<xsl:value-of select="username"/>
<xsl:text>: </xsl:text>
<xsl:value-of select="commenttext"/>
<br/>
</xsl:for-each>
</div>
<br/>
</xsl:for-each>
</ul>
</xsl:for-each>
</xsl:when>
<xsl:otherwise>
<ul>
<xsl:for-each select="dishes/dish">
<xsl:sort select="title"/>
<xsl:if test="availablefrom != '' and availableto != ''">
<xsl:value-of select="substring(availablefrom,1,10)"/>
<xsl:text> kuni </xsl:text>
<xsl:value-of select="substring(availableto,1,10)"/>
</xsl:if>
<li>
<span style="font-weight:bold">
<xsl:value-of select="title"/>
</span>
<xsl:text> - </xsl:text>
<xsl:value-of select="description"/>
<xsl:text> </xsl:text>
<span style="font-weight:bold">
<xsl:if test="@glutenfree='true'">
<xsl:text>G </xsl:text>
</xsl:if>
<xsl:if test="@lactosefree='true'">
<xsl:text>L </xsl:text>
</xsl:if>
<xsl:if test="@vegan='true'">
<xsl:text>V </xsl:text>
</xsl:if>
</span>
</li>
<xsl:for-each select="ingredients/ingredient">
<xsl:value-of select="name"/>
<xsl:if test="position()!=last()">
<xsl:text>, </xsl:text>
</xsl:if>
</xsl:for-each>
<p style="font-weight:bold">
<xsl:text>Hind: </xsl:text>
<xsl:choose>
<xsl:when test="@price!=''">
<xsl:value-of select="@price"/>
</xsl:when>
<xsl:otherwise>
<xsl:text>-</xsl:text>
</xsl:otherwise>
</xsl:choose>
<xsl:text> / Päevapakkumise hind: </xsl:text>
<xsl:choose>
<xsl:when test="@dailyprice!=''">
<xsl:value-of select="@dailyprice"/>
</xsl:when>
<xsl:otherwise>
<xsl:text>-</xsl:text>
</xsl:otherwise>
</xsl:choose>
</p>
<div style="background-color:LightGray;">
<xsl:text>Hinnang praele: </xsl:text>
<xsl:choose>
<xsl:when test="rating/@ratingvalue!=''">
<xsl:value-of select="rating/@ratingvalue"/>
</xsl:when>
<xsl:otherwise>
<xsl:text>Ei ole veel hinnatud!</xsl:text>
</xsl:otherwise>
</xsl:choose>
<br/>
<xsl:for-each select="rating/comments/comment">
<xsl:value-of select="username"/>
<xsl:text>: </xsl:text>
<xsl:value-of select="commenttext"/>
<br/>
</xsl:for-each>
</div>
<br/>
</xsl:for-each>
</ul>
</xsl:otherwise>
</xsl:choose>
<div style="background-color:LightGray;">
<xsl:text>Hinnang restoranile: </xsl:text>
<xsl:choose>
<xsl:when test="rating/@ratingvalue!=''">
<xsl:value-of select="rating/@ratingvalue"/>
</xsl:when>
<xsl:otherwise>
<xsl:text>Ei ole veel hinnatud!</xsl:text>
</xsl:otherwise>
</xsl:choose>
<br/>
<xsl:for-each select="rating/comments/comment">
<xsl:value-of select="username"/>
<xsl:text>: </xsl:text>
<xsl:value-of select="commenttext"/>
<br/>
</xsl:for-each>
</div>
<p>
<xsl:text>*G - gluteenivaba, </xsl:text>
<xsl:text>L - laktoosivaba, </xsl:text>
<xsl:text>V - vegan </xsl:text>
</p>
</xsl:for-each>
</body>
</html>
</xsl:template>
</xsl:stylesheet>
XSLT(XML)
XML'is kuvatakse elemendid ja atribuudid eesti keeles. Kuvatakse restorani andmed ja toidud, mille hinnang on 8 või rohkem punkti.
<?xml version="1.0" encoding="utf-8" ?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:msxsl="urn:schemas-microsoft-com:xslt" exclude-result-prefixes="msxsl">
<xsl:output method="xml" indent="yes"/>
<xsl:template match="/restaurants">
<restoranid>
<xsl:for-each select="restaurant">
<restoran url="{@url}" telefon="{@contactnumber}" email="{@email}">
<nimi>
<xsl:value-of select="name"/>
</nimi>
<aadress>
<postiaadress>
<xsl:value-of select="address/addressfirstline"/>
</postiaadress>
<linn>
<xsl:value-of select="address/locality"/>
</linn>
<indeks>
<xsl:value-of select="address/postcode"/>
</indeks>
<riik>
<xsl:value-of select="address/@country"/>
</riik>
</aadress>
<parimadToidud>
<xsl:choose>
<xsl:when test="menus/menu/dishes/dish">
<xsl:for-each select="menus/menu/dishes/dish">
<xsl:if test="rating/@ratingvalue >= 8.0">
<heaToit hinnang="{rating/@ratingvalue}" paevaPakkumiseHind="{@dailyprice}" hind="{@price}"
vegan="{@vegan}" laktoosivaba="{@lactosefree}" gluteenivaba="{@glutenfree}">
<nimi>
<xsl:value-of select="title"/>
</nimi>
<kirjeldus>
<xsl:value-of select="description"/>
</kirjeldus>
</heaToit>
</xsl:if>
</xsl:for-each>
</xsl:when>
<xsl:otherwise>
<xsl:for-each select="dishes/dish">
<xsl:if test="rating/@ratingvalue >= 8.0">
<heaToit hinnang="{rating/@ratingvalue}" paevaPakkumiseHind="{@dailyprice}" hind="{@price}"
vegan="{@vegan}" laktoosivaba="{@lactosefree}" gluteenivaba="{@glutenfree}">
<nimi>
<xsl:value-of select="title"/>
</nimi>
<kirjeldus>
<xsl:value-of select="description"/>
</kirjeldus>
</heaToit>
</xsl:if>
</xsl:for-each>
</xsl:otherwise>
</xsl:choose>
</parimadToidud>
</restoran>
</xsl:for-each>
</restoranid>
</xsl:template>
</xsl:stylesheet>
Retsensioonid
Veebiteenuse retsensioon meeskonnale MOT
- Puudub README vms. Dokumentatsioon projekti ülespanemiseks ja seadistamiseks.
- Rollide loomine vigane, seda võiks teha eraldi failis, mis on DAL.App.EF projektis (kasutasime algul sama lähenemist nagu antud projekt). Takistab esialgset andmebaasi uuendust, kui rollid veel puuduvad. Rakenduse installimine tarnitud kujul ei õnnestunud. Selleks et Nuget Package Manageri käsk Update-Database töötaks pidi ajutiselt välja kommenteerima Startup.cs faili rea 153 (// CreateRoles(services).Wait();).
Andmebaasist:
- Üldiselt paneksin igale väljale juurde AddTime ja UpdateTime see on hea tava millest korduvalt ka andmebaasi alustes räägiti ja rõhutati.
Olemitest:
- Kui juba domainis nimetate kõik olemid singulaarselt siis võiks ju sama teha ka andmebaasis - lisakeerukuse loomine kohas kus see ei ole vajalik.
- Entity-te arv oli nõutust väiksem - Kokku 8 olemit, millest 1 on many-to-many liittabel ja 1 otsene koopia Identity framework Users tabelist.
- Olem People ~= Users ja sisaldab ka viidet Identity tabelile ApplicationUsers (ApplicationUserId). Juhul kui see on mõeldud rakendusega mitte registreerunud kasutajate võistlusele kirja panemiseks, siis oleks olemil rohkem infot vaja - vähemalt mingigi kontakti väli (email |/& tel.nr. |/& aadress).
- Competition.Comment on varchar(100) - võiks ja peaks olema varchar(1000)
- Competition.CompetitionPlaceId - kui võistluse toimumiskoht mingil põhjusel peaks keelduma korraldusest või kui võistlust alles pannakse kokku ja ei ole veel teada kus see toimuma saab siis mis peaks sinna sisestama (NOT NULL => NULL)
- Competition.Time -> Mis aega see näitab? Property nimest seda välja ei loe. Kui StartTime, siis kus on EndTime või Duration?
- CompetitionPlace.County -> int type enum default väärtus on 0, kuna see pole aga enumi klassis implementeeritud põhjustab see vea. (Millisele maakonnale vastab 0?) See teada tuntud probleem millele google annab hõlpsasti erinevaid lahendusi: näiteks siin ja siin.
- Väljad mis viitavad Identity framework olemitele läbi nende ID-de ei peaks olema nvarchar(max) vaid nvarchar(450) (kuigi isegi see on reaalsuses liiga pikk).
- PersonInTeam.PersonInTeamId -> ei peaks olema sellist välja üldse, selle tabeli PK peaks olema kombinatsioon nende tabeli ID-st mida ta ühendab. Praegu ei ole unique indeksit üle nende, mis tähendab et sama inimene saab mitu korda sama tiimiga liituda.
- Participations - Miks selline nimi, kui registreerimine juba oli? Saan aru et selles olemis tahetakse säilitada võistluse tulemusi. Siis võiks ju juba olla CompetitionResults. Kus on aga saavutatud koha numbri talletamine? Kui võistluse punktid ja ajad on kõik kirja saadud siis tahaks ju tulemusi ka kuidagi näha ja võrrelda ning selle võrdluse tulemi kuhugi salvestada.
- Participations.Time - Mis aega näitab? Eeldan, et võistluse läbimise aega? Kui nii, siis miks ei ole NULL lubatud - mõnedel võistlustel aega ju ei mõõdeta?
- Participation.Points - Kas igal võistlusel antakse ainult täisarv punkte (int)? Võiks igaks juhuks siiski implementeerida double väärtuse tüübi. Lisaks ei ole võimalik osalust luua ilma punkte teadmata - peaks siis määrama default väärtus. Mõnede võistluste puhul jälle punkte ei anta vaid mõõdetakse aega.
- Participation.Disqualified - Nullid ei ole lubatud -> järelikult oleks default väärtust vaja.
- Registrations - Projekti Must have alla on märgitud “Osaleja saab registreerida võistlusele meeskonda” kuid olemis puudub TeamsId ning samuti ei ole seda võimalust kuidagi läbi Servicei implementeeritud.
- Registrations - Millal registreering tehti on suhteliselt tähtis teada. Paljude võistluste puhul sõltub sellest näiteks registreerimistasu.
DTO-d:
- Täiesti mõttetult sisse toodud kiht, lihtsalt selleks et nii oli ette nähtud. Kõik DTO-d konverteeritakse ümber 1:1-le Domain objektideks ja vastupidi.
- Üldiselt oleks vaja teha tööd veel DTOdega, et need rohkem peegeldaksid oma kasutust. Näiteks:
- küsides kõiki võistlusi, kas meil on tarvis avalikustada kliendile, mis on kasutaja ID, kes iga võistluse lõi? DTOsid võiks olla rohkem erinevate kasutusjuhtumite jaoks.
- CompetitionDTO võiks sisaldada ka nimekirja võistlejatest, kus koondatakse info ManyToMany seoste põhjal kasutades vahetabelit, nagu näiteks:
List<PersonDTO> people = c.Participation
.FindAll(p => p.CompetitionId == c.CompetitionId)
.Select(PersonDTO.CreateFromParticipation)
.ToList();
API Kontrolleritest:
- Puudub Swagger startup konfiguratsioon, Swagger annotatsioonid kaotavad mõtte ning pole võimalik kontrollida, kas need on korrektselt loodud.
- ProducesResponse annotatsiooni pole kusagile lisatud, staatuskoodid on natuke poolikult läbi mõeldud, vähemalt need, mida Swaggeri raames mainitud on
- Annotatsioon Authorize, kus on ära määratud AuthorizationScheme peaks minema kogu klassi peale, meetodis saab ära määrata vaid selle, mis rollid on lubatud.
- AccountController
- Response code uue kasutaja loomisel võiks olla 201
- Puuduvad koodid nagu 429, 500
- Registreerumisel võiks kontrollida, kas kasutaja on juba olemas, mitte vaid rolli olemasolu
- Kas kasutajal ei või antud kontekstis olla mitu rolli? Sisselogimisel võetakse rollide seast vaid esimene ja lisatakse juurde Claimile. Võimalus kirjutada ka meetod, mis lisab kasutajale kõik rollid.
- CompetitionPlacesController/CompetitionsController/CompetitionTypesController/ParticipationsController/RegistrationsController/TeamsController
- Antud kontrollerid on kõik väga sarnase loogikaga
- Get() tagastab vaid koodi 200, tagastuskoodi võimalusi on veel
- Post() meetod küsib sisse DTOd, mille üheks parameetriks on id, id genereeritakse automaatselt, seda ei peaks panema kaasa postitatavasse mudelisse, mis alles loob objekti
- Put() puhul sama kriitika, siin on id küll vajalik, aga see juba pannakse kaasa parameetrina eraldi
- See tundub natuke veidrana üritada kontrollida uuendatud mudeli põhjal, kas tegemist oli BadRequestiga. Eelkõige peaks id puhul üles otsima, kas antud objekt on üldse olemas ja seejärel üritada uuendada, muidu tagastada BadRequest. Kui see ei õnnestu, siis peaks juba errorkood 500 peegeldama seda, et midagi läks teenuse poole peal valesti.
- Sama kommentaar deletemise kohta, enne tuleks kontrollida, kas objekt on olemas. Siis alles objekt kustutada.
- PeopleController
- Kas tõepoolest igaüks saab pärida infot inimeste kohta ja neid postitada?
- PersonInTeamsController
- API disaini poole pealt on natuke veider teha vahetabelite kohta kontroller. Tundub loogilisem panna antud kontrolleri meetodid teistesse kontroleritesse, mis on seotud siis kas meeskondade või inimestega. Näiteks küsida ** PeopleControlleris kõik inimesed, kes on meeskondades või küsida neid meeskonna järgi. TeamsControlleris seevastu anda võimalus lisada uusi inimesi meeskonda, neid muuta või eemaldada.
- Id järgi ei hakka ükski klient küsima vahetabeli kohta infot, see tundub otstarbetu tegevus, pigem küsida kas meeskonna või inimese id järgi sissekandeid
- Kriitika kontrollerite kohta: kontrollerid enamuses pöörduvad vaid spetsiifiliste domeenide poole ja viivad ellu väga tavalist CRUD funktsionaalsust, mille tõttu tundub, et need on natuke veel toored. Näiteks on välja toodud “must have” nimekirjas projekti analüüsis, et meeskonna liige peaks saama muuta meeskonna andmeid. Antud kontrollerites vaid autentitakse, et kasutaja oleks rollis “participant”, kuid mitte seda, kas ta ka muudetava meeskonna liige on. Samuti pole võimalik kasutajatel näha, palju on mingile võistlusele osalejaid registreerunud, CompetitionsController ei tegele sellega, et tagastada võistlusele registreerunud kasutajaid, selle asemel on olemas ParticipationsController, mis tagastab vahetabeli infot selle kohta, mis võistlusega on seotud mis inimene. See võib olla tohutult suur info, mida hakata filtreerima kliendi poolelt. Rohkem tuleks teha andmete agregeerimist vastavalt teenuse kontekstile.
Projekti kihid/struktuur:
- Olemas Business Logic, Data Access Layer, DTO’d, Service’id, Unit of Work, domeeniklassid ning API spetsiifilised kontrollerid, mis asuvad oma kaustas põhiprojekti all . DAL omab erinevaid kihte interface’ide ja neid implementeerivate klasside jaoks. Repositooriumitega suhtlemine on abstraktne, kõik käib läbi Data Access Layer’i, mille pööle pöörduvad teenuseklassid ning DAL erinevad kihid hoolitsevad selle eest, et loodaks õiget tüüpi repositooriumeid, andmeid agregeeritakse, uuendatakse ja salvestatakse organiseeritud kujul ning neid tagastatakse teenustele soovitud kujul (DTOd).
Logiraamat
07.02.2018
Gupi loomine ning suhtluskanali seadistamine
24.03.2018
Trello boardi loomine
25.03.2018
Wiki lehe loomine. Esimene tõsisem projekti koosolek ning tegevuskava paika panemine.
26.03.2018
Alustasime veebiteenuse analüüsiga ja funktsionaalsuse kirjeldamisega.
27.03.2018
Täiendasime veebiteenuse analüüsi.
Esmase andmebaasi mudeli koostamine.
Postitatud esimeste analüüsi arutelude põhjal valminud algne prototüüp.
30.03.2018
Valmis andmebaasimudel.
08.04.2018
Tehtud muudatused analüüsis vastavalt tagasisidele.
19.04.2018
Loodud projekti põhi koos domeenimudelitega. Projekt lisatud Githubi: https://github.com/sikumiku/TaisKohtApi
05-06.05.2018
Osalesime hackathonil. Lisatud controllerid, service'id koos vajalike kihtidega.
16.05.2018
Uuendatud andmebaasimudelit, DTO'sid.
18-19.05.2018
Lisatud klientrakendus, security, äriloogikat.
20.05.2018
Lisatud API dokumentatsioon.
22-24.05.2018
Uuendatud contoller'eid, service'id.
25-27.05.2018
Uuendatud contoller'eid, service'id. Lisatud kasutajate ja rollide kontrolle meetoditesse. Täiustatud klientrakendust. Swagger dokumentatsioon.
31.05-01.06.2018
Tegeletud XML, XSD ja XSLT failide loomisega.
02.06-03.06.2018
Tegeletud XML ja XSLT failide täiustamisega ja retsensioonide kirjutamisega.