diff --git a/eeg_backend/src/main/java/at/mueller/eeg/backend/community/repository/MembershipRepository.java b/eeg_backend/src/main/java/at/mueller/eeg/backend/community/repository/MembershipRepository.java index d020d38..3392750 100644 --- a/eeg_backend/src/main/java/at/mueller/eeg/backend/community/repository/MembershipRepository.java +++ b/eeg_backend/src/main/java/at/mueller/eeg/backend/community/repository/MembershipRepository.java @@ -34,6 +34,6 @@ public interface MembershipRepository extends JpaRepository { @Query("SELECT m FROM Membership m JOIN FETCH m.meteringPoint mp JOIN FETCH m.energyCommunity ec WHERE mp.userId = :userId") List findByUserIdWithDetails(@Param("userId") UUID userId); - @Query("SELECT m FROM Membership m JOIN FETCH m.meteringPoint mp JOIN FETCH m.energyCommunity ec JOIN FETCH mp.user u WHERE m.status = 'PENDING'") + @Query("SELECT m FROM Membership m JOIN FETCH m.meteringPoint mp JOIN FETCH m.energyCommunity ec WHERE m.status = 'PENDING'") List findPendingWithDetails(); } \ No newline at end of file diff --git a/eeg_backend/src/test/java/at/mueller/eeg/backend/OpenApiGeneratorTest.java b/eeg_backend/src/test/java/at/mueller/eeg/backend/OpenApiGeneratorTest.java index ef7d9db..cdd92b4 100644 --- a/eeg_backend/src/test/java/at/mueller/eeg/backend/OpenApiGeneratorTest.java +++ b/eeg_backend/src/test/java/at/mueller/eeg/backend/OpenApiGeneratorTest.java @@ -2,10 +2,11 @@ package at.mueller.eeg.backend; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.webmvc.test.autoconfigure.AutoConfigureMockMvc; +import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.web.servlet.MockMvc; +import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; @@ -14,7 +15,7 @@ import static org.springframework.test.web.servlet.request.MockMvcRequestBuilder import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; @SpringBootTest -@AutoConfigureMockMvc +@AutoConfigureMockMvc(addFilters = false) class OpenApiGeneratorTest { @Autowired @@ -26,11 +27,11 @@ class OpenApiGeneratorTest { .andExpect(status().isOk()) .andReturn() .getResponse() - .getContentAsString(); + .getContentAsString(StandardCharsets.UTF_8); Path outputPath = Paths.get("../eeg_frontend/openapi.yaml"); - Files.writeString(outputPath, openApiYaml); + Files.writeString(outputPath, openApiYaml, StandardCharsets.UTF_8); System.out.println("OpenAPI YAML erfolgreich nach " + outputPath.toAbsolutePath() + " exportiert."); } diff --git a/eeg_frontend/openapi.yaml b/eeg_frontend/openapi.yaml index f0995ce..3c79ada 100644 --- a/eeg_frontend/openapi.yaml +++ b/eeg_frontend/openapi.yaml @@ -7,10 +7,135 @@ servers: description: Generated server url tags: - name: Authentication - description: Public Endpoints für Login und Registrierung + description: Public Endpoints für Login und Registrierung +- name: User Profile + description: Profilverwaltung und Passwort-Reset - name: Admin IAM description: User-Verwaltung und Freigabe-Workflow paths: + /api/users/me: + get: + tags: + - User Profile + summary: Eigenes Profil abrufen + operationId: getOwnProfile + responses: + "200": + description: OK + content: + '*/*': + schema: + $ref: "#/components/schemas/UserProfileResponse" + put: + tags: + - User Profile + summary: Profil aktualisieren + operationId: updateOwnProfile + requestBody: + content: + application/json: + schema: + $ref: "#/components/schemas/UpdateProfileRequest" + required: true + responses: + "200": + description: OK + content: + '*/*': + schema: + $ref: "#/components/schemas/UserProfileResponse" + /api/users/me/password: + put: + tags: + - User Profile + summary: Passwort ändern + operationId: changePassword + requestBody: + content: + application/json: + schema: + $ref: "#/components/schemas/ChangePasswordRequest" + required: true + responses: + "200": + description: OK + /api/users/me/email: + put: + tags: + - User Profile + summary: E-Mail ändern (mit erneuter Verifizierung) + operationId: changeEmail + requestBody: + content: + application/json: + schema: + $ref: "#/components/schemas/ChangeEmailRequest" + required: true + responses: + "200": + description: OK + /api/notifications/{id}/read: + put: + tags: + - notification-controller + operationId: markAsRead + parameters: + - name: id + in: path + required: true + schema: + type: string + format: uuid + responses: + "200": + description: OK + /api/notifications/read-all: + put: + tags: + - notification-controller + operationId: markAllAsRead + responses: + "200": + description: OK + /api/community/metering-points/{id}: + put: + tags: + - metering-point-controller + operationId: updateOwnMeteringPoint + parameters: + - name: id + in: path + required: true + schema: + type: string + format: uuid + requestBody: + content: + application/json: + schema: + $ref: "#/components/schemas/UpdateMeteringPointRequest" + required: true + responses: + "200": + description: OK + content: + '*/*': + schema: + $ref: "#/components/schemas/MeteringPointResponse" + delete: + tags: + - metering-point-controller + operationId: deleteOwnMeteringPoint + parameters: + - name: id + in: path + required: true + schema: + type: string + format: uuid + responses: + "200": + description: OK /api/community/admin/memberships/{id}/reject: put: tags: @@ -148,11 +273,26 @@ paths: schema: type: string format: uuid + /api/auth/reset-password: + post: + tags: + - User Profile + summary: Passwort zurücksetzen + operationId: resetPassword + requestBody: + content: + application/json: + schema: + $ref: "#/components/schemas/ResetPasswordRequest" + required: true + responses: + "200": + description: OK /api/auth/register: post: tags: - Authentication - summary: Registriert einen neuen User inkl. Zählpunkt + summary: Registriert einen neuen User inkl. Zählpunkt description: Setzt den User auf Status PENDING und versendet eine Verifizierungs-E-Mail. operationId: register requestBody: @@ -169,7 +309,7 @@ paths: tags: - Authentication summary: User Login - description: "Gibt einen JWT zurück. Schlägt fehl, wenn Status noch PENDING\ + description: "Gibt einen JWT zurück. Schlägt fehl, wenn Status noch PENDING\ \ ist." operationId: login requestBody: @@ -185,12 +325,27 @@ paths: '*/*': schema: $ref: "#/components/schemas/AuthResponse" + /api/auth/forgot-password: + post: + tags: + - User Profile + summary: Passwort-Reset anfordern + operationId: forgotPassword + requestBody: + content: + application/json: + schema: + $ref: "#/components/schemas/ForgotPasswordRequest" + required: true + responses: + "200": + description: OK /api/admin/users/{userId}/reject: post: tags: - Admin IAM summary: User ablehnen - description: Lehnt den Antrag ab (z.B. weil Zählpunkt nicht ins Netz passt). + description: Lehnt den Antrag ab (z.B. weil Zählpunkt nicht ins Netz passt). operationId: rejectUser parameters: - name: userId @@ -250,6 +405,33 @@ paths: application/json: schema: $ref: "#/components/schemas/EnergyCommunityDto" + /api/notifications: + get: + tags: + - notification-controller + operationId: getNotifications + responses: + "200": + description: OK + content: + '*/*': + schema: + type: array + items: + $ref: "#/components/schemas/Notification" + /api/notifications/unread: + get: + tags: + - notification-controller + operationId: getUnreadCount + responses: + "200": + description: OK + content: + '*/*': + schema: + type: integer + format: int64 /api/dashboard/user: get: tags: @@ -309,6 +491,34 @@ paths: type: array items: $ref: "#/components/schemas/MeteringPointResponse" + /api/community/memberships/me: + get: + tags: + - membership-controller + operationId: getOwnMemberships + responses: + "200": + description: OK + content: + '*/*': + schema: + type: array + items: + $ref: "#/components/schemas/MembershipResponse" + /api/community/admin/memberships/pending: + get: + tags: + - membership-controller + operationId: getPendingMemberships + responses: + "200": + description: OK + content: + '*/*': + schema: + type: array + items: + $ref: "#/components/schemas/PendingMembershipResponse" /api/auth/verify-email: get: tags: @@ -327,7 +537,7 @@ paths: get: tags: - Admin IAM - summary: Lädt alle wartenden User + summary: Lädt alle wartenden User description: Zeigt User im Status PENDING inkl. der Ergebnisse des EDA-Netz-Checks. operationId: getPendingUsers responses: @@ -341,6 +551,126 @@ paths: $ref: "#/components/schemas/UserProfileResponse" components: schemas: + UpdateProfileRequest: + type: object + description: Payload für die Profilaktualisierung + properties: + firstName: + type: string + description: Vorname + example: Max + minLength: 1 + lastName: + type: string + description: Nachname + example: Mustermann + minLength: 1 + organizationName: + type: string + description: "Nur bei juristischer Person: Firmen-, Vereins- oder Gemeindename" + participantType: + type: string + description: Privatperson oder juristische Person + enum: + - PRIVATE + - COMPANY + example: PRIVATE + required: + - firstName + - lastName + - participantType + UserProfileResponse: + type: object + description: Repräsentation eines Users für das Admin-Dashboard + properties: + id: + type: string + format: uuid + participantType: + type: string + enum: + - PRIVATE + - COMPANY + firstName: + type: string + lastName: + type: string + organizationName: + type: string + email: + type: string + status: + type: string + ChangePasswordRequest: + type: object + description: Payload für die Passwortänderung + properties: + currentPassword: + type: string + description: Aktuelles Passwort + minLength: 1 + newPassword: + type: string + description: Neues Passwort + example: NeuesPasswort123! + maxLength: 2147483647 + minLength: 8 + required: + - currentPassword + - newPassword + ChangeEmailRequest: + type: object + description: Payload für die E-Mail-Änderung + properties: + newEmail: + type: string + format: email + description: Neue E-Mail-Adresse + example: neu@example.com + minLength: 1 + required: + - newEmail + UpdateMeteringPointRequest: + type: object + properties: + type: + type: string + enum: + - CONSUMER + - PRODUCER + - PROSUME + required: + - type + MeteringPointResponse: + type: object + properties: + id: + type: string + format: uuid + userId: + type: string + format: uuid + atNumber: + type: string + type: + type: string + enum: + - CONSUMER + - PRODUCER + - PROSUME + makoState: + type: string + enum: + - NEW + - WAITING_FOR_CONSENT + - CONSENT_GRANTED + - ACTIVE + - REJECTED + - ERROR + gridOperatorId: + type: string + ownerEmail: + type: string EnergyCommunityDto: type: object properties: @@ -381,36 +711,6 @@ components: required: - atNumber - type - MeteringPointResponse: - type: object - properties: - id: - type: string - format: uuid - userId: - type: string - format: uuid - atNumber: - type: string - type: - type: string - enum: - - CONSUMER - - PRODUCER - - PROSUME - makoState: - type: string - enum: - - NEW - - WAITING_FOR_CONSENT - - CONSENT_GRANTED - - ACTIVE - - REJECTED - - ERROR - gridOperatorId: - type: string - ownerEmail: - type: string MembershipRequest: type: object properties: @@ -429,9 +729,26 @@ components: - energyCommunityId - meteringPointId - priorityLevel + ResetPasswordRequest: + type: object + description: Payload für Passwort zurücksetzen + properties: + token: + type: string + description: Reset-Token + minLength: 1 + newPassword: + type: string + description: Neues Passwort + example: NeuesPasswort123! + maxLength: 2147483647 + minLength: 8 + required: + - newPassword + - token RegistrationRequest: type: object - description: Payload für die Registrierung eines neuen Users + description: Payload für die Registrierung eines neuen Users properties: participantType: type: string @@ -472,7 +789,7 @@ components: - password LoginRequest: type: object - description: Payload für den Login + description: Payload für den Login properties: email: type: string @@ -501,6 +818,43 @@ components: type: string description: Rolle des Users example: USER + ForgotPasswordRequest: + type: object + description: Payload für Passwort-Reset anfordern + properties: + email: + type: string + format: email + description: E-Mail-Adresse + example: max@example.com + minLength: 1 + required: + - email + Notification: + type: object + properties: + id: + type: string + format: uuid + userId: + type: string + format: uuid + title: + type: string + message: + type: string + type: + type: string + enum: + - INFO + - SUCCESS + - WARNING + - ERROR + read: + type: boolean + createdAt: + type: string + format: date-time UserStats: type: object properties: @@ -541,25 +895,51 @@ components: - BEG edaEcId: type: string - UserProfileResponse: + MembershipResponse: type: object - description: Repräsentation eines Users für das Admin-Dashboard properties: id: type: string format: uuid - participantType: + meteringPointId: + type: string + format: uuid + atNumber: + type: string + energyCommunityId: + type: string + format: uuid + communityName: + type: string + priorityLevel: + type: integer + format: int32 + status: + type: string + validFrom: + type: string + format: date + validTo: + type: string + format: date + PendingMembershipResponse: + type: object + properties: + id: + type: string + format: uuid + userEmail: type: string - enum: - - PRIVATE - - COMPANY firstName: type: string lastName: type: string - organizationName: + atNumber: type: string - email: + communityName: type: string + priorityLevel: + type: integer + format: int32 status: type: string