diff --git a/.husky/pre-commit b/.husky/pre-commit index 1bd14e2..37fe008 100644 --- a/.husky/pre-commit +++ b/.husky/pre-commit @@ -1,6 +1,6 @@ npm run compile all -CLIENT_DIFFS=$(git status dist/clients --porcelain) +CLIENT_DIFFS=$(git diff --name-only dist/clients) if [[ $CLIENT_DIFFS ]]; then echo "OpenAPI spec is out of date. Add your OpenAPI file(s) to the commit:" echo "$CLIENT_DIFFS" diff --git a/dist/clients/faculty/openapi.yaml b/dist/clients/faculty/openapi.yaml index 3a177ed..166418d 100644 --- a/dist/clients/faculty/openapi.yaml +++ b/dist/clients/faculty/openapi.yaml @@ -11,6 +11,7 @@ tags: - name: Bookings - name: Courses - name: Lectures + - name: Factotum - name: News - name: People - name: Places @@ -1406,6 +1407,326 @@ paths: $ref: '#/components/schemas/ErrorResponse' tags: - Places + /incidents/categories: + get: + operationId: Factotum_getIssueCategories + summary: List issue categories | Elenca categorie di problema + description: |- + Lists the available issue categories. Categories are global + and do not depend on the place. + parameters: [] + responses: + '200': + description: The request has succeeded. + content: + application/json: + schema: + type: object + properties: + data: + type: array + items: + $ref: '#/components/schemas/IssueCategory' + required: + - data + example: + data: + - id: ELETTRICO + name: Elettrico + description: Problemi elettrici + priority: + - high + - urgent + - id: IDRAULICO + name: Idraulico + description: Problemi idraulici + priority: + - medium + - high + - id: ARREDI + name: Arredi + description: Arredi danneggiati + priority: + - low + - medium + - id: CLIMA + name: Condizionamento e Riscaldamento + description: Problemi climatizzazione + priority: + - low + - medium + - high + '400': + description: The server could not understand the request due to invalid syntax. + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '500': + description: Server error + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + tags: + - Factotum + /incidents/reports: + get: + operationId: Factotum_listIncidents + summary: List incidents | Elenca segnalazioni + description: |- + Lists incidents in minimal view (id, date, issue category id). If the + place filters are specified, returns only the incidents associated with + that place (useful for consulting the history before opening a new one); + without filters it returns all the incidents of the authenticated user. + parameters: + - name: buildingId + in: query + required: false + schema: + type: string + explode: false + - name: floorId + in: query + required: false + schema: + type: string + explode: false + - name: roomId + in: query + required: false + schema: + type: string + explode: false + responses: + '200': + description: The request has succeeded. + content: + application/json: + schema: + type: object + properties: + data: + type: array + items: + $ref: '#/components/schemas/IncidentOverview' + required: + - data + example: + data: + - id: INC-2026-0042 + date: '2026-04-20T18:30:00Z' + issueCategoryId: IDRAULICO + place: + siteId: TO_CIT + buildingId: TO_CIT11 + floorId: XP02 + roomId: '017' + - id: INC-2026-0039 + date: '2026-01-25T15:09:00Z' + issueCategoryId: ELETTRICO + place: + siteId: TO_CIT + buildingId: TO_CIT11 + floorId: XP02 + roomId: '017' + '400': + description: The server could not understand the request due to invalid syntax. + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '500': + description: Server error + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + tags: + - Factotum + post: + operationId: Factotum_createIncident + summary: Create incident | Crea segnalazione + description: |- + Creates a new fault/anomaly incident. The request is + multipart/form-data to allow the optional upload of an attachment + (e.g. photo of the problem). The place reference, the category, the + priority and the description are mandatory. + parameters: [] + responses: + '200': + description: The request has succeeded. + content: + application/json: + schema: + type: object + properties: + data: + type: object + properties: + insertedId: + type: integer + required: + - insertedId + required: + - data + '400': + description: The server could not understand the request due to invalid syntax. + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '500': + description: Server error + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + tags: + - Factotum + requestBody: + required: true + content: + multipart/form-data: + schema: + $ref: '#/components/schemas/IncidentRequest' + encoding: + place: + contentType: application/json + priority: + contentType: application/json + /incidents/reports/{incidentId}: + get: + operationId: FacultyFactotum_getIncident + summary: Get incident (faculty) | Mostra segnalazione (faculty) + description: |- + Returns the single incident identified by `incidentId`, + with all the details. + parameters: + - name: incidentId + in: path + required: true + schema: + type: string + responses: + '200': + description: The request has succeeded. + content: + application/json: + schema: + type: object + properties: + data: + $ref: '#/components/schemas/Incident' + required: + - data + example: + data: + id: INC-2026-0042 + description: Rubinetto del bagno donne che perde da ieri sera + date: '2026-04-20T18:30:00Z' + status: new + place: + siteId: TO_CIT + buildingId: TO_CIT11 + floorId: XP02 + roomId: '017' + issueCategoryId: IDRAULICO + attachments: + - id: 0 + filename: foto.jpg + mimeType: image/jpeg + sizeInKiloBytes: 512 + '400': + description: The server could not understand the request due to invalid syntax. + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '404': + description: The server cannot find the requested resource. + '500': + description: Server error + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + tags: + - Factotum + delete: + operationId: FacultyFactotum_deleteIncident + summary: Delete incident (faculty) | Annulla segnalazione (faculty) + description: Cancels an existing incident. + parameters: + - name: incidentId + in: path + required: true + schema: + type: string + responses: + '204': + description: 'There is no content to send for this request, but the headers may be useful. ' + '400': + description: The server could not understand the request due to invalid syntax. + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '404': + description: The server cannot find the requested resource. + '500': + description: Server error + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + tags: + - Factotum + /incidents/reports/{incidentId}/attachments/{attachmentId}: + get: + operationId: FacultyFactotum_getIncidentAttachment + summary: Download incident attachment (faculty) | Scarica allegato segnalazione (faculty) + description: |- + Downloads the binary content of an incident attachment. + May respond with the file inline or with a redirect to a temporary + download URL. + parameters: + - name: incidentId + in: path + required: true + schema: + type: string + - name: attachmentId + in: path + required: true + schema: + type: integer + responses: + '200': + description: The request has succeeded. + content: + application/octet-stream: + schema: + type: string + format: binary + '302': + description: Redirection + '400': + description: The server could not understand the request due to invalid syntax. + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '404': + description: The server cannot find the requested resource. + '500': + description: Server error + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + tags: + - Factotum /lectures: get: operationId: Lectures_getLectures @@ -3447,7 +3768,6 @@ components: - teachingPeriod - teacherId - teacherName - - previousEditions - isOverBooking - enrollmentRole - year @@ -3477,14 +3797,6 @@ components: type: string nullable: true example: Mario Rossi - previousEditions: - type: array - items: - $ref: '#/components/schemas/CourseModuleEdition' - description: Previous editions of this course that were part of the student's PSP - example: - - id: 244577 - year: '2021' isOverBooking: type: boolean example: false @@ -3503,9 +3815,6 @@ components: teachingPeriod: 2-2 teacherId: 3001 teacherName: Mario Rossi - previousEditions: - - id: 251005 - year: '2024' isOverBooking: false enrollmentRole: student year: '2025' @@ -3521,6 +3830,21 @@ components: id: type: integer example: 244577 + CourseModuleOverview: + type: object + required: + - previousEditions + properties: + previousEditions: + type: array + items: + $ref: '#/components/schemas/CourseModuleEdition' + description: Previous editions of this course that were part of the student's PSP + example: + - id: 244577 + year: '2021' + allOf: + - $ref: '#/components/schemas/CourseModule' CourseNotices: type: object required: @@ -3555,10 +3879,10 @@ components: modules: type: array items: - $ref: '#/components/schemas/CourseModule' + $ref: '#/components/schemas/CourseModuleOverview' nullable: true allOf: - - $ref: '#/components/schemas/CourseModule' + - $ref: '#/components/schemas/CourseModuleOverview' CoursePreferencesRequest: type: object properties: @@ -3812,6 +4136,161 @@ components: type: student clientId: 5a54f626-4b7e-488e-a0e3-300f54051510 token: c679fa38ff665df6a676acd833194385 + Incident: + type: object + required: + - description + - status + - attachments + properties: + description: + type: string + status: + $ref: '#/components/schemas/IncidentStatus' + attachments: + type: array + items: + $ref: '#/components/schemas/IncidentAttachment' + allOf: + - $ref: '#/components/schemas/IncidentOverview' + description: |- + Full incident on the faculty side: extends `IncidentOverview` with + description, status and attachments. Category details are retrieved + from the relevant endpoint. + example: + id: INC-2026-0042 + description: Rubinetto del bagno donne che perde da ieri sera + date: '2026-04-20T18:30:00Z' + status: new + place: + siteId: TO_CIT + buildingId: TO_CIT11 + floorId: XP02 + roomId: '017' + issueCategoryId: IDRAULICO + attachments: + - id: 0 + filename: foto.jpg + mimeType: image/jpeg + sizeInKiloBytes: 512 + IncidentAttachment: + type: object + required: + - id + - filename + - mimeType + - sizeInKiloBytes + properties: + id: + type: number + filename: + type: string + mimeType: + type: string + sizeInKiloBytes: + type: integer + description: |- + Attachment of an incident. To be clarified whether it can be only a photo + or any type of file. + example: + id: 0 + filename: foto.jpg + mimeType: image/jpeg + sizeInKiloBytes: 512 + IncidentOverview: + type: object + required: + - id + - date + - issueCategoryId + - place + properties: + id: + type: string + date: + type: string + format: date-time + issueCategoryId: + type: string + place: + $ref: '#/components/schemas/PlaceIds' + description: |- + Minimal view of an incident: id, date, place and issue category id. + Category details are retrieved from `/incidents/categories`. Faculty + extends this model with the full details. + example: + id: INC-2026-0042 + date: '2026-04-20T18:30:00Z' + issueCategoryId: IDRAULICO + place: + siteId: TO_CIT + buildingId: TO_CIT11 + floorId: XP02 + roomId: '017' + IncidentPriority: + type: string + enum: + - low + - medium + - high + - urgent + description: |- + Intervention priority associated with an issue category. + Determines the urgency with which the incident is handled. + IncidentRequest: + type: object + properties: + place: + $ref: '#/components/schemas/PlaceIds' + issueCategoryId: + type: string + priority: + $ref: '#/components/schemas/IncidentPriority' + description: + type: string + attachment: + type: string + format: binary + required: + - place + - issueCategoryId + - priority + - description + IncidentStatus: + type: string + enum: + - new + - in_progress + - resolved + - closed + IssueCategory: + type: object + required: + - id + - name + - description + - priority + properties: + id: + type: string + name: + type: string + description: + type: string + priority: + type: array + items: + $ref: '#/components/schemas/IncidentPriority' + description: Priorities selectable when opening an incident of this category. + description: Related to the type of issue + example: + id: IDRAULICO + name: Idraulico + description: 'Problemi idraulici: rubinetti, scarichi, perdite' + priority: + - medium + - high + - urgent Lecture: type: object required: @@ -4295,6 +4774,8 @@ components: items: type: object properties: + id: + type: string name: type: string description: @@ -4302,6 +4783,7 @@ components: category: type: string required: + - id - name - description - category @@ -4374,6 +4856,22 @@ components: example: false subCategory: $ref: '#/components/schemas/PlaceCategory' + PlaceIds: + type: object + required: + - buildingId + - floorId + - roomId + - siteId + properties: + buildingId: + type: string + floorId: + type: string + roomId: + type: string + siteId: + type: string PlaceOverview: type: object required: diff --git a/dist/clients/student/openapi.yaml b/dist/clients/student/openapi.yaml index 36c8ae2..e8d332f 100644 --- a/dist/clients/student/openapi.yaml +++ b/dist/clients/student/openapi.yaml @@ -11,6 +11,7 @@ tags: - name: Bookings - name: Courses - name: Lectures + - name: Factotum - name: News - name: People - name: Places @@ -1810,6 +1811,194 @@ paths: $ref: '#/components/schemas/ErrorResponse' tags: - Student + /incidents/categories: + get: + operationId: Factotum_getIssueCategories + summary: List issue categories | Elenca categorie di problema + description: |- + Lists the available issue categories. Categories are global + and do not depend on the place. + parameters: [] + responses: + '200': + description: The request has succeeded. + content: + application/json: + schema: + type: object + properties: + data: + type: array + items: + $ref: '#/components/schemas/IssueCategory' + required: + - data + example: + data: + - id: ELETTRICO + name: Elettrico + description: Problemi elettrici + priority: + - high + - urgent + - id: IDRAULICO + name: Idraulico + description: Problemi idraulici + priority: + - medium + - high + - id: ARREDI + name: Arredi + description: Arredi danneggiati + priority: + - low + - medium + - id: CLIMA + name: Condizionamento e Riscaldamento + description: Problemi climatizzazione + priority: + - low + - medium + - high + '400': + description: The server could not understand the request due to invalid syntax. + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '500': + description: Server error + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + tags: + - Factotum + /incidents/reports: + get: + operationId: Factotum_listIncidents + summary: List incidents | Elenca segnalazioni + description: |- + Lists incidents in minimal view (id, date, issue category id). If the + place filters are specified, returns only the incidents associated with + that place (useful for consulting the history before opening a new one); + without filters it returns all the incidents of the authenticated user. + parameters: + - name: buildingId + in: query + required: false + schema: + type: string + explode: false + - name: floorId + in: query + required: false + schema: + type: string + explode: false + - name: roomId + in: query + required: false + schema: + type: string + explode: false + responses: + '200': + description: The request has succeeded. + content: + application/json: + schema: + type: object + properties: + data: + type: array + items: + $ref: '#/components/schemas/IncidentOverview' + required: + - data + example: + data: + - id: INC-2026-0042 + date: '2026-04-20T18:30:00Z' + issueCategoryId: IDRAULICO + place: + siteId: TO_CIT + buildingId: TO_CIT11 + floorId: XP02 + roomId: '017' + - id: INC-2026-0039 + date: '2026-01-25T15:09:00Z' + issueCategoryId: ELETTRICO + place: + siteId: TO_CIT + buildingId: TO_CIT11 + floorId: XP02 + roomId: '017' + '400': + description: The server could not understand the request due to invalid syntax. + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '500': + description: Server error + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + tags: + - Factotum + post: + operationId: Factotum_createIncident + summary: Create incident | Crea segnalazione + description: |- + Creates a new fault/anomaly incident. The request is + multipart/form-data to allow the optional upload of an attachment + (e.g. photo of the problem). The place reference, the category, the + priority and the description are mandatory. + parameters: [] + responses: + '200': + description: The request has succeeded. + content: + application/json: + schema: + type: object + properties: + data: + type: object + properties: + insertedId: + type: integer + required: + - insertedId + required: + - data + '400': + description: The server could not understand the request due to invalid syntax. + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + '500': + description: Server error + content: + application/json: + schema: + $ref: '#/components/schemas/ErrorResponse' + tags: + - Factotum + requestBody: + required: true + content: + multipart/form-data: + schema: + $ref: '#/components/schemas/IncidentRequest' + encoding: + place: + contentType: application/json + priority: + contentType: application/json /job-offers: get: operationId: JobOffers_getJobOffers @@ -5730,6 +5919,100 @@ components: type: student clientId: 5a54f626-4b7e-488e-a0e3-300f54051510 token: c679fa38ff665df6a676acd833194385 + IncidentOverview: + type: object + required: + - id + - date + - issueCategoryId + - place + properties: + id: + type: string + date: + type: string + format: date-time + issueCategoryId: + type: string + place: + $ref: '#/components/schemas/PlaceIds' + description: |- + Minimal view of an incident: id, date, place and issue category id. + Category details are retrieved from `/incidents/categories`. Faculty + extends this model with the full details. + example: + id: INC-2026-0042 + date: '2026-04-20T18:30:00Z' + issueCategoryId: IDRAULICO + place: + siteId: TO_CIT + buildingId: TO_CIT11 + floorId: XP02 + roomId: '017' + IncidentPriority: + type: string + enum: + - low + - medium + - high + - urgent + description: |- + Intervention priority associated with an issue category. + Determines the urgency with which the incident is handled. + IncidentRequest: + type: object + properties: + place: + $ref: '#/components/schemas/PlaceIds' + issueCategoryId: + type: string + priority: + $ref: '#/components/schemas/IncidentPriority' + description: + type: string + attachment: + type: string + format: binary + required: + - place + - issueCategoryId + - priority + - description + IncidentStatus: + type: string + enum: + - new + - in_progress + - resolved + - closed + IssueCategory: + type: object + required: + - id + - name + - description + - priority + properties: + id: + type: string + name: + type: string + description: + type: string + priority: + type: array + items: + $ref: '#/components/schemas/IncidentPriority' + description: Priorities selectable when opening an incident of this category. + description: Related to the type of issue + example: + id: IDRAULICO + name: Idraulico + description: 'Problemi idraulici: rubinetti, scarichi, perdite' + priority: + - medium + - high + - urgent JobOffer: type: object required: @@ -6520,6 +6803,8 @@ components: items: type: object properties: + id: + type: string name: type: string description: @@ -6527,6 +6812,7 @@ components: category: type: string required: + - id - name - description - category @@ -6599,6 +6885,22 @@ components: example: false subCategory: $ref: '#/components/schemas/PlaceCategory' + PlaceIds: + type: object + required: + - buildingId + - floorId + - roomId + - siteId + properties: + buildingId: + type: string + floorId: + type: string + roomId: + type: string + siteId: + type: string PlaceOverview: type: object required: diff --git a/src/clients/faculty/main.tsp b/src/clients/faculty/main.tsp index 2e2e803..719f50d 100644 --- a/src/clients/faculty/main.tsp +++ b/src/clients/faculty/main.tsp @@ -9,11 +9,13 @@ import "../../routes/common/announcements.tsp"; import "../../routes/common/auth.tsp"; import "../../routes/common/bookings.tsp"; import "../../routes/common/courses.tsp"; +import "../../routes/common/factotum.tsp"; import "../../routes/common/news.tsp"; import "../../routes/common/people.tsp"; import "../../routes/common/places.tsp"; // Faculty routes +import "../../routes/faculty/factotum.tsp"; import "./version.tsp"; diff --git a/src/clients/student/main.tsp b/src/clients/student/main.tsp index a86a9b4..6b5fca3 100644 --- a/src/clients/student/main.tsp +++ b/src/clients/student/main.tsp @@ -9,6 +9,7 @@ import "../../routes/common/announcements.tsp"; import "../../routes/common/auth.tsp"; import "../../routes/common/bookings.tsp"; import "../../routes/common/courses.tsp"; +import "../../routes/common/factotum.tsp"; import "../../routes/common/news.tsp"; import "../../routes/common/people.tsp"; import "../../routes/common/places.tsp"; diff --git a/src/common.tsp b/src/common.tsp index 0b7dec4..c4f80b4 100644 --- a/src/common.tsp +++ b/src/common.tsp @@ -11,11 +11,15 @@ model UpdatePreferencesRequest { language?: "it" | "en"; } -model PlaceRef { +model PlaceIds { buildingId: string; floorId: string; roomId: string; siteId: string; +} + +model PlaceRef { + ...PlaceIds; name: string; } diff --git a/src/examples/common/factotum.tsp b/src/examples/common/factotum.tsp new file mode 100644 index 0000000..5d5b546 --- /dev/null +++ b/src/examples/common/factotum.tsp @@ -0,0 +1,89 @@ +import "@typespec/http"; + +using Http; + +namespace PolitoAPI; + +const _ex_issue_category = #{ + id: "IDRAULICO", + name: "Idraulico", + description: "Problemi idraulici: rubinetti, scarichi, perdite", + priority: #[ + IncidentPriority.medium, + IncidentPriority.high, + IncidentPriority.urgent + ], +}; + +const _ex_place_id = #{ + siteId: "TO_CIT", + buildingId: "TO_CIT11", + floorId: "XP02", + roomId: "017", +}; + +const _ex_issue_categories_list = #[ + #{ + id: "ELETTRICO", + name: "Elettrico", + description: "Problemi elettrici", + priority: #[IncidentPriority.high, IncidentPriority.urgent], + }, + #{ + id: "IDRAULICO", + name: "Idraulico", + description: "Problemi idraulici", + priority: #[IncidentPriority.medium, IncidentPriority.high], + }, + #{ + id: "ARREDI", + name: "Arredi", + description: "Arredi danneggiati", + priority: #[IncidentPriority.low, IncidentPriority.medium], + }, + #{ + id: "CLIMA", + name: "Condizionamento e Riscaldamento", + description: "Problemi climatizzazione", + priority: #[ + IncidentPriority.low, + IncidentPriority.medium, + IncidentPriority.high + ], + } +]; + +const _ex_predefined_descriptions = #[ + "Flusso di lavoro", + "Risposta necessaria entro 1 Settimana", + "Completamento necessario entro 1 Mesi", + "Nessuna procedura necessaria" +]; + +const _ex_incident_overview = #{ + id: "INC-2026-0042", + date: utcDateTime.fromISO("2026-04-20T18:30:00Z"), + issueCategoryId: "IDRAULICO", + place: _ex_place_id, +}; + +const _ex_listIncidents_resp = #{ + returnType: #{ + statusCode: 200, + body: #{ + data: #[ + _ex_incident_overview, + #{ + id: "INC-2026-0039", + date: utcDateTime.fromISO("2026-01-25T15:09:00Z"), + issueCategoryId: "ELETTRICO", + place: _ex_place_id, + } + ], + }, + }, +}; + +const _ex_getIssueCategories_resp = #{ + returnType: #{ statusCode: 200, body: #{ data: _ex_issue_categories_list } }, +}; diff --git a/src/examples/faculty/factotum.tsp b/src/examples/faculty/factotum.tsp new file mode 100644 index 0000000..f2b0138 --- /dev/null +++ b/src/examples/faculty/factotum.tsp @@ -0,0 +1,27 @@ +import "@typespec/http"; +import "../common/factotum.tsp"; + +using Http; + +namespace PolitoAPI; + +const _ex_incident_attachment = #{ + id: 0, + filename: "foto.jpg", + mimeType: "image/jpeg", + sizeInKiloBytes: 512, +}; + +const _ex_incident = #{ + id: "INC-2026-0042", + description: "Rubinetto del bagno donne che perde da ieri sera", + date: utcDateTime.fromISO("2026-04-20T18:30:00Z"), + status: IncidentStatus.new, + place: _ex_place_id, + issueCategoryId: "IDRAULICO", + attachments: #[_ex_incident_attachment], +}; + +const _ex_faculty_getIncident_resp = #{ + returnType: #{ statusCode: 200, body: #{ data: _ex_incident } }, +}; diff --git a/src/routes/common/factotum.tsp b/src/routes/common/factotum.tsp new file mode 100644 index 0000000..0eeaa4b --- /dev/null +++ b/src/routes/common/factotum.tsp @@ -0,0 +1,105 @@ +import "@typespec/http"; +import "@typespec/openapi3"; + +import "../../common.tsp"; +import "../../examples/common/factotum.tsp"; + +namespace PolitoAPI; + +using Http; + +enum IncidentStatus { + new: "new", + inProgress: "in_progress", + resolved: "resolved", + closed: "closed", +} + +/** + * Intervention priority associated with an issue category. + * Determines the urgency with which the incident is handled. + */ +enum IncidentPriority { + low: "low", + medium: "medium", + high: "high", + urgent: "urgent", +} + +/** Related to the type of issue */ +@example(_ex_issue_category) +model IssueCategory { + id: string; + name: string; + description: string; + + /** Priorities selectable when opening an incident of this category. */ + priority: IncidentPriority[]; +} + +/** + * Minimal view of an incident: id, date, place and issue category id. + * Category details are retrieved from `/incidents/categories`. Faculty + * extends this model with the full details. + */ +@example(_ex_incident_overview) +model IncidentOverview { + id: string; + date: utcDateTime; + issueCategoryId: string; + place: PlaceIds; +} + +model IncidentRequest { + place: HttpPart; + issueCategoryId: HttpPart; + priority: HttpPart; + description: HttpPart; + attachment?: HttpPart; +} + +@tag("Factotum") +@route("/incidents") +interface Factotum { + /** + * Lists the available issue categories. Categories are global + * and do not depend on the place. + */ + @get + @route("/categories") + @summary("List issue categories | Elenca categorie di problema") + @opExample(_ex_getIssueCategories_resp) + getIssueCategories(): OkDataResponse | BaseErrors; + + /** + * Lists incidents in minimal view (id, date, issue category id). If the + * place filters are specified, returns only the incidents associated with + * that place (useful for consulting the history before opening a new one); + * without filters it returns all the incidents of the authenticated user. + */ + @get + @route("/reports") + @summary("List incidents | Elenca segnalazioni") + @opExample(_ex_listIncidents_resp) + listIncidents( + @query buildingId?: string, + @query floorId?: string, + @query roomId?: string, + ): OkDataResponse | BaseErrors; + + /** + * Creates a new fault/anomaly incident. The request is + * multipart/form-data to allow the optional upload of an attachment + * (e.g. photo of the problem). The place reference, the category, the + * priority and the description are mandatory. + */ + @post + @route("/reports") + @summary("Create incident | Crea segnalazione") + createIncident( + @header contentType: "multipart/form-data", + @multipartBody body: IncidentRequest, + ): OkDataResponse<{ + insertedId: integer; + }> | BaseErrors; +} diff --git a/src/routes/common/places.tsp b/src/routes/common/places.tsp index 0455c69..2ef5000 100644 --- a/src/routes/common/places.tsp +++ b/src/routes/common/places.tsp @@ -135,6 +135,7 @@ model Place { phone: string | null; } | null; resources: { + id: string; name: string; description: string; category: string; diff --git a/src/routes/faculty/factotum.tsp b/src/routes/faculty/factotum.tsp new file mode 100644 index 0000000..984ab4a --- /dev/null +++ b/src/routes/faculty/factotum.tsp @@ -0,0 +1,77 @@ +import "@typespec/http"; +import "@typespec/openapi3"; + +import "../../common.tsp"; +import "../../examples/faculty/factotum.tsp"; +import "../common/factotum.tsp"; + +namespace PolitoAPI; + +using Http; + +/** + * Attachment of an incident. To be clarified whether it can be only a photo + * or any type of file. + */ +@example(_ex_incident_attachment) +model IncidentAttachment { + id: numeric; + filename: string; + mimeType: string; + sizeInKiloBytes: integer; +} + +/** + * Full incident on the faculty side: extends `IncidentOverview` with + * description, status and attachments. Category details are retrieved + * from the relevant endpoint. + */ +@example(_ex_incident) +model Incident extends IncidentOverview { + description: string; + status: IncidentStatus; + attachments: IncidentAttachment[]; +} + +@tag("Factotum") +@route("/incidents/reports") +interface FacultyFactotum { + /** + * Returns the single incident identified by `incidentId`, + * with all the details. + */ + @get + @route("/{incidentId}") + @summary("Get incident (faculty) | Mostra segnalazione (faculty)") + @opExample(_ex_faculty_getIncident_resp) + getIncident( + @path incidentId: string, + ): OkDataResponse | NotFoundResponse | BaseErrors; + + /** + * Cancels an existing incident. + */ + @route("/{incidentId}") + @delete + @summary("Delete incident (faculty) | Annulla segnalazione (faculty)") + deleteIncident( + @path incidentId: string, + ): NoContentResponse | NotFoundResponse | BaseErrors; + + /** + * Downloads the binary content of an incident attachment. + * May respond with the file inline or with a redirect to a temporary + * download URL. + */ + @get + @route("/{incidentId}/attachments/{attachmentId}") + @summary("Download incident attachment (faculty) | Scarica allegato segnalazione (faculty)") + getIncidentAttachment(@path incidentId: string, @path attachmentId: integer): + | { + @header contentType: "application/octet-stream"; + @body body: bytes; + } + | RedirectResponse + | NotFoundResponse + | BaseErrors; +}