diff --git a/Dockerfile b/Dockerfile index 49a2055..66cd915 100644 --- a/Dockerfile +++ b/Dockerfile @@ -8,6 +8,7 @@ RUN CGO_ENABLED=0 GOOS=linux go build -o api cmd/api/external/main.go FROM gcr.io/distroless/static-debian12:nonroot AS final WORKDIR /app +COPY --from=build /src/external/docs /app/external/docs COPY --from=build /src/api /app/api CMD ["/app/api"] \ No newline at end of file diff --git a/Dockerfile_internal b/Dockerfile_internal deleted file mode 100644 index 5f07c2c..0000000 --- a/Dockerfile_internal +++ /dev/null @@ -1,13 +0,0 @@ -FROM golang:1.22-bookworm AS build - -WORKDIR /src -COPY ./ /src - -RUN CGO_ENABLED=0 GOOS=linux go build -o api cmd/api/internal/main.go - -FROM gcr.io/distroless/static-debian12:nonroot AS final - -WORKDIR /app -COPY --from=build /src/api /app/api - -CMD ["/app/api"] \ No newline at end of file diff --git a/external/docs/docs.go b/external/docs/docs.go index 46792c4..71277cb 100644 --- a/external/docs/docs.go +++ b/external/docs/docs.go @@ -226,6 +226,1193 @@ const docTemplate = `{ } } }, + "/facility/{FacilityID}/event-templates": { + "get": { + "description": "Get Event Templates by Facility", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "event" + ], + "summary": "Get Event Templates", + "parameters": [ + { + "type": "string", + "description": "Facility ID", + "name": "FacilityID", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/event.EventTemplateResponse" + } + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/utils.ErrResponse" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/utils.ErrResponse" + } + } + } + } + }, + "/facility/{FacilityID}/event-templates/{EventTemplateID}": { + "put": { + "description": "Update an Event Template", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "event" + ], + "summary": "Update an Event Template", + "parameters": [ + { + "type": "string", + "description": "Facility ID", + "name": "FacilityID", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "Event Template ID", + "name": "EventTemplateID", + "in": "path", + "required": true + }, + { + "description": "Event Template", + "name": "event", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/event.EventTemplateRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/event.EventTemplateResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/utils.ErrResponse" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/utils.ErrResponse" + } + } + } + }, + "delete": { + "description": "Delete an Event Template", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "event" + ], + "summary": "Delete an Event Template", + "parameters": [ + { + "type": "string", + "description": "Facility ID", + "name": "FacilityID", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "Event Template ID", + "name": "EventTemplateID", + "in": "path", + "required": true + } + ], + "responses": { + "204": { + "description": "No Content" + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/utils.ErrResponse" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/utils.ErrResponse" + } + } + } + } + }, + "/facility/{FacilityID}/events": { + "get": { + "description": "Get Events by Facility", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "event" + ], + "summary": "Get Events", + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/event.EventResponse" + } + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/utils.ErrResponse" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/utils.ErrResponse" + } + } + } + }, + "post": { + "description": "Create an Event", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "event" + ], + "summary": "Create an Event", + "parameters": [ + { + "description": "Event", + "name": "event", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/event.EventRequest" + } + } + ], + "responses": { + "201": { + "description": "Created", + "schema": { + "$ref": "#/definitions/event.EventResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/utils.ErrResponse" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/utils.ErrResponse" + } + } + } + } + }, + "/facility/{FacilityID}/events/{EventID}": { + "get": { + "description": "Get Event by ID", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "event" + ], + "summary": "Get Event", + "parameters": [ + { + "type": "string", + "description": "Event ID", + "name": "EventID", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/event.EventResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/utils.ErrResponse" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/utils.ErrResponse" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/utils.ErrResponse" + } + } + } + }, + "put": { + "description": "Update Event by ID", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "event" + ], + "summary": "Update Event", + "parameters": [ + { + "type": "string", + "description": "Event ID", + "name": "EventID", + "in": "path", + "required": true + }, + { + "description": "Event", + "name": "event", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/event.EventRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/event.EventResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/utils.ErrResponse" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/utils.ErrResponse" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/utils.ErrResponse" + } + } + } + }, + "delete": { + "description": "Delete Event by ID", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "event" + ], + "summary": "Delete Event", + "parameters": [ + { + "type": "string", + "description": "Event ID", + "name": "EventID", + "in": "path", + "required": true + } + ], + "responses": { + "204": { + "description": "No Content" + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/utils.ErrResponse" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/utils.ErrResponse" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/utils.ErrResponse" + } + } + } + }, + "patch": { + "description": "Patch Event by ID", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "event" + ], + "summary": "Patch Event", + "parameters": [ + { + "type": "string", + "description": "Event ID", + "name": "EventID", + "in": "path", + "required": true + }, + { + "description": "Event", + "name": "event", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/event.EventRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/event.EventResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/utils.ErrResponse" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/utils.ErrResponse" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/utils.ErrResponse" + } + } + } + } + }, + "/facility/{FacilityID}/events/{EventID}/positions": { + "get": { + "description": "Get Event Positions", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "event" + ], + "summary": "Get Event Positions", + "parameters": [ + { + "type": "string", + "description": "Facility ID", + "name": "FacilityID", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "Event ID", + "name": "EventID", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/event.EventPositionResponse" + } + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/utils.ErrResponse" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/utils.ErrResponse" + } + } + } + }, + "post": { + "description": "Create an Event Position", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "event" + ], + "summary": "Create an Event Position", + "parameters": [ + { + "type": "string", + "description": "Facility ID", + "name": "FacilityID", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "Event ID", + "name": "EventID", + "in": "path", + "required": true + }, + { + "description": "Event Position", + "name": "event", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/event.EventPositionRequest" + } + } + ], + "responses": { + "201": { + "description": "Created", + "schema": { + "$ref": "#/definitions/event.EventPositionResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/utils.ErrResponse" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/utils.ErrResponse" + } + } + } + } + }, + "/facility/{FacilityID}/events/{EventID}/positions/{EventPositionID}": { + "get": { + "description": "Get Event Position", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "event" + ], + "summary": "Get Event Position", + "parameters": [ + { + "type": "string", + "description": "Facility ID", + "name": "FacilityID", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "Event ID", + "name": "EventID", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "Event Position ID", + "name": "EventPositionID", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/event.EventPositionResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/utils.ErrResponse" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/utils.ErrResponse" + } + } + } + }, + "delete": { + "description": "Delete an Event Position", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "event" + ], + "summary": "Delete an Event Position", + "parameters": [ + { + "type": "string", + "description": "Facility ID", + "name": "FacilityID", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "Event ID", + "name": "EventID", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "Event Position ID", + "name": "EventPositionID", + "in": "path", + "required": true + } + ], + "responses": { + "204": { + "description": "No Content" + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/utils.ErrResponse" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/utils.ErrResponse" + } + } + } + }, + "patch": { + "description": "Patch an Event Position", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "event" + ], + "summary": "Patch an Event Position", + "parameters": [ + { + "type": "string", + "description": "Facility ID", + "name": "FacilityID", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "Event ID", + "name": "EventID", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "Event Position ID", + "name": "EventPositionID", + "in": "path", + "required": true + }, + { + "description": "Event Position", + "name": "event", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/event.EventPositionRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/event.EventPositionResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/utils.ErrResponse" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/utils.ErrResponse" + } + } + } + } + }, + "/facility/{FacilityID}/events/{EventID}/routing": { + "get": { + "description": "Get Event Routing", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "event" + ], + "summary": "Get Event Routing", + "parameters": [ + { + "type": "string", + "description": "Facility ID", + "name": "FacilityID", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "Event ID", + "name": "EventID", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/event.EventRoutingResponse" + } + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/utils.ErrResponse" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/utils.ErrResponse" + } + } + } + }, + "post": { + "description": "Create an Event Routing", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "event" + ], + "summary": "Create an Event Routing", + "parameters": [ + { + "type": "string", + "description": "Facility ID", + "name": "FacilityID", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "Event ID", + "name": "EventID", + "in": "path", + "required": true + }, + { + "description": "Event Routing", + "name": "event", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/event.EventRoutingRequest" + } + } + ], + "responses": { + "201": { + "description": "Created", + "schema": { + "$ref": "#/definitions/event.EventRoutingResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/utils.ErrResponse" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/utils.ErrResponse" + } + } + } + } + }, + "/facility/{FacilityID}/events/{EventID}/routing/{EventRoutingID}": { + "delete": { + "description": "Delete an Event Routing", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "event" + ], + "summary": "Delete an Event Routing", + "parameters": [ + { + "type": "string", + "description": "Facility ID", + "name": "FacilityID", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "Event ID", + "name": "EventID", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "Event Routing ID", + "name": "EventRoutingID", + "in": "path", + "required": true + } + ], + "responses": { + "204": { + "description": "No Content" + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/utils.ErrResponse" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/utils.ErrResponse" + } + } + } + }, + "patch": { + "description": "Patch an Event Routing", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "event" + ], + "summary": "Patch an Event Routing", + "parameters": [ + { + "type": "string", + "description": "Facility ID", + "name": "FacilityID", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "Event ID", + "name": "EventID", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "Event Routing ID", + "name": "EventRoutingID", + "in": "path", + "required": true + }, + { + "description": "Event Routing", + "name": "event", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/event.EventRoutingRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/event.EventRoutingResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/utils.ErrResponse" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/utils.ErrResponse" + } + } + } + } + }, + "/facility/{FacilityID}/events/{EventID}/signups": { + "get": { + "description": "Get Event Signups", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "event" + ], + "summary": "Get Event Signups", + "parameters": [ + { + "type": "string", + "description": "Facility ID", + "name": "FacilityID", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "Event ID", + "name": "EventID", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/event.EventSignupResponse" + } + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/utils.ErrResponse" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/utils.ErrResponse" + } + } + } + }, + "post": { + "description": "Create an Event Signup", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "event" + ], + "summary": "Create an Event Signup", + "parameters": [ + { + "type": "string", + "description": "Facility ID", + "name": "FacilityID", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "Event ID", + "name": "EventID", + "in": "path", + "required": true + }, + { + "description": "Event Signup", + "name": "event", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/event.EventSignupRequest" + } + } + ], + "responses": { + "201": { + "description": "Created", + "schema": { + "$ref": "#/definitions/event.EventSignupResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/utils.ErrResponse" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/utils.ErrResponse" + } + } + } + } + }, + "/facility/{FacilityID}/events/{EventID}/signups/{EventSignupID}": { + "get": { + "description": "Get an Event Signup", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "event" + ], + "summary": "Get an Event Signup", + "parameters": [ + { + "type": "string", + "description": "Facility ID", + "name": "FacilityID", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "Event ID", + "name": "EventID", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "Event Signup ID", + "name": "EventSignupID", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/event.EventSignupResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/utils.ErrResponse" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/utils.ErrResponse" + } + } + } + }, + "delete": { + "description": "Delete an Event Signup", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "event" + ], + "summary": "Delete an Event Signup", + "parameters": [ + { + "type": "string", + "description": "Facility ID", + "name": "FacilityID", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "Event ID", + "name": "EventID", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "Event Signup ID", + "name": "EventSignupID", + "in": "path", + "required": true + } + ], + "responses": { + "204": { + "description": "No Content" + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/utils.ErrResponse" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/utils.ErrResponse" + } + } + } + } + }, "/facility/{FacilityID}/faq": { "get": { "description": "List all FAQs", @@ -1824,6 +3011,47 @@ const docTemplate = `{ } } }, + "/user/discord/unlink": { + "get": { + "description": "Unlink your Discord account from your VATUSA account", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "discord" + ], + "summary": "Unlink your Discord account", + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/user.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/utils.ErrResponse" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/utils.ErrResponse" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/utils.ErrResponse" + } + } + } + } + }, "/user/{cid}": { "get": { "description": "Get information for the user", @@ -3751,23 +4979,348 @@ const docTemplate = `{ "EmailUser" ] }, - "disciplinary_log.Request": { + "disciplinary_log.Request": { + "type": "object", + "required": [ + "entry" + ], + "properties": { + "entry": { + "type": "string", + "example": "Misconduct in discord" + }, + "vatusa_only": { + "type": "boolean", + "example": true + } + } + }, + "disciplinary_log.Response": { + "type": "object", + "properties": { + "cid": { + "type": "integer", + "example": 1293257 + }, + "created_at": { + "type": "string", + "example": "2021-01-01T00:00:00Z" + }, + "created_by": { + "type": "string", + "example": "'1234567' or 'System'" + }, + "entry": { + "type": "string", + "example": "Changed Preferred OIs to RP" + }, + "id": { + "type": "integer", + "example": 1 + }, + "updated_at": { + "type": "string", + "example": "2021-01-01T00:00:00Z" + }, + "updated_by": { + "type": "string", + "example": "'1234567' or 'System'" + }, + "vatusa_only": { + "type": "boolean", + "example": true + } + } + }, + "event.EventPositionRequest": { + "type": "object", + "required": [ + "position" + ], + "properties": { + "assignee": { + "type": "integer", + "example": 1293257 + }, + "position": { + "type": "string", + "example": "ZDV_APP" + }, + "secondary_assignee": { + "type": "integer", + "example": 1293257 + }, + "shifts": { + "type": "boolean", + "example": true + } + } + }, + "event.EventPositionResponse": { + "type": "object", + "properties": { + "assignee": { + "type": "integer", + "example": 1293257 + }, + "created_at": { + "type": "string", + "example": "2021-01-01T00:00:00Z" + }, + "event_id": { + "type": "integer", + "example": 1 + }, + "facility": { + "allOf": [ + { + "$ref": "#/definitions/constants.FacilityID" + } + ], + "example": "ZDV" + }, + "id": { + "type": "integer", + "example": 1 + }, + "position": { + "type": "string", + "example": "ZDV_APP" + }, + "secondary_assignee": { + "type": "integer", + "example": 1293257 + }, + "shifts": { + "type": "boolean", + "example": true + }, + "signups": { + "type": "array", + "items": { + "$ref": "#/definitions/models.EventSignup" + } + }, + "updated_at": { + "type": "string", + "example": "2021-01-01T00:00:00Z" + } + } + }, + "event.EventRequest": { + "type": "object", + "required": [ + "description", + "end_date", + "facilities", + "fields", + "start_date", + "title" + ], + "properties": { + "banner_url": { + "type": "string", + "example": "https://zdvartcc.org/banner.jpg" + }, + "description": { + "type": "string", + "example": "Join us for a fun night of flying in and out of Denver!" + }, + "end_date": { + "type": "string", + "example": "2021-01-01T00:00:00Z" + }, + "facilities": { + "type": "array", + "items": { + "$ref": "#/definitions/constants.FacilityID" + }, + "example": [ + "[\"ZDV\"", + " \"ZAB\"", + " \"ZLC\"]" + ] + }, + "fields": { + "type": "array", + "items": { + "type": "string" + }, + "example": [ + "[\"KDEN\"", + " \"KBJC\"", + " \"KAPA\"]" + ] + }, + "start_date": { + "type": "string", + "example": "2021-01-01T00:00:00Z" + }, + "title": { + "type": "string", + "example": "ZDV FNO" + } + } + }, + "event.EventResponse": { + "type": "object", + "properties": { + "banner_url": { + "type": "string", + "example": "https://zdvartcc.org/banner.jpg" + }, + "created_at": { + "type": "string", + "example": "2021-01-01T00:00:00Z" + }, + "description": { + "type": "string", + "example": "Join us for a fun night of flying in and out of Denver!" + }, + "end_date": { + "type": "string", + "example": "2021-01-01T00:00:00Z" + }, + "facilities": { + "type": "array", + "items": { + "$ref": "#/definitions/constants.FacilityID" + }, + "example": [ + "[\"ZDV\"", + " \"ZAB\"", + " \"ZLC\"]" + ] + }, + "fields": { + "type": "array", + "items": { + "type": "string" + }, + "example": [ + "[\"KDEN\"", + " \"KBJC\"", + " \"KAPA\"]" + ] + }, + "id": { + "type": "integer", + "example": 1 + }, + "positions": { + "type": "array", + "items": { + "$ref": "#/definitions/models.EventPosition" + } + }, + "routing": { + "type": "array", + "items": { + "$ref": "#/definitions/models.EventRouting" + } + }, + "start_date": { + "type": "string", + "example": "2021-01-01T00:00:00Z" + }, + "title": { + "type": "string", + "example": "ZDV FNO" + }, + "updated_at": { + "type": "string", + "example": "2021-01-01T00:00:00Z" + } + } + }, + "event.EventRoutingRequest": { "type": "object", "required": [ - "entry" + "destination", + "notes", + "origin", + "routing" ], "properties": { - "entry": { + "destination": { "type": "string", - "example": "Misconduct in discord" + "example": "ZAB" }, - "vatusa_only": { - "type": "boolean", - "example": true + "notes": { + "type": "string", + "example": "Expect vectors to final at DBL" + }, + "origin": { + "type": "string", + "example": "ZDV" + }, + "routing": { + "type": "string", + "example": "ZDV J80 DBL J80 FQF J80 HCT J80 HBU J80 HCT J80 FQF J80 DBL J80 ZAB" } } }, - "disciplinary_log.Response": { + "event.EventRoutingResponse": { + "type": "object", + "properties": { + "created_at": { + "type": "string", + "example": "2021-01-01T00:00:00Z" + }, + "destination": { + "type": "string", + "example": "KDEN" + }, + "event_id": { + "type": "integer", + "example": 1 + }, + "id": { + "type": "integer", + "example": 1 + }, + "notes": { + "type": "string", + "example": "JETS ONLY" + }, + "origin": { + "type": "string", + "example": "KLAX" + }, + "routing": { + "type": "string", + "example": "DCT" + }, + "updated_at": { + "type": "string", + "example": "2021-01-01T00:00:00Z" + } + } + }, + "event.EventSignupRequest": { + "type": "object", + "required": [ + "cid", + "position_id", + "shift" + ], + "properties": { + "cid": { + "type": "integer", + "example": 1293257 + }, + "position_id": { + "type": "integer", + "example": 1 + }, + "shift": { + "description": "1 = Primary, 2 = Secondary", + "type": "integer", + "example": 1 + } + } + }, + "event.EventSignupResponse": { "type": "object", "properties": { "cid": { @@ -3778,29 +5331,134 @@ const docTemplate = `{ "type": "string", "example": "2021-01-01T00:00:00Z" }, - "created_by": { - "type": "string", - "example": "'1234567' or 'System'" - }, - "entry": { - "type": "string", - "example": "Changed Preferred OIs to RP" + "event_id": { + "type": "integer", + "example": 1 }, "id": { "type": "integer", "example": 1 }, + "position_id": { + "type": "integer", + "example": 1 + }, + "shift": { + "description": "1 = Primary, 2 = Secondary", + "type": "integer", + "example": 1 + }, "updated_at": { "type": "string", "example": "2021-01-01T00:00:00Z" + } + } + }, + "event.EventTemplateRequest": { + "type": "object", + "required": [ + "facilities", + "fields", + "positions", + "title" + ], + "properties": { + "facilities": { + "type": "array", + "items": { + "$ref": "#/definitions/constants.FacilityID" + }, + "example": [ + "[\"ZDV\"", + " \"ZAB\"", + " \"ZLC\"]" + ] }, - "updated_by": { + "fields": { + "type": "array", + "items": { + "type": "string" + }, + "example": [ + "[\"KDEN\"", + " \"KBJC\"", + " \"KAPA\"]" + ] + }, + "positions": { + "type": "array", + "items": { + "type": "string" + }, + "example": [ + "[\"ZDV_APP\"", + " \"ZDV_TWR\"]" + ] + }, + "shifts": { + "type": "boolean", + "example": true + }, + "title": { "type": "string", - "example": "'1234567' or 'System'" + "example": "KDEN FNO" + } + } + }, + "event.EventTemplateResponse": { + "type": "object", + "properties": { + "created_at": { + "type": "string", + "example": "2021-01-01T00:00:00Z" }, - "vatusa_only": { + "facilities": { + "type": "array", + "items": { + "$ref": "#/definitions/constants.FacilityID" + }, + "example": [ + "[\"ZDV\"", + " \"ZAB\"", + " \"ZLC\"]" + ] + }, + "fields": { + "type": "array", + "items": { + "type": "string" + }, + "example": [ + "[\"KDEN\"", + " \"KBJC\"", + " \"KAPA\"]" + ] + }, + "id": { + "type": "integer", + "example": 1 + }, + "positions": { + "type": "array", + "items": { + "type": "string" + }, + "example": [ + "[\"ZDV_APP\"", + " \"ZDV_TWR\"]" + ] + }, + "shifts": { "type": "boolean", "example": true + }, + "title": { + "type": "string", + "example": "KDEN FNO Template" + }, + "updated_at": { + "type": "string", + "example": "2021-01-01T00:00:00Z" } } }, @@ -4092,6 +5750,128 @@ const docTemplate = `{ } } }, + "models.EventPosition": { + "type": "object", + "properties": { + "assignee": { + "type": "integer", + "example": 1293257 + }, + "created_at": { + "type": "string", + "example": "2021-01-01T00:00:00Z" + }, + "event_id": { + "type": "integer", + "example": 1 + }, + "facility": { + "allOf": [ + { + "$ref": "#/definitions/constants.FacilityID" + } + ], + "example": "ZDV" + }, + "id": { + "type": "integer", + "example": 1 + }, + "position": { + "type": "string", + "example": "ZDV_APP" + }, + "secondary_assignee": { + "type": "integer", + "example": 1293257 + }, + "shifts": { + "type": "boolean", + "example": true + }, + "signups": { + "type": "array", + "items": { + "$ref": "#/definitions/models.EventSignup" + } + }, + "updated_at": { + "type": "string", + "example": "2021-01-01T00:00:00Z" + } + } + }, + "models.EventRouting": { + "type": "object", + "properties": { + "created_at": { + "type": "string", + "example": "2021-01-01T00:00:00Z" + }, + "destination": { + "type": "string", + "example": "KDEN" + }, + "event_id": { + "type": "integer", + "example": 1 + }, + "id": { + "type": "integer", + "example": 1 + }, + "notes": { + "type": "string", + "example": "JETS ONLY" + }, + "origin": { + "type": "string", + "example": "KLAX" + }, + "routing": { + "type": "string", + "example": "DCT" + }, + "updated_at": { + "type": "string", + "example": "2021-01-01T00:00:00Z" + } + } + }, + "models.EventSignup": { + "type": "object", + "properties": { + "cid": { + "type": "integer", + "example": 1293257 + }, + "created_at": { + "type": "string", + "example": "2021-01-01T00:00:00Z" + }, + "event_id": { + "type": "integer", + "example": 1 + }, + "id": { + "type": "integer", + "example": 1 + }, + "position_id": { + "type": "integer", + "example": 1 + }, + "shift": { + "description": "1 = Primary, 2 = Secondary", + "type": "integer", + "example": 1 + }, + "updated_at": { + "type": "string", + "example": "2021-01-01T00:00:00Z" + } + } + }, "models.UserFlag": { "type": "object", "properties": { @@ -4788,6 +6568,13 @@ const docTemplate = `{ }, "user_notification.Request": { "type": "object", + "required": [ + "discord", + "email", + "events", + "feedback", + "training" + ], "properties": { "discord": { "type": "boolean", diff --git a/external/docs/swagger.json b/external/docs/swagger.json index 2423737..1e97120 100644 --- a/external/docs/swagger.json +++ b/external/docs/swagger.json @@ -219,6 +219,1193 @@ } } }, + "/facility/{FacilityID}/event-templates": { + "get": { + "description": "Get Event Templates by Facility", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "event" + ], + "summary": "Get Event Templates", + "parameters": [ + { + "type": "string", + "description": "Facility ID", + "name": "FacilityID", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/event.EventTemplateResponse" + } + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/utils.ErrResponse" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/utils.ErrResponse" + } + } + } + } + }, + "/facility/{FacilityID}/event-templates/{EventTemplateID}": { + "put": { + "description": "Update an Event Template", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "event" + ], + "summary": "Update an Event Template", + "parameters": [ + { + "type": "string", + "description": "Facility ID", + "name": "FacilityID", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "Event Template ID", + "name": "EventTemplateID", + "in": "path", + "required": true + }, + { + "description": "Event Template", + "name": "event", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/event.EventTemplateRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/event.EventTemplateResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/utils.ErrResponse" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/utils.ErrResponse" + } + } + } + }, + "delete": { + "description": "Delete an Event Template", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "event" + ], + "summary": "Delete an Event Template", + "parameters": [ + { + "type": "string", + "description": "Facility ID", + "name": "FacilityID", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "Event Template ID", + "name": "EventTemplateID", + "in": "path", + "required": true + } + ], + "responses": { + "204": { + "description": "No Content" + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/utils.ErrResponse" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/utils.ErrResponse" + } + } + } + } + }, + "/facility/{FacilityID}/events": { + "get": { + "description": "Get Events by Facility", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "event" + ], + "summary": "Get Events", + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/event.EventResponse" + } + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/utils.ErrResponse" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/utils.ErrResponse" + } + } + } + }, + "post": { + "description": "Create an Event", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "event" + ], + "summary": "Create an Event", + "parameters": [ + { + "description": "Event", + "name": "event", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/event.EventRequest" + } + } + ], + "responses": { + "201": { + "description": "Created", + "schema": { + "$ref": "#/definitions/event.EventResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/utils.ErrResponse" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/utils.ErrResponse" + } + } + } + } + }, + "/facility/{FacilityID}/events/{EventID}": { + "get": { + "description": "Get Event by ID", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "event" + ], + "summary": "Get Event", + "parameters": [ + { + "type": "string", + "description": "Event ID", + "name": "EventID", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/event.EventResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/utils.ErrResponse" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/utils.ErrResponse" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/utils.ErrResponse" + } + } + } + }, + "put": { + "description": "Update Event by ID", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "event" + ], + "summary": "Update Event", + "parameters": [ + { + "type": "string", + "description": "Event ID", + "name": "EventID", + "in": "path", + "required": true + }, + { + "description": "Event", + "name": "event", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/event.EventRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/event.EventResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/utils.ErrResponse" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/utils.ErrResponse" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/utils.ErrResponse" + } + } + } + }, + "delete": { + "description": "Delete Event by ID", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "event" + ], + "summary": "Delete Event", + "parameters": [ + { + "type": "string", + "description": "Event ID", + "name": "EventID", + "in": "path", + "required": true + } + ], + "responses": { + "204": { + "description": "No Content" + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/utils.ErrResponse" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/utils.ErrResponse" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/utils.ErrResponse" + } + } + } + }, + "patch": { + "description": "Patch Event by ID", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "event" + ], + "summary": "Patch Event", + "parameters": [ + { + "type": "string", + "description": "Event ID", + "name": "EventID", + "in": "path", + "required": true + }, + { + "description": "Event", + "name": "event", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/event.EventRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/event.EventResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/utils.ErrResponse" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/utils.ErrResponse" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/utils.ErrResponse" + } + } + } + } + }, + "/facility/{FacilityID}/events/{EventID}/positions": { + "get": { + "description": "Get Event Positions", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "event" + ], + "summary": "Get Event Positions", + "parameters": [ + { + "type": "string", + "description": "Facility ID", + "name": "FacilityID", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "Event ID", + "name": "EventID", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/event.EventPositionResponse" + } + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/utils.ErrResponse" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/utils.ErrResponse" + } + } + } + }, + "post": { + "description": "Create an Event Position", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "event" + ], + "summary": "Create an Event Position", + "parameters": [ + { + "type": "string", + "description": "Facility ID", + "name": "FacilityID", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "Event ID", + "name": "EventID", + "in": "path", + "required": true + }, + { + "description": "Event Position", + "name": "event", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/event.EventPositionRequest" + } + } + ], + "responses": { + "201": { + "description": "Created", + "schema": { + "$ref": "#/definitions/event.EventPositionResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/utils.ErrResponse" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/utils.ErrResponse" + } + } + } + } + }, + "/facility/{FacilityID}/events/{EventID}/positions/{EventPositionID}": { + "get": { + "description": "Get Event Position", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "event" + ], + "summary": "Get Event Position", + "parameters": [ + { + "type": "string", + "description": "Facility ID", + "name": "FacilityID", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "Event ID", + "name": "EventID", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "Event Position ID", + "name": "EventPositionID", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/event.EventPositionResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/utils.ErrResponse" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/utils.ErrResponse" + } + } + } + }, + "delete": { + "description": "Delete an Event Position", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "event" + ], + "summary": "Delete an Event Position", + "parameters": [ + { + "type": "string", + "description": "Facility ID", + "name": "FacilityID", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "Event ID", + "name": "EventID", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "Event Position ID", + "name": "EventPositionID", + "in": "path", + "required": true + } + ], + "responses": { + "204": { + "description": "No Content" + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/utils.ErrResponse" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/utils.ErrResponse" + } + } + } + }, + "patch": { + "description": "Patch an Event Position", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "event" + ], + "summary": "Patch an Event Position", + "parameters": [ + { + "type": "string", + "description": "Facility ID", + "name": "FacilityID", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "Event ID", + "name": "EventID", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "Event Position ID", + "name": "EventPositionID", + "in": "path", + "required": true + }, + { + "description": "Event Position", + "name": "event", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/event.EventPositionRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/event.EventPositionResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/utils.ErrResponse" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/utils.ErrResponse" + } + } + } + } + }, + "/facility/{FacilityID}/events/{EventID}/routing": { + "get": { + "description": "Get Event Routing", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "event" + ], + "summary": "Get Event Routing", + "parameters": [ + { + "type": "string", + "description": "Facility ID", + "name": "FacilityID", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "Event ID", + "name": "EventID", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/event.EventRoutingResponse" + } + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/utils.ErrResponse" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/utils.ErrResponse" + } + } + } + }, + "post": { + "description": "Create an Event Routing", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "event" + ], + "summary": "Create an Event Routing", + "parameters": [ + { + "type": "string", + "description": "Facility ID", + "name": "FacilityID", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "Event ID", + "name": "EventID", + "in": "path", + "required": true + }, + { + "description": "Event Routing", + "name": "event", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/event.EventRoutingRequest" + } + } + ], + "responses": { + "201": { + "description": "Created", + "schema": { + "$ref": "#/definitions/event.EventRoutingResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/utils.ErrResponse" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/utils.ErrResponse" + } + } + } + } + }, + "/facility/{FacilityID}/events/{EventID}/routing/{EventRoutingID}": { + "delete": { + "description": "Delete an Event Routing", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "event" + ], + "summary": "Delete an Event Routing", + "parameters": [ + { + "type": "string", + "description": "Facility ID", + "name": "FacilityID", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "Event ID", + "name": "EventID", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "Event Routing ID", + "name": "EventRoutingID", + "in": "path", + "required": true + } + ], + "responses": { + "204": { + "description": "No Content" + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/utils.ErrResponse" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/utils.ErrResponse" + } + } + } + }, + "patch": { + "description": "Patch an Event Routing", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "event" + ], + "summary": "Patch an Event Routing", + "parameters": [ + { + "type": "string", + "description": "Facility ID", + "name": "FacilityID", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "Event ID", + "name": "EventID", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "Event Routing ID", + "name": "EventRoutingID", + "in": "path", + "required": true + }, + { + "description": "Event Routing", + "name": "event", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/event.EventRoutingRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/event.EventRoutingResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/utils.ErrResponse" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/utils.ErrResponse" + } + } + } + } + }, + "/facility/{FacilityID}/events/{EventID}/signups": { + "get": { + "description": "Get Event Signups", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "event" + ], + "summary": "Get Event Signups", + "parameters": [ + { + "type": "string", + "description": "Facility ID", + "name": "FacilityID", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "Event ID", + "name": "EventID", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/event.EventSignupResponse" + } + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/utils.ErrResponse" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/utils.ErrResponse" + } + } + } + }, + "post": { + "description": "Create an Event Signup", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "event" + ], + "summary": "Create an Event Signup", + "parameters": [ + { + "type": "string", + "description": "Facility ID", + "name": "FacilityID", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "Event ID", + "name": "EventID", + "in": "path", + "required": true + }, + { + "description": "Event Signup", + "name": "event", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/event.EventSignupRequest" + } + } + ], + "responses": { + "201": { + "description": "Created", + "schema": { + "$ref": "#/definitions/event.EventSignupResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/utils.ErrResponse" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/utils.ErrResponse" + } + } + } + } + }, + "/facility/{FacilityID}/events/{EventID}/signups/{EventSignupID}": { + "get": { + "description": "Get an Event Signup", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "event" + ], + "summary": "Get an Event Signup", + "parameters": [ + { + "type": "string", + "description": "Facility ID", + "name": "FacilityID", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "Event ID", + "name": "EventID", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "Event Signup ID", + "name": "EventSignupID", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/event.EventSignupResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/utils.ErrResponse" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/utils.ErrResponse" + } + } + } + }, + "delete": { + "description": "Delete an Event Signup", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "event" + ], + "summary": "Delete an Event Signup", + "parameters": [ + { + "type": "string", + "description": "Facility ID", + "name": "FacilityID", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "Event ID", + "name": "EventID", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "Event Signup ID", + "name": "EventSignupID", + "in": "path", + "required": true + } + ], + "responses": { + "204": { + "description": "No Content" + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/utils.ErrResponse" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/utils.ErrResponse" + } + } + } + } + }, "/facility/{FacilityID}/faq": { "get": { "description": "List all FAQs", @@ -1817,6 +3004,47 @@ } } }, + "/user/discord/unlink": { + "get": { + "description": "Unlink your Discord account from your VATUSA account", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "discord" + ], + "summary": "Unlink your Discord account", + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/user.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/utils.ErrResponse" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/utils.ErrResponse" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/utils.ErrResponse" + } + } + } + } + }, "/user/{cid}": { "get": { "description": "Get information for the user", @@ -3744,23 +4972,348 @@ "EmailUser" ] }, - "disciplinary_log.Request": { + "disciplinary_log.Request": { + "type": "object", + "required": [ + "entry" + ], + "properties": { + "entry": { + "type": "string", + "example": "Misconduct in discord" + }, + "vatusa_only": { + "type": "boolean", + "example": true + } + } + }, + "disciplinary_log.Response": { + "type": "object", + "properties": { + "cid": { + "type": "integer", + "example": 1293257 + }, + "created_at": { + "type": "string", + "example": "2021-01-01T00:00:00Z" + }, + "created_by": { + "type": "string", + "example": "'1234567' or 'System'" + }, + "entry": { + "type": "string", + "example": "Changed Preferred OIs to RP" + }, + "id": { + "type": "integer", + "example": 1 + }, + "updated_at": { + "type": "string", + "example": "2021-01-01T00:00:00Z" + }, + "updated_by": { + "type": "string", + "example": "'1234567' or 'System'" + }, + "vatusa_only": { + "type": "boolean", + "example": true + } + } + }, + "event.EventPositionRequest": { + "type": "object", + "required": [ + "position" + ], + "properties": { + "assignee": { + "type": "integer", + "example": 1293257 + }, + "position": { + "type": "string", + "example": "ZDV_APP" + }, + "secondary_assignee": { + "type": "integer", + "example": 1293257 + }, + "shifts": { + "type": "boolean", + "example": true + } + } + }, + "event.EventPositionResponse": { + "type": "object", + "properties": { + "assignee": { + "type": "integer", + "example": 1293257 + }, + "created_at": { + "type": "string", + "example": "2021-01-01T00:00:00Z" + }, + "event_id": { + "type": "integer", + "example": 1 + }, + "facility": { + "allOf": [ + { + "$ref": "#/definitions/constants.FacilityID" + } + ], + "example": "ZDV" + }, + "id": { + "type": "integer", + "example": 1 + }, + "position": { + "type": "string", + "example": "ZDV_APP" + }, + "secondary_assignee": { + "type": "integer", + "example": 1293257 + }, + "shifts": { + "type": "boolean", + "example": true + }, + "signups": { + "type": "array", + "items": { + "$ref": "#/definitions/models.EventSignup" + } + }, + "updated_at": { + "type": "string", + "example": "2021-01-01T00:00:00Z" + } + } + }, + "event.EventRequest": { + "type": "object", + "required": [ + "description", + "end_date", + "facilities", + "fields", + "start_date", + "title" + ], + "properties": { + "banner_url": { + "type": "string", + "example": "https://zdvartcc.org/banner.jpg" + }, + "description": { + "type": "string", + "example": "Join us for a fun night of flying in and out of Denver!" + }, + "end_date": { + "type": "string", + "example": "2021-01-01T00:00:00Z" + }, + "facilities": { + "type": "array", + "items": { + "$ref": "#/definitions/constants.FacilityID" + }, + "example": [ + "[\"ZDV\"", + " \"ZAB\"", + " \"ZLC\"]" + ] + }, + "fields": { + "type": "array", + "items": { + "type": "string" + }, + "example": [ + "[\"KDEN\"", + " \"KBJC\"", + " \"KAPA\"]" + ] + }, + "start_date": { + "type": "string", + "example": "2021-01-01T00:00:00Z" + }, + "title": { + "type": "string", + "example": "ZDV FNO" + } + } + }, + "event.EventResponse": { + "type": "object", + "properties": { + "banner_url": { + "type": "string", + "example": "https://zdvartcc.org/banner.jpg" + }, + "created_at": { + "type": "string", + "example": "2021-01-01T00:00:00Z" + }, + "description": { + "type": "string", + "example": "Join us for a fun night of flying in and out of Denver!" + }, + "end_date": { + "type": "string", + "example": "2021-01-01T00:00:00Z" + }, + "facilities": { + "type": "array", + "items": { + "$ref": "#/definitions/constants.FacilityID" + }, + "example": [ + "[\"ZDV\"", + " \"ZAB\"", + " \"ZLC\"]" + ] + }, + "fields": { + "type": "array", + "items": { + "type": "string" + }, + "example": [ + "[\"KDEN\"", + " \"KBJC\"", + " \"KAPA\"]" + ] + }, + "id": { + "type": "integer", + "example": 1 + }, + "positions": { + "type": "array", + "items": { + "$ref": "#/definitions/models.EventPosition" + } + }, + "routing": { + "type": "array", + "items": { + "$ref": "#/definitions/models.EventRouting" + } + }, + "start_date": { + "type": "string", + "example": "2021-01-01T00:00:00Z" + }, + "title": { + "type": "string", + "example": "ZDV FNO" + }, + "updated_at": { + "type": "string", + "example": "2021-01-01T00:00:00Z" + } + } + }, + "event.EventRoutingRequest": { "type": "object", "required": [ - "entry" + "destination", + "notes", + "origin", + "routing" ], "properties": { - "entry": { + "destination": { "type": "string", - "example": "Misconduct in discord" + "example": "ZAB" }, - "vatusa_only": { - "type": "boolean", - "example": true + "notes": { + "type": "string", + "example": "Expect vectors to final at DBL" + }, + "origin": { + "type": "string", + "example": "ZDV" + }, + "routing": { + "type": "string", + "example": "ZDV J80 DBL J80 FQF J80 HCT J80 HBU J80 HCT J80 FQF J80 DBL J80 ZAB" } } }, - "disciplinary_log.Response": { + "event.EventRoutingResponse": { + "type": "object", + "properties": { + "created_at": { + "type": "string", + "example": "2021-01-01T00:00:00Z" + }, + "destination": { + "type": "string", + "example": "KDEN" + }, + "event_id": { + "type": "integer", + "example": 1 + }, + "id": { + "type": "integer", + "example": 1 + }, + "notes": { + "type": "string", + "example": "JETS ONLY" + }, + "origin": { + "type": "string", + "example": "KLAX" + }, + "routing": { + "type": "string", + "example": "DCT" + }, + "updated_at": { + "type": "string", + "example": "2021-01-01T00:00:00Z" + } + } + }, + "event.EventSignupRequest": { + "type": "object", + "required": [ + "cid", + "position_id", + "shift" + ], + "properties": { + "cid": { + "type": "integer", + "example": 1293257 + }, + "position_id": { + "type": "integer", + "example": 1 + }, + "shift": { + "description": "1 = Primary, 2 = Secondary", + "type": "integer", + "example": 1 + } + } + }, + "event.EventSignupResponse": { "type": "object", "properties": { "cid": { @@ -3771,29 +5324,134 @@ "type": "string", "example": "2021-01-01T00:00:00Z" }, - "created_by": { - "type": "string", - "example": "'1234567' or 'System'" - }, - "entry": { - "type": "string", - "example": "Changed Preferred OIs to RP" + "event_id": { + "type": "integer", + "example": 1 }, "id": { "type": "integer", "example": 1 }, + "position_id": { + "type": "integer", + "example": 1 + }, + "shift": { + "description": "1 = Primary, 2 = Secondary", + "type": "integer", + "example": 1 + }, "updated_at": { "type": "string", "example": "2021-01-01T00:00:00Z" + } + } + }, + "event.EventTemplateRequest": { + "type": "object", + "required": [ + "facilities", + "fields", + "positions", + "title" + ], + "properties": { + "facilities": { + "type": "array", + "items": { + "$ref": "#/definitions/constants.FacilityID" + }, + "example": [ + "[\"ZDV\"", + " \"ZAB\"", + " \"ZLC\"]" + ] }, - "updated_by": { + "fields": { + "type": "array", + "items": { + "type": "string" + }, + "example": [ + "[\"KDEN\"", + " \"KBJC\"", + " \"KAPA\"]" + ] + }, + "positions": { + "type": "array", + "items": { + "type": "string" + }, + "example": [ + "[\"ZDV_APP\"", + " \"ZDV_TWR\"]" + ] + }, + "shifts": { + "type": "boolean", + "example": true + }, + "title": { "type": "string", - "example": "'1234567' or 'System'" + "example": "KDEN FNO" + } + } + }, + "event.EventTemplateResponse": { + "type": "object", + "properties": { + "created_at": { + "type": "string", + "example": "2021-01-01T00:00:00Z" }, - "vatusa_only": { + "facilities": { + "type": "array", + "items": { + "$ref": "#/definitions/constants.FacilityID" + }, + "example": [ + "[\"ZDV\"", + " \"ZAB\"", + " \"ZLC\"]" + ] + }, + "fields": { + "type": "array", + "items": { + "type": "string" + }, + "example": [ + "[\"KDEN\"", + " \"KBJC\"", + " \"KAPA\"]" + ] + }, + "id": { + "type": "integer", + "example": 1 + }, + "positions": { + "type": "array", + "items": { + "type": "string" + }, + "example": [ + "[\"ZDV_APP\"", + " \"ZDV_TWR\"]" + ] + }, + "shifts": { "type": "boolean", "example": true + }, + "title": { + "type": "string", + "example": "KDEN FNO Template" + }, + "updated_at": { + "type": "string", + "example": "2021-01-01T00:00:00Z" } } }, @@ -4085,6 +5743,128 @@ } } }, + "models.EventPosition": { + "type": "object", + "properties": { + "assignee": { + "type": "integer", + "example": 1293257 + }, + "created_at": { + "type": "string", + "example": "2021-01-01T00:00:00Z" + }, + "event_id": { + "type": "integer", + "example": 1 + }, + "facility": { + "allOf": [ + { + "$ref": "#/definitions/constants.FacilityID" + } + ], + "example": "ZDV" + }, + "id": { + "type": "integer", + "example": 1 + }, + "position": { + "type": "string", + "example": "ZDV_APP" + }, + "secondary_assignee": { + "type": "integer", + "example": 1293257 + }, + "shifts": { + "type": "boolean", + "example": true + }, + "signups": { + "type": "array", + "items": { + "$ref": "#/definitions/models.EventSignup" + } + }, + "updated_at": { + "type": "string", + "example": "2021-01-01T00:00:00Z" + } + } + }, + "models.EventRouting": { + "type": "object", + "properties": { + "created_at": { + "type": "string", + "example": "2021-01-01T00:00:00Z" + }, + "destination": { + "type": "string", + "example": "KDEN" + }, + "event_id": { + "type": "integer", + "example": 1 + }, + "id": { + "type": "integer", + "example": 1 + }, + "notes": { + "type": "string", + "example": "JETS ONLY" + }, + "origin": { + "type": "string", + "example": "KLAX" + }, + "routing": { + "type": "string", + "example": "DCT" + }, + "updated_at": { + "type": "string", + "example": "2021-01-01T00:00:00Z" + } + } + }, + "models.EventSignup": { + "type": "object", + "properties": { + "cid": { + "type": "integer", + "example": 1293257 + }, + "created_at": { + "type": "string", + "example": "2021-01-01T00:00:00Z" + }, + "event_id": { + "type": "integer", + "example": 1 + }, + "id": { + "type": "integer", + "example": 1 + }, + "position_id": { + "type": "integer", + "example": 1 + }, + "shift": { + "description": "1 = Primary, 2 = Secondary", + "type": "integer", + "example": 1 + }, + "updated_at": { + "type": "string", + "example": "2021-01-01T00:00:00Z" + } + } + }, "models.UserFlag": { "type": "object", "properties": { @@ -4781,6 +6561,13 @@ }, "user_notification.Request": { "type": "object", + "required": [ + "discord", + "email", + "events", + "feedback", + "training" + ], "properties": { "discord": { "type": "boolean", diff --git a/external/docs/swagger.yaml b/external/docs/swagger.yaml index a325103..449191a 100644 --- a/external/docs/swagger.yaml +++ b/external/docs/swagger.yaml @@ -234,6 +234,317 @@ definitions: example: true type: boolean type: object + event.EventPositionRequest: + properties: + assignee: + example: 1293257 + type: integer + position: + example: ZDV_APP + type: string + secondary_assignee: + example: 1293257 + type: integer + shifts: + example: true + type: boolean + required: + - position + type: object + event.EventPositionResponse: + properties: + assignee: + example: 1293257 + type: integer + created_at: + example: "2021-01-01T00:00:00Z" + type: string + event_id: + example: 1 + type: integer + facility: + allOf: + - $ref: '#/definitions/constants.FacilityID' + example: ZDV + id: + example: 1 + type: integer + position: + example: ZDV_APP + type: string + secondary_assignee: + example: 1293257 + type: integer + shifts: + example: true + type: boolean + signups: + items: + $ref: '#/definitions/models.EventSignup' + type: array + updated_at: + example: "2021-01-01T00:00:00Z" + type: string + type: object + event.EventRequest: + properties: + banner_url: + example: https://zdvartcc.org/banner.jpg + type: string + description: + example: Join us for a fun night of flying in and out of Denver! + type: string + end_date: + example: "2021-01-01T00:00:00Z" + type: string + facilities: + example: + - '["ZDV"' + - ' "ZAB"' + - ' "ZLC"]' + items: + $ref: '#/definitions/constants.FacilityID' + type: array + fields: + example: + - '["KDEN"' + - ' "KBJC"' + - ' "KAPA"]' + items: + type: string + type: array + start_date: + example: "2021-01-01T00:00:00Z" + type: string + title: + example: ZDV FNO + type: string + required: + - description + - end_date + - facilities + - fields + - start_date + - title + type: object + event.EventResponse: + properties: + banner_url: + example: https://zdvartcc.org/banner.jpg + type: string + created_at: + example: "2021-01-01T00:00:00Z" + type: string + description: + example: Join us for a fun night of flying in and out of Denver! + type: string + end_date: + example: "2021-01-01T00:00:00Z" + type: string + facilities: + example: + - '["ZDV"' + - ' "ZAB"' + - ' "ZLC"]' + items: + $ref: '#/definitions/constants.FacilityID' + type: array + fields: + example: + - '["KDEN"' + - ' "KBJC"' + - ' "KAPA"]' + items: + type: string + type: array + id: + example: 1 + type: integer + positions: + items: + $ref: '#/definitions/models.EventPosition' + type: array + routing: + items: + $ref: '#/definitions/models.EventRouting' + type: array + start_date: + example: "2021-01-01T00:00:00Z" + type: string + title: + example: ZDV FNO + type: string + updated_at: + example: "2021-01-01T00:00:00Z" + type: string + type: object + event.EventRoutingRequest: + properties: + destination: + example: ZAB + type: string + notes: + example: Expect vectors to final at DBL + type: string + origin: + example: ZDV + type: string + routing: + example: ZDV J80 DBL J80 FQF J80 HCT J80 HBU J80 HCT J80 FQF J80 DBL J80 ZAB + type: string + required: + - destination + - notes + - origin + - routing + type: object + event.EventRoutingResponse: + properties: + created_at: + example: "2021-01-01T00:00:00Z" + type: string + destination: + example: KDEN + type: string + event_id: + example: 1 + type: integer + id: + example: 1 + type: integer + notes: + example: JETS ONLY + type: string + origin: + example: KLAX + type: string + routing: + example: DCT + type: string + updated_at: + example: "2021-01-01T00:00:00Z" + type: string + type: object + event.EventSignupRequest: + properties: + cid: + example: 1293257 + type: integer + position_id: + example: 1 + type: integer + shift: + description: 1 = Primary, 2 = Secondary + example: 1 + type: integer + required: + - cid + - position_id + - shift + type: object + event.EventSignupResponse: + properties: + cid: + example: 1293257 + type: integer + created_at: + example: "2021-01-01T00:00:00Z" + type: string + event_id: + example: 1 + type: integer + id: + example: 1 + type: integer + position_id: + example: 1 + type: integer + shift: + description: 1 = Primary, 2 = Secondary + example: 1 + type: integer + updated_at: + example: "2021-01-01T00:00:00Z" + type: string + type: object + event.EventTemplateRequest: + properties: + facilities: + example: + - '["ZDV"' + - ' "ZAB"' + - ' "ZLC"]' + items: + $ref: '#/definitions/constants.FacilityID' + type: array + fields: + example: + - '["KDEN"' + - ' "KBJC"' + - ' "KAPA"]' + items: + type: string + type: array + positions: + example: + - '["ZDV_APP"' + - ' "ZDV_TWR"]' + items: + type: string + type: array + shifts: + example: true + type: boolean + title: + example: KDEN FNO + type: string + required: + - facilities + - fields + - positions + - title + type: object + event.EventTemplateResponse: + properties: + created_at: + example: "2021-01-01T00:00:00Z" + type: string + facilities: + example: + - '["ZDV"' + - ' "ZAB"' + - ' "ZLC"]' + items: + $ref: '#/definitions/constants.FacilityID' + type: array + fields: + example: + - '["KDEN"' + - ' "KBJC"' + - ' "KAPA"]' + items: + type: string + type: array + id: + example: 1 + type: integer + positions: + example: + - '["ZDV_APP"' + - ' "ZDV_TWR"]' + items: + type: string + type: array + shifts: + example: true + type: boolean + title: + example: KDEN FNO Template + type: string + updated_at: + example: "2021-01-01T00:00:00Z" + type: string + type: object facility.Request: properties: name: @@ -429,6 +740,93 @@ definitions: - $ref: '#/definitions/types.StatusType' example: pending type: object + models.EventPosition: + properties: + assignee: + example: 1293257 + type: integer + created_at: + example: "2021-01-01T00:00:00Z" + type: string + event_id: + example: 1 + type: integer + facility: + allOf: + - $ref: '#/definitions/constants.FacilityID' + example: ZDV + id: + example: 1 + type: integer + position: + example: ZDV_APP + type: string + secondary_assignee: + example: 1293257 + type: integer + shifts: + example: true + type: boolean + signups: + items: + $ref: '#/definitions/models.EventSignup' + type: array + updated_at: + example: "2021-01-01T00:00:00Z" + type: string + type: object + models.EventRouting: + properties: + created_at: + example: "2021-01-01T00:00:00Z" + type: string + destination: + example: KDEN + type: string + event_id: + example: 1 + type: integer + id: + example: 1 + type: integer + notes: + example: JETS ONLY + type: string + origin: + example: KLAX + type: string + routing: + example: DCT + type: string + updated_at: + example: "2021-01-01T00:00:00Z" + type: string + type: object + models.EventSignup: + properties: + cid: + example: 1293257 + type: integer + created_at: + example: "2021-01-01T00:00:00Z" + type: string + event_id: + example: 1 + type: integer + id: + example: 1 + type: integer + position_id: + example: 1 + type: integer + shift: + description: 1 = Primary, 2 = Secondary + example: 1 + type: integer + updated_at: + example: "2021-01-01T00:00:00Z" + type: string + type: object models.UserFlag: properties: cid: @@ -933,6 +1331,12 @@ definitions: training: example: true type: boolean + required: + - discord + - email + - events + - feedback + - training type: object user_notification.Response: properties: @@ -971,45 +1375,679 @@ definitions: created_at: example: "2021-01-01T00:00:00Z" type: string - facility_id: - allOf: - - $ref: '#/definitions/constants.FacilityID' - example: ZDV - role_id: - allOf: - - $ref: '#/definitions/constants.RoleID' - example: ATM - type: object - utils.ErrResponse: - properties: - code: - description: application-specific error code - type: integer - error: - description: application-level error message, for debugging + facility_id: + allOf: + - $ref: '#/definitions/constants.FacilityID' + example: ZDV + role_id: + allOf: + - $ref: '#/definitions/constants.RoleID' + example: ATM + type: object + utils.ErrResponse: + properties: + code: + description: application-specific error code + type: integer + error: + description: application-level error message, for debugging + type: string + status: + description: user-level status message + type: string + type: object +info: + contact: + email: vatusa6@vatusa.net + name: VATUSA Support + url: http://www.swagger.io/support + description: VATUSAs public API + license: + name: Apache 2.0 + url: http://www.apache.org/licenses/LICENSE-2.0.html + termsOfService: http://swagger.io/terms/ + title: VATUSA API + version: "0.1" +paths: + /facility: + get: + consumes: + - application/json + description: Get all facilities + produces: + - application/json + responses: + "200": + description: OK + schema: + items: + $ref: '#/definitions/facility.Response' + type: array + "400": + description: Bad Request + schema: + $ref: '#/definitions/utils.ErrResponse' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/utils.ErrResponse' + summary: Get all facilities + tags: + - facility + /facility/{FacilityID}: + get: + consumes: + - application/json + description: Get information for a specific facility + parameters: + - description: Facility ID + in: path + name: FacilityID + required: true + type: string + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/facility.Response' + "400": + description: Bad Request + schema: + $ref: '#/definitions/utils.ErrResponse' + "404": + description: Not Found + schema: + $ref: '#/definitions/utils.ErrResponse' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/utils.ErrResponse' + summary: Get a specific facility + tags: + - facility + patch: + consumes: + - application/json + description: Patch a facility + parameters: + - description: Facility ID + in: path + name: FacilityID + required: true + type: string + - description: Facility + in: body + name: facility + required: true + schema: + $ref: '#/definitions/facility.Request' + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/facility.Response' + "400": + description: Bad Request + schema: + $ref: '#/definitions/utils.ErrResponse' + "404": + description: Not Found + schema: + $ref: '#/definitions/utils.ErrResponse' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/utils.ErrResponse' + summary: Patch a facility + tags: + - facility + put: + consumes: + - application/json + description: Update a facility + parameters: + - description: Facility ID + in: path + name: FacilityID + required: true + type: string + - description: Facility + in: body + name: facility + required: true + schema: + $ref: '#/definitions/facility.Request' + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/facility.Response' + "400": + description: Bad Request + schema: + $ref: '#/definitions/utils.ErrResponse' + "404": + description: Not Found + schema: + $ref: '#/definitions/utils.ErrResponse' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/utils.ErrResponse' + summary: Update a facility + tags: + - facility + /facility/{FacilityID}/event-templates: + get: + consumes: + - application/json + description: Get Event Templates by Facility + parameters: + - description: Facility ID + in: path + name: FacilityID + required: true + type: string + produces: + - application/json + responses: + "200": + description: OK + schema: + items: + $ref: '#/definitions/event.EventTemplateResponse' + type: array + "400": + description: Bad Request + schema: + $ref: '#/definitions/utils.ErrResponse' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/utils.ErrResponse' + summary: Get Event Templates + tags: + - event + /facility/{FacilityID}/event-templates/{EventTemplateID}: + delete: + consumes: + - application/json + description: Delete an Event Template + parameters: + - description: Facility ID + in: path + name: FacilityID + required: true + type: string + - description: Event Template ID + in: path + name: EventTemplateID + required: true + type: string + produces: + - application/json + responses: + "204": + description: No Content + "400": + description: Bad Request + schema: + $ref: '#/definitions/utils.ErrResponse' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/utils.ErrResponse' + summary: Delete an Event Template + tags: + - event + put: + consumes: + - application/json + description: Update an Event Template + parameters: + - description: Facility ID + in: path + name: FacilityID + required: true + type: string + - description: Event Template ID + in: path + name: EventTemplateID + required: true + type: string + - description: Event Template + in: body + name: event + required: true + schema: + $ref: '#/definitions/event.EventTemplateRequest' + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/event.EventTemplateResponse' + "400": + description: Bad Request + schema: + $ref: '#/definitions/utils.ErrResponse' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/utils.ErrResponse' + summary: Update an Event Template + tags: + - event + /facility/{FacilityID}/events: + get: + consumes: + - application/json + description: Get Events by Facility + produces: + - application/json + responses: + "200": + description: OK + schema: + items: + $ref: '#/definitions/event.EventResponse' + type: array + "400": + description: Bad Request + schema: + $ref: '#/definitions/utils.ErrResponse' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/utils.ErrResponse' + summary: Get Events + tags: + - event + post: + consumes: + - application/json + description: Create an Event + parameters: + - description: Event + in: body + name: event + required: true + schema: + $ref: '#/definitions/event.EventRequest' + produces: + - application/json + responses: + "201": + description: Created + schema: + $ref: '#/definitions/event.EventResponse' + "400": + description: Bad Request + schema: + $ref: '#/definitions/utils.ErrResponse' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/utils.ErrResponse' + summary: Create an Event + tags: + - event + /facility/{FacilityID}/events/{EventID}: + delete: + consumes: + - application/json + description: Delete Event by ID + parameters: + - description: Event ID + in: path + name: EventID + required: true + type: string + produces: + - application/json + responses: + "204": + description: No Content + "400": + description: Bad Request + schema: + $ref: '#/definitions/utils.ErrResponse' + "404": + description: Not Found + schema: + $ref: '#/definitions/utils.ErrResponse' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/utils.ErrResponse' + summary: Delete Event + tags: + - event + get: + consumes: + - application/json + description: Get Event by ID + parameters: + - description: Event ID + in: path + name: EventID + required: true + type: string + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/event.EventResponse' + "400": + description: Bad Request + schema: + $ref: '#/definitions/utils.ErrResponse' + "404": + description: Not Found + schema: + $ref: '#/definitions/utils.ErrResponse' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/utils.ErrResponse' + summary: Get Event + tags: + - event + patch: + consumes: + - application/json + description: Patch Event by ID + parameters: + - description: Event ID + in: path + name: EventID + required: true + type: string + - description: Event + in: body + name: event + required: true + schema: + $ref: '#/definitions/event.EventRequest' + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/event.EventResponse' + "400": + description: Bad Request + schema: + $ref: '#/definitions/utils.ErrResponse' + "404": + description: Not Found + schema: + $ref: '#/definitions/utils.ErrResponse' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/utils.ErrResponse' + summary: Patch Event + tags: + - event + put: + consumes: + - application/json + description: Update Event by ID + parameters: + - description: Event ID + in: path + name: EventID + required: true + type: string + - description: Event + in: body + name: event + required: true + schema: + $ref: '#/definitions/event.EventRequest' + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/event.EventResponse' + "400": + description: Bad Request + schema: + $ref: '#/definitions/utils.ErrResponse' + "404": + description: Not Found + schema: + $ref: '#/definitions/utils.ErrResponse' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/utils.ErrResponse' + summary: Update Event + tags: + - event + /facility/{FacilityID}/events/{EventID}/positions: + get: + consumes: + - application/json + description: Get Event Positions + parameters: + - description: Facility ID + in: path + name: FacilityID + required: true + type: string + - description: Event ID + in: path + name: EventID + required: true + type: string + produces: + - application/json + responses: + "200": + description: OK + schema: + items: + $ref: '#/definitions/event.EventPositionResponse' + type: array + "400": + description: Bad Request + schema: + $ref: '#/definitions/utils.ErrResponse' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/utils.ErrResponse' + summary: Get Event Positions + tags: + - event + post: + consumes: + - application/json + description: Create an Event Position + parameters: + - description: Facility ID + in: path + name: FacilityID + required: true + type: string + - description: Event ID + in: path + name: EventID + required: true + type: string + - description: Event Position + in: body + name: event + required: true + schema: + $ref: '#/definitions/event.EventPositionRequest' + produces: + - application/json + responses: + "201": + description: Created + schema: + $ref: '#/definitions/event.EventPositionResponse' + "400": + description: Bad Request + schema: + $ref: '#/definitions/utils.ErrResponse' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/utils.ErrResponse' + summary: Create an Event Position + tags: + - event + /facility/{FacilityID}/events/{EventID}/positions/{EventPositionID}: + delete: + consumes: + - application/json + description: Delete an Event Position + parameters: + - description: Facility ID + in: path + name: FacilityID + required: true + type: string + - description: Event ID + in: path + name: EventID + required: true + type: string + - description: Event Position ID + in: path + name: EventPositionID + required: true + type: string + produces: + - application/json + responses: + "204": + description: No Content + "400": + description: Bad Request + schema: + $ref: '#/definitions/utils.ErrResponse' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/utils.ErrResponse' + summary: Delete an Event Position + tags: + - event + get: + consumes: + - application/json + description: Get Event Position + parameters: + - description: Facility ID + in: path + name: FacilityID + required: true + type: string + - description: Event ID + in: path + name: EventID + required: true + type: string + - description: Event Position ID + in: path + name: EventPositionID + required: true + type: string + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/event.EventPositionResponse' + "400": + description: Bad Request + schema: + $ref: '#/definitions/utils.ErrResponse' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/utils.ErrResponse' + summary: Get Event Position + tags: + - event + patch: + consumes: + - application/json + description: Patch an Event Position + parameters: + - description: Facility ID + in: path + name: FacilityID + required: true + type: string + - description: Event ID + in: path + name: EventID + required: true type: string - status: - description: user-level status message + - description: Event Position ID + in: path + name: EventPositionID + required: true type: string - type: object -info: - contact: - email: vatusa6@vatusa.net - name: VATUSA Support - url: http://www.swagger.io/support - description: VATUSAs public API - license: - name: Apache 2.0 - url: http://www.apache.org/licenses/LICENSE-2.0.html - termsOfService: http://swagger.io/terms/ - title: VATUSA API - version: "0.1" -paths: - /facility: + - description: Event Position + in: body + name: event + required: true + schema: + $ref: '#/definitions/event.EventPositionRequest' + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/event.EventPositionResponse' + "400": + description: Bad Request + schema: + $ref: '#/definitions/utils.ErrResponse' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/utils.ErrResponse' + summary: Patch an Event Position + tags: + - event + /facility/{FacilityID}/events/{EventID}/routing: get: consumes: - application/json - description: Get all facilities + description: Get Event Routing + parameters: + - description: Facility ID + in: path + name: FacilityID + required: true + type: string + - description: Event ID + in: path + name: EventID + required: true + type: string produces: - application/json responses: @@ -1017,7 +2055,7 @@ paths: description: OK schema: items: - $ref: '#/definitions/facility.Response' + $ref: '#/definitions/event.EventRoutingResponse' type: array "400": description: Bad Request @@ -1027,118 +2065,279 @@ paths: description: Internal Server Error schema: $ref: '#/definitions/utils.ErrResponse' - summary: Get all facilities + summary: Get Event Routing tags: - - facility - /facility/{FacilityID}: - get: + - event + post: consumes: - application/json - description: Get information for a specific facility + description: Create an Event Routing parameters: - description: Facility ID in: path name: FacilityID required: true type: string + - description: Event ID + in: path + name: EventID + required: true + type: string + - description: Event Routing + in: body + name: event + required: true + schema: + $ref: '#/definitions/event.EventRoutingRequest' produces: - application/json responses: - "200": - description: OK + "201": + description: Created schema: - $ref: '#/definitions/facility.Response' + $ref: '#/definitions/event.EventRoutingResponse' "400": description: Bad Request schema: $ref: '#/definitions/utils.ErrResponse' - "404": - description: Not Found + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/utils.ErrResponse' + summary: Create an Event Routing + tags: + - event + /facility/{FacilityID}/events/{EventID}/routing/{EventRoutingID}: + delete: + consumes: + - application/json + description: Delete an Event Routing + parameters: + - description: Facility ID + in: path + name: FacilityID + required: true + type: string + - description: Event ID + in: path + name: EventID + required: true + type: string + - description: Event Routing ID + in: path + name: EventRoutingID + required: true + type: string + produces: + - application/json + responses: + "204": + description: No Content + "400": + description: Bad Request schema: $ref: '#/definitions/utils.ErrResponse' "500": description: Internal Server Error schema: $ref: '#/definitions/utils.ErrResponse' - summary: Get a specific facility + summary: Delete an Event Routing tags: - - facility + - event patch: consumes: - application/json - description: Patch a facility + description: Patch an Event Routing parameters: - description: Facility ID in: path name: FacilityID required: true type: string - - description: Facility + - description: Event ID + in: path + name: EventID + required: true + type: string + - description: Event Routing ID + in: path + name: EventRoutingID + required: true + type: string + - description: Event Routing in: body - name: facility + name: event required: true schema: - $ref: '#/definitions/facility.Request' + $ref: '#/definitions/event.EventRoutingRequest' produces: - application/json responses: "200": description: OK schema: - $ref: '#/definitions/facility.Response' + $ref: '#/definitions/event.EventRoutingResponse' "400": description: Bad Request schema: $ref: '#/definitions/utils.ErrResponse' - "404": - description: Not Found + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/utils.ErrResponse' + summary: Patch an Event Routing + tags: + - event + /facility/{FacilityID}/events/{EventID}/signups: + get: + consumes: + - application/json + description: Get Event Signups + parameters: + - description: Facility ID + in: path + name: FacilityID + required: true + type: string + - description: Event ID + in: path + name: EventID + required: true + type: string + produces: + - application/json + responses: + "200": + description: OK + schema: + items: + $ref: '#/definitions/event.EventSignupResponse' + type: array + "400": + description: Bad Request schema: $ref: '#/definitions/utils.ErrResponse' "500": description: Internal Server Error schema: $ref: '#/definitions/utils.ErrResponse' - summary: Patch a facility + summary: Get Event Signups tags: - - facility - put: + - event + post: consumes: - application/json - description: Update a facility + description: Create an Event Signup parameters: - description: Facility ID in: path name: FacilityID required: true type: string - - description: Facility + - description: Event ID + in: path + name: EventID + required: true + type: string + - description: Event Signup in: body - name: facility + name: event required: true schema: - $ref: '#/definitions/facility.Request' + $ref: '#/definitions/event.EventSignupRequest' produces: - application/json responses: - "200": - description: OK + "201": + description: Created schema: - $ref: '#/definitions/facility.Response' + $ref: '#/definitions/event.EventSignupResponse' "400": description: Bad Request schema: $ref: '#/definitions/utils.ErrResponse' - "404": - description: Not Found + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/utils.ErrResponse' + summary: Create an Event Signup + tags: + - event + /facility/{FacilityID}/events/{EventID}/signups/{EventSignupID}: + delete: + consumes: + - application/json + description: Delete an Event Signup + parameters: + - description: Facility ID + in: path + name: FacilityID + required: true + type: string + - description: Event ID + in: path + name: EventID + required: true + type: string + - description: Event Signup ID + in: path + name: EventSignupID + required: true + type: string + produces: + - application/json + responses: + "204": + description: No Content + "400": + description: Bad Request schema: $ref: '#/definitions/utils.ErrResponse' "500": description: Internal Server Error schema: $ref: '#/definitions/utils.ErrResponse' - summary: Update a facility + summary: Delete an Event Signup tags: - - facility + - event + get: + consumes: + - application/json + description: Get an Event Signup + parameters: + - description: Facility ID + in: path + name: FacilityID + required: true + type: string + - description: Event ID + in: path + name: EventID + required: true + type: string + - description: Event Signup ID + in: path + name: EventSignupID + required: true + type: string + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/event.EventSignupResponse' + "400": + description: Bad Request + schema: + $ref: '#/definitions/utils.ErrResponse' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/utils.ErrResponse' + summary: Get an Event Signup + tags: + - event /facility/{FacilityID}/faq: get: consumes: @@ -3339,6 +4538,33 @@ paths: summary: Update a user flag tags: - user-flag + /user/discord/unlink: + get: + consumes: + - application/json + description: Unlink your Discord account from your VATUSA account + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/user.Response' + "400": + description: Bad Request + schema: + $ref: '#/definitions/utils.ErrResponse' + "404": + description: Not Found + schema: + $ref: '#/definitions/utils.ErrResponse' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/utils.ErrResponse' + summary: Unlink your Discord account + tags: + - discord securityDefinitions: ApiKeyAuth: in: header diff --git a/external/v3/event/event.go b/external/v3/event/event.go new file mode 100644 index 0000000..d6267dd --- /dev/null +++ b/external/v3/event/event.go @@ -0,0 +1,230 @@ +package event + +import ( + "github.com/VATUSA/primary-api/pkg/database/models" + "github.com/VATUSA/primary-api/pkg/utils" + "github.com/go-chi/render" + log "github.com/sirupsen/logrus" + "net/http" + "slices" + "time" +) + +// CreateEvent godoc +// @Summary Create an Event +// @Description Create an Event +// @Tags event +// @Accept json +// @Produce json +// @Param event body EventRequest true "Event" +// @Success 201 {object} EventResponse +// @Failure 400 {object} utils.ErrResponse +// @Failure 500 {object} utils.ErrResponse +// @Router /facility/{FacilityID}/events [post] +func CreateEvent(w http.ResponseWriter, r *http.Request) { + req := &EventRequest{} + if err := render.Bind(r, req); err != nil { + utils.Render(w, r, utils.ErrBadRequest) + return + } + + if err := req.Validate(); err != nil { + utils.Render(w, r, utils.ErrBadRequest) + return + } + + fac := utils.GetFacilityCtx(r) + if !slices.Contains(req.Facilities, fac.ID) { + log.Errorf("Facility %s not in facilities list", fac.ID) + utils.Render(w, r, utils.ErrBadRequest) + return + } + + ev := &models.Event{ + Title: req.Title, + Description: req.Description, + BannerURL: req.BannerURL, + StartDate: req.StartDate, + EndDate: req.EndDate, + Fields: req.Fields, + Facilities: req.Facilities, + } + + if err := ev.Create(); err != nil { + log.WithError(err).Error("Error creating event") + utils.Render(w, r, utils.ErrInternalServer) + return + } + + utils.Response(r, http.StatusCreated) + utils.Render(w, r, NewEventResponse(ev)) +} + +// GetEvents godoc +// @Summary Get Events +// @Description Get Events by Facility +// @Tags event +// @Accept json +// @Produce json +// @Success 200 {object} []EventResponse +// @Failure 400 {object} utils.ErrResponse +// @Failure 500 {object} utils.ErrResponse +// @Router /facility/{FacilityID}/events [get] +func GetEvents(w http.ResponseWriter, r *http.Request) { + fac := utils.GetFacilityCtx(r) + + events, err := models.GetEventsFiltered(fac.ID, time.Now()) + if err != nil { + log.WithError(err).Error("Error getting events") + utils.Render(w, r, utils.ErrInternalServer) + return + } + + if err := render.RenderList(w, r, NewEventListResponse(events)); err != nil { + utils.Render(w, r, utils.ErrRender(err)) + return + } +} + +// GetEvent godoc +// @Summary Get Event +// @Description Get Event by ID +// @Tags event +// @Accept json +// @Produce json +// @Param EventID path string true "Event ID" +// @Success 200 {object} EventResponse +// @Failure 400 {object} utils.ErrResponse +// @Failure 404 {object} utils.ErrResponse +// @Failure 500 {object} utils.ErrResponse +// @Router /facility/{FacilityID}/events/{EventID} [get] +func GetEvent(w http.ResponseWriter, r *http.Request) { + ev := utils.GetEventCtx(r) + utils.Render(w, r, NewEventResponse(ev)) +} + +// UpdateEvent godoc +// @Summary Update Event +// @Description Update Event by ID +// @Tags event +// @Accept json +// @Produce json +// @Param EventID path string true "Event ID" +// @Param event body EventRequest true "Event" +// @Success 200 {object} EventResponse +// @Failure 400 {object} utils.ErrResponse +// @Failure 404 {object} utils.ErrResponse +// @Failure 500 {object} utils.ErrResponse +// @Router /facility/{FacilityID}/events/{EventID} [put] +func UpdateEvent(w http.ResponseWriter, r *http.Request) { + ev := utils.GetEventCtx(r) + + req := &EventRequest{} + if err := render.Bind(r, req); err != nil { + utils.Render(w, r, utils.ErrBadRequest) + return + } + + if err := req.Validate(); err != nil { + utils.Render(w, r, utils.ErrBadRequest) + return + } + + ev.Title = req.Title + ev.Description = req.Description + ev.BannerURL = req.BannerURL + ev.StartDate = req.StartDate + ev.EndDate = req.EndDate + ev.Fields = req.Fields + ev.Facilities = req.Facilities + + if err := ev.Update(); err != nil { + log.WithError(err).Error("Error updating event") + utils.Render(w, r, utils.ErrInternalServer) + return + } + + utils.Render(w, r, NewEventResponse(ev)) +} + +// PatchEvent godoc +// @Summary Patch Event +// @Description Patch Event by ID +// @Tags event +// @Accept json +// @Produce json +// @Param EventID path string true "Event ID" +// @Param event body EventRequest true "Event" +// @Success 200 {object} EventResponse +// @Failure 400 {object} utils.ErrResponse +// @Failure 404 {object} utils.ErrResponse +// @Failure 500 {object} utils.ErrResponse +// @Router /facility/{FacilityID}/events/{EventID} [patch] +func PatchEvent(w http.ResponseWriter, r *http.Request) { + ev := utils.GetEventCtx(r) + + req := &EventRequest{} + if err := render.Bind(r, req); err != nil { + utils.Render(w, r, utils.ErrBadRequest) + return + } + + if err := req.Validate(); err != nil { + utils.Render(w, r, utils.ErrBadRequest) + return + } + + if req.Title != "" { + ev.Title = req.Title + } + if req.Description != "" { + ev.Description = req.Description + } + if req.BannerURL != "" { + ev.BannerURL = req.BannerURL + } + if !req.StartDate.IsZero() { + ev.StartDate = req.StartDate + } + if !req.EndDate.IsZero() { + ev.EndDate = req.EndDate + } + if len(req.Fields) > 0 { + ev.Fields = req.Fields + } + if len(req.Facilities) > 0 { + ev.Facilities = req.Facilities + } + + if err := ev.Update(); err != nil { + log.WithError(err).Error("Error updating event") + utils.Render(w, r, utils.ErrInternalServer) + return + } + + utils.Render(w, r, NewEventResponse(ev)) +} + +// DeleteEvent godoc +// @Summary Delete Event +// @Description Delete Event by ID +// @Tags event +// @Accept json +// @Produce json +// @Param EventID path string true "Event ID" +// @Success 204 +// @Failure 400 {object} utils.ErrResponse +// @Failure 404 {object} utils.ErrResponse +// @Failure 500 {object} utils.ErrResponse +// @Router /facility/{FacilityID}/events/{EventID} [delete] +func DeleteEvent(w http.ResponseWriter, r *http.Request) { + ev := utils.GetEventCtx(r) + + if err := ev.Delete(); err != nil { + log.WithError(err).Error("Error deleting event") + utils.Render(w, r, utils.ErrInternalServer) + return + } + + utils.Response(r, http.StatusNoContent) +} diff --git a/external/v3/event/event_position.go b/external/v3/event/event_position.go new file mode 100644 index 0000000..4ea9ae7 --- /dev/null +++ b/external/v3/event/event_position.go @@ -0,0 +1,183 @@ +package event + +import ( + "github.com/VATUSA/primary-api/pkg/database/models" + "github.com/VATUSA/primary-api/pkg/utils" + "github.com/go-chi/render" + log "github.com/sirupsen/logrus" + "net/http" +) + +// CreateEventPosition godoc +// @Summary Create an Event Position +// @Description Create an Event Position +// @Tags event +// @Accept json +// @Produce json +// @Param FacilityID path string true "Facility ID" +// @Param EventID path string true "Event ID" +// @Param event body EventPositionRequest true "Event Position" +// @Success 201 {object} EventPositionResponse +// @Failure 400 {object} utils.ErrResponse +// @Failure 500 {object} utils.ErrResponse +// @Router /facility/{FacilityID}/events/{EventID}/positions [post] +func CreateEventPosition(w http.ResponseWriter, r *http.Request) { + req := &EventPositionRequest{} + if err := render.Bind(r, req); err != nil { + utils.Render(w, r, utils.ErrBadRequest) + return + } + + if err := req.Validate(); err != nil { + utils.Render(w, r, utils.ErrBadRequest) + return + } + + fac := utils.GetFacilityCtx(r) + position := &models.EventPosition{ + EventID: utils.GetEventCtx(r).ID, + Facility: fac.ID, + Position: req.Position, + Shifts: false, + Assignee: 0, + SecondaryAssignee: 0, + } + + if req.Shifts != nil { + position.Shifts = *req.Shifts + } + if req.Assignee != nil { + position.Assignee = *req.Assignee + } + if req.SecondaryAssignee != nil { + position.SecondaryAssignee = *req.SecondaryAssignee + } + + if err := position.Create(); err != nil { + log.WithError(err).Error("Error creating event position") + utils.Render(w, r, utils.ErrInternalServer) + return + } + + utils.Response(r, http.StatusCreated) + utils.Render(w, r, NewEventPositionResponse(position)) +} + +// GetEventPositions godoc +// @Summary Get Event Positions +// @Description Get Event Positions +// @Tags event +// @Accept json +// @Produce json +// @Param FacilityID path string true "Facility ID" +// @Param EventID path string true "Event ID" +// @Success 200 {object} []EventPositionResponse +// @Failure 400 {object} utils.ErrResponse +// @Failure 500 {object} utils.ErrResponse +// @Router /facility/{FacilityID}/events/{EventID}/positions [get] +func GetEventPositions(w http.ResponseWriter, r *http.Request) { + positions, err := models.GetEventPositionsFiltered(utils.GetEventCtx(r).ID, utils.GetFacilityCtx(r).ID) + if err != nil { + log.WithError(err).Error("Error getting event positions") + utils.Render(w, r, utils.ErrInternalServer) + return + } + + if err := render.RenderList(w, r, NewEventPositionListResponse(positions)); err != nil { + log.WithError(err).Error("Error rendering event positions") + utils.Render(w, r, utils.ErrInternalServer) + return + } +} + +// GetEventPosition godoc +// @Summary Get Event Position +// @Description Get Event Position +// @Tags event +// @Accept json +// @Produce json +// @Param FacilityID path string true "Facility ID" +// @Param EventID path string true "Event ID" +// @Param EventPositionID path string true "Event Position ID" +// @Success 200 {object} EventPositionResponse +// @Failure 400 {object} utils.ErrResponse +// @Failure 500 {object} utils.ErrResponse +// @Router /facility/{FacilityID}/events/{EventID}/positions/{EventPositionID} [get] +func GetEventPosition(w http.ResponseWriter, r *http.Request) { + position := utils.GetEventPositionCtx(r) + utils.Render(w, r, NewEventPositionResponse(position)) +} + +// PatchEventPosition godoc +// @Summary Patch an Event Position +// @Description Patch an Event Position +// @Tags event +// @Accept json +// @Produce json +// @Param FacilityID path string true "Facility ID" +// @Param EventID path string true "Event ID" +// @Param EventPositionID path string true "Event Position ID" +// @Param event body EventPositionRequest true "Event Position" +// @Success 200 {object} EventPositionResponse +// @Failure 400 {object} utils.ErrResponse +// @Failure 500 {object} utils.ErrResponse +// @Router /facility/{FacilityID}/events/{EventID}/positions/{EventPositionID} [patch] +func PatchEventPosition(w http.ResponseWriter, r *http.Request) { + position := utils.GetEventPositionCtx(r) + + req := &EventPositionRequest{} + if err := render.Bind(r, req); err != nil { + utils.Render(w, r, utils.ErrBadRequest) + return + } + + if err := req.Validate(); err != nil { + utils.Render(w, r, utils.ErrBadRequest) + return + } + + if req.Position != "" { + position.Position = req.Position + } + if req.Assignee != nil { + position.Assignee = *req.Assignee + } + if req.Shifts != nil { + position.Shifts = *req.Shifts + } + if req.SecondaryAssignee != nil { + position.SecondaryAssignee = *req.SecondaryAssignee + } + + if err := position.Update(); err != nil { + log.WithError(err).Error("Error updating event position") + utils.Render(w, r, utils.ErrInternalServer) + return + } + + utils.Render(w, r, NewEventPositionResponse(position)) +} + +// DeleteEventPosition godoc +// @Summary Delete an Event Position +// @Description Delete an Event Position +// @Tags event +// @Accept json +// @Produce json +// @Param FacilityID path string true "Facility ID" +// @Param EventID path string true "Event ID" +// @Param EventPositionID path string true "Event Position ID" +// @Success 204 +// @Failure 400 {object} utils.ErrResponse +// @Failure 500 {object} utils.ErrResponse +// @Router /facility/{FacilityID}/events/{EventID}/positions/{EventPositionID} [delete] +func DeleteEventPosition(w http.ResponseWriter, r *http.Request) { + position := utils.GetEventPositionCtx(r) + if err := position.Delete(); err != nil { + log.WithError(err).Error("Error deleting event position") + utils.Render(w, r, utils.ErrInternalServer) + return + } + + utils.Response(r, http.StatusNoContent) +} diff --git a/external/v3/event/event_routing.go b/external/v3/event/event_routing.go new file mode 100644 index 0000000..739396c --- /dev/null +++ b/external/v3/event/event_routing.go @@ -0,0 +1,154 @@ +package event + +import ( + "github.com/VATUSA/primary-api/pkg/database/models" + "github.com/VATUSA/primary-api/pkg/utils" + "github.com/go-chi/render" + log "github.com/sirupsen/logrus" + "net/http" +) + +// GetEventRouting godoc +// @Summary Get Event Routing +// @Description Get Event Routing +// @Tags event +// @Accept json +// @Produce json +// @Param FacilityID path string true "Facility ID" +// @Param EventID path string true "Event ID" +// @Success 200 {object} []EventRoutingResponse +// @Failure 400 {object} utils.ErrResponse +// @Failure 500 {object} utils.ErrResponse +// @Router /facility/{FacilityID}/events/{EventID}/routing [get] +func GetEventRouting(w http.ResponseWriter, r *http.Request) { + event := utils.GetEventCtx(r) + routing, err := models.GetEventRoutingFiltered(event.ID) + if err != nil { + log.WithError(err).Error("Error getting event routing") + utils.Render(w, r, utils.ErrInternalServer) + return + } + + if err := render.RenderList(w, r, NewEventRoutingListResponse(routing)); err != nil { + utils.Render(w, r, utils.ErrInternalServer) + return + } +} + +// CreateEventRouting godoc +// @Summary Create an Event Routing +// @Description Create an Event Routing +// @Tags event +// @Accept json +// @Produce json +// @Param FacilityID path string true "Facility ID" +// @Param EventID path string true "Event ID" +// @Param event body EventRoutingRequest true "Event Routing" +// @Success 201 {object} EventRoutingResponse +// @Failure 400 {object} utils.ErrResponse +// @Failure 500 {object} utils.ErrResponse +// @Router /facility/{FacilityID}/events/{EventID}/routing [post] +func CreateEventRouting(w http.ResponseWriter, r *http.Request) { + req := &EventRoutingRequest{} + if err := render.Bind(r, req); err != nil { + utils.Render(w, r, utils.ErrBadRequest) + return + } + + if err := req.Validate(); err != nil { + utils.Render(w, r, utils.ErrBadRequest) + return + } + + event := utils.GetEventCtx(r) + routing := &models.EventRouting{ + EventID: event.ID, + Origin: req.Origin, + Destination: req.Destination, + Routing: req.Routing, + Notes: req.Notes, + } + + if err := routing.Create(); err != nil { + log.WithError(err).Error("Error creating event routing") + utils.Render(w, r, utils.ErrInternalServer) + return + } + + utils.Response(r, http.StatusCreated) + utils.Render(w, r, NewEventRoutingResponse(routing)) +} + +// PatchEventRouting godoc +// @Summary Patch an Event Routing +// @Description Patch an Event Routing +// @Tags event +// @Accept json +// @Produce json +// @Param FacilityID path string true "Facility ID" +// @Param EventID path string true "Event ID" +// @Param EventRoutingID path string true "Event Routing ID" +// @Param event body EventRoutingRequest true "Event Routing" +// @Success 200 {object} EventRoutingResponse +// @Failure 400 {object} utils.ErrResponse +// @Failure 500 {object} utils.ErrResponse +// @Router /facility/{FacilityID}/events/{EventID}/routing/{EventRoutingID} [patch] +func PatchEventRouting(w http.ResponseWriter, r *http.Request) { + routing := utils.GetEventRoutingCtx(r) + + req := &EventRoutingRequest{} + if err := render.Bind(r, req); err != nil { + utils.Render(w, r, utils.ErrBadRequest) + return + } + + if err := req.Validate(); err != nil { + utils.Render(w, r, utils.ErrBadRequest) + return + } + + if req.Origin != "" { + routing.Origin = req.Origin + } + if req.Destination != "" { + routing.Destination = req.Destination + } + if req.Routing != "" { + routing.Routing = req.Routing + } + if req.Notes != "" { + routing.Notes = req.Notes + } + + if err := routing.Update(); err != nil { + log.WithError(err).Error("Error patching event routing") + utils.Render(w, r, utils.ErrInternalServer) + return + } + + utils.Render(w, r, NewEventRoutingResponse(routing)) +} + +// DeleteEventRouting godoc +// @Summary Delete an Event Routing +// @Description Delete an Event Routing +// @Tags event +// @Accept json +// @Produce json +// @Param FacilityID path string true "Facility ID" +// @Param EventID path string true "Event ID" +// @Param EventRoutingID path string true "Event Routing ID" +// @Success 204 +// @Failure 400 {object} utils.ErrResponse +// @Failure 500 {object} utils.ErrResponse +// @Router /facility/{FacilityID}/events/{EventID}/routing/{EventRoutingID} [delete] +func DeleteEventRouting(w http.ResponseWriter, r *http.Request) { + routing := utils.GetEventRoutingCtx(r) + if err := routing.Delete(); err != nil { + log.WithError(err).Error("Error deleting event routing") + utils.Render(w, r, utils.ErrInternalServer) + return + } + + utils.Response(r, http.StatusNoContent) +} diff --git a/external/v3/event/event_signup.go b/external/v3/event/event_signup.go new file mode 100644 index 0000000..2ada5b2 --- /dev/null +++ b/external/v3/event/event_signup.go @@ -0,0 +1,120 @@ +package event + +import ( + "github.com/VATUSA/primary-api/pkg/database/models" + "github.com/VATUSA/primary-api/pkg/utils" + "github.com/go-chi/render" + log "github.com/sirupsen/logrus" + "net/http" +) + +// CreateEventSignup godoc +// @Summary Create an Event Signup +// @Description Create an Event Signup +// @Tags event +// @Accept json +// @Produce json +// @Param FacilityID path string true "Facility ID" +// @Param EventID path string true "Event ID" +// @Param event body EventSignupRequest true "Event Signup" +// @Success 201 {object} EventSignupResponse +// @Failure 400 {object} utils.ErrResponse +// @Failure 500 {object} utils.ErrResponse +// @Router /facility/{FacilityID}/events/{EventID}/signups [post] +func CreateEventSignup(w http.ResponseWriter, r *http.Request) { + req := &EventSignupRequest{} + if err := render.Bind(r, req); err != nil { + utils.Render(w, r, utils.ErrBadRequest) + return + } + + if err := req.Validate(); err != nil { + utils.Render(w, r, utils.ErrBadRequest) + return + } + + signup := &models.EventSignup{ + PositionID: req.PositionID, + CID: req.CID, + Shift: req.Shift, + } + + if err := signup.Create(); err != nil { + log.WithError(err).Error("Error creating event signup") + utils.Render(w, r, utils.ErrInternalServer) + return + } + + utils.Response(r, http.StatusCreated) + utils.Render(w, r, NewEventSignupResponse(signup)) +} + +// GetEventSignup godoc +// @Summary Get an Event Signup +// @Description Get an Event Signup +// @Tags event +// @Accept json +// @Produce json +// @Param FacilityID path string true "Facility ID" +// @Param EventID path string true "Event ID" +// @Param EventSignupID path string true "Event Signup ID" +// @Success 200 {object} EventSignupResponse +// @Failure 400 {object} utils.ErrResponse +// @Failure 500 {object} utils.ErrResponse +// @Router /facility/{FacilityID}/events/{EventID}/signups/{EventSignupID} [get] +func GetEventSignup(w http.ResponseWriter, r *http.Request) { + signup := utils.GetEventSignupCtx(r) + utils.Render(w, r, NewEventSignupResponse(signup)) +} + +// GetEventSignups godoc +// @Summary Get Event Signups +// @Description Get Event Signups +// @Tags event +// @Accept json +// @Produce json +// @Param FacilityID path string true "Facility ID" +// @Param EventID path string true "Event ID" +// @Success 200 {object} []EventSignupResponse +// @Failure 400 {object} utils.ErrResponse +// @Failure 500 {object} utils.ErrResponse +// @Router /facility/{FacilityID}/events/{EventID}/signups [get] +func GetEventSignups(w http.ResponseWriter, r *http.Request) { + event := utils.GetEventCtx(r) + signups, err := models.GetEventSignupFiltered(event.ID) + if err != nil { + log.WithError(err).Error("Error getting event signups") + utils.Render(w, r, utils.ErrInternalServer) + return + } + + if err := render.RenderList(w, r, NewEventSignupListResponse(signups)); err != nil { + utils.Render(w, r, utils.ErrInternalServer) + return + } +} + +// DeleteEventSignup godoc +// @Summary Delete an Event Signup +// @Description Delete an Event Signup +// @Tags event +// @Accept json +// @Produce json +// @Param FacilityID path string true "Facility ID" +// @Param EventID path string true "Event ID" +// @Param EventSignupID path string true "Event Signup ID" +// @Success 204 +// @Failure 400 {object} utils.ErrResponse +// @Failure 500 {object} utils.ErrResponse +// @Router /facility/{FacilityID}/events/{EventID}/signups/{EventSignupID} [delete] +func DeleteEventSignup(w http.ResponseWriter, r *http.Request) { + signup := utils.GetEventSignupCtx(r) + + if err := signup.Delete(); err != nil { + log.WithError(err).Error("Error deleting event signup") + utils.Render(w, r, utils.ErrInternalServer) + return + } + + utils.Response(r, http.StatusNoContent) +} diff --git a/external/v3/event/event_template.go b/external/v3/event/event_template.go new file mode 100644 index 0000000..9a4b487 --- /dev/null +++ b/external/v3/event/event_template.go @@ -0,0 +1,100 @@ +package event + +import ( + "github.com/VATUSA/primary-api/pkg/database/models" + "github.com/VATUSA/primary-api/pkg/utils" + "github.com/go-chi/render" + log "github.com/sirupsen/logrus" + "net/http" +) + +// GetEventTemplates godoc +// @Summary Get Event Templates +// @Description Get Event Templates by Facility +// @Tags event +// @Accept json +// @Produce json +// @Param FacilityID path string true "Facility ID" +// @Success 200 {object} []EventTemplateResponse +// @Failure 400 {object} utils.ErrResponse +// @Failure 500 {object} utils.ErrResponse +// @Router /facility/{FacilityID}/event-templates [get] +func GetEventTemplates(w http.ResponseWriter, r *http.Request) { + fac := utils.GetFacilityCtx(r) + + eventTemplates, err := models.GetEventTemplatesFiltered(fac.ID) + if err != nil { + log.WithError(err).Error("Error getting event templates") + utils.Render(w, r, utils.ErrInternalServer) + return + } + + if err := render.RenderList(w, r, NewEventTemplateListResponse(eventTemplates)); err != nil { + utils.Render(w, r, utils.ErrRender(err)) + return + } +} + +// UpdateEventTemplate godoc +// @Summary Update an Event Template +// @Description Update an Event Template +// @Tags event +// @Accept json +// @Produce json +// @Param FacilityID path string true "Facility ID" +// @Param EventTemplateID path string true "Event Template ID" +// @Param event body EventTemplateRequest true "Event Template" +// @Success 200 {object} EventTemplateResponse +// @Failure 400 {object} utils.ErrResponse +// @Failure 500 {object} utils.ErrResponse +// @Router /facility/{FacilityID}/event-templates/{EventTemplateID} [put] +func UpdateEventTemplate(w http.ResponseWriter, r *http.Request) { + et := utils.GetEventTemplateCtx(r) + + req := &EventTemplateRequest{} + if err := render.Bind(r, req); err != nil { + utils.Render(w, r, utils.ErrBadRequest) + return + } + + if err := req.Validate(); err != nil { + utils.Render(w, r, utils.ErrBadRequest) + return + } + + et.Title = req.Title + et.Positions = req.Positions + et.Facilities = req.Facilities + et.Fields = req.Fields + et.Shifts = req.Shifts + + if err := et.Update(); err != nil { + utils.Render(w, r, utils.ErrInternalServer) + return + } + + utils.Render(w, r, NewEventTemplateResponse(et)) +} + +// DeleteEventTemplate godoc +// @Summary Delete an Event Template +// @Description Delete an Event Template +// @Tags event +// @Accept json +// @Produce json +// @Param FacilityID path string true "Facility ID" +// @Param EventTemplateID path string true "Event Template ID" +// @Success 204 +// @Failure 400 {object} utils.ErrResponse +// @Failure 500 {object} utils.ErrResponse +// @Router /facility/{FacilityID}/event-templates/{EventTemplateID} [delete] +func DeleteEventTemplate(w http.ResponseWriter, r *http.Request) { + et := utils.GetEventTemplateCtx(r) + + if err := et.Delete(); err != nil { + utils.Render(w, r, utils.ErrInternalServer) + return + } + + utils.Response(r, http.StatusNoContent) +} diff --git a/external/v3/event/router.go b/external/v3/event/router.go new file mode 100644 index 0000000..a91f0df --- /dev/null +++ b/external/v3/event/router.go @@ -0,0 +1,198 @@ +package event + +import ( + "context" + "github.com/VATUSA/primary-api/pkg/database/models" + middleware "github.com/VATUSA/primary-api/pkg/go-chi/middleware/auth" + "github.com/VATUSA/primary-api/pkg/utils" + "github.com/go-chi/chi/v5" + "net/http" + "strconv" +) + +func EventRouter(r chi.Router) { + r.Get("/", GetEvents) + r.With(middleware.NotGuest, middleware.CanEditEvent).Post("/", CreateEvent) + + r.Route("/{EventID}", func(r chi.Router) { + r.Use(eventCtx) + + r.Get("/", GetEvent) + r.With(middleware.NotGuest, middleware.CanEditEvent).Put("/", UpdateEvent) + r.With(middleware.NotGuest, middleware.CanEditEvent).Patch("/", PatchEvent) + r.With(middleware.NotGuest, middleware.CanEditEvent).Delete("/", DeleteEvent) + + r.Route("/positions", func(r chi.Router) { + r.Get("/", GetEventPositions) + r.With(middleware.NotGuest, middleware.CanEditEvent).Post("/", CreateEventPosition) + + r.Route("/{EventPositionID}", func(r chi.Router) { + r.Use(positionCtx) + + r.Get("/", GetEventPosition) + r.With(middleware.NotGuest, middleware.CanEditEvent).Patch("/", PatchEventPosition) + r.With(middleware.NotGuest, middleware.CanEditEvent).Delete("/", DeleteEventPosition) + }) + }) + + r.Route("/signups", func(r chi.Router) { + r.Get("/", GetEventSignups) + r.With(middleware.NotGuest, middleware.CanEventSignup).Post("/", CreateEventSignup) + + r.Route("/{EventSignupID}", func(r chi.Router) { + r.Use(signupCtx) + + r.Get("/", GetEventSignup) + r.With(middleware.NotGuest, middleware.CanDeleteEventSignup).Delete("/", DeleteEventSignup) + }) + }) + + r.Route("/routing", func(r chi.Router) { + r.Get("/", GetEventRouting) + r.With(middleware.NotGuest, middleware.CanEditEvent).Post("/", CreateEventRouting) + + r.Route("/{EventRoutingID}", func(r chi.Router) { + r.Use(routingCtx) + + r.With(middleware.NotGuest, middleware.CanEditEvent).Patch("/", PatchEventRouting) + r.With(middleware.NotGuest, middleware.CanEditEvent).Delete("/", DeleteEventRouting) + }) + }) + }) +} + +func TemplateRouter(r chi.Router) { + r.With(middleware.NotGuest).Get("/", GetEventTemplates) + + r.Route("/{EventTemplateID}", func(r chi.Router) { + r.Use(templateCtx) + + r.With(middleware.NotGuest, middleware.CanEditEvent).Put("/", UpdateEventTemplate) + r.With(middleware.NotGuest, middleware.CanEditEvent).Delete("/", DeleteEventTemplate) + }) +} + +func eventCtx(next http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + eventID := chi.URLParam(r, "EventID") + if eventID == "" { + utils.Render(w, r, utils.ErrNotFound) + return + } + + uintEventID, err := strconv.ParseUint(eventID, 10, 64) + if err != nil { + utils.Render(w, r, utils.ErrBadRequest) + return + } + + ev := &models.Event{ID: uint(uintEventID)} + if err := ev.Get(); err != nil { + utils.Render(w, r, utils.ErrNotFound) + return + } + + ctx := context.WithValue(r.Context(), utils.EventKey{}, ev) + next.ServeHTTP(w, r.WithContext(ctx)) + }) +} + +func positionCtx(next http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + positionID := chi.URLParam(r, "EventPositionID") + if positionID == "" { + utils.Render(w, r, utils.ErrNotFound) + return + } + + uintPositionID, err := strconv.ParseUint(positionID, 10, 64) + if err != nil { + utils.Render(w, r, utils.ErrBadRequest) + return + } + + ep := &models.EventPosition{ID: uint(uintPositionID)} + if err := ep.Get(); err != nil { + utils.Render(w, r, utils.ErrNotFound) + return + } + + ctx := context.WithValue(r.Context(), utils.EventPositionKey{}, ep) + next.ServeHTTP(w, r.WithContext(ctx)) + }) +} + +func signupCtx(next http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + signupID := chi.URLParam(r, "EventSignupID") + if signupID == "" { + utils.Render(w, r, utils.ErrNotFound) + return + } + + uintSignupID, err := strconv.ParseUint(signupID, 10, 64) + if err != nil { + utils.Render(w, r, utils.ErrBadRequest) + return + } + + es := &models.EventSignup{ID: uint(uintSignupID)} + if err := es.Get(); err != nil { + utils.Render(w, r, utils.ErrNotFound) + return + } + + ctx := context.WithValue(r.Context(), utils.EventSignupKey{}, es) + next.ServeHTTP(w, r.WithContext(ctx)) + }) +} + +func routingCtx(next http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + routingID := chi.URLParam(r, "EventRoutingID") + if routingID == "" { + utils.Render(w, r, utils.ErrNotFound) + return + } + + uintRoutingID, err := strconv.ParseUint(routingID, 10, 64) + if err != nil { + utils.Render(w, r, utils.ErrBadRequest) + return + } + + er := &models.EventRouting{ID: uint(uintRoutingID)} + if err := er.Get(); err != nil { + utils.Render(w, r, utils.ErrNotFound) + return + } + + ctx := context.WithValue(r.Context(), utils.EventRoutingKey{}, er) + next.ServeHTTP(w, r.WithContext(ctx)) + }) +} + +func templateCtx(next http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + templateID := chi.URLParam(r, "EventTemplateID") + if templateID == "" { + utils.Render(w, r, utils.ErrNotFound) + return + } + + uintTemplateID, err := strconv.ParseUint(templateID, 10, 64) + if err != nil { + utils.Render(w, r, utils.ErrBadRequest) + return + } + + et := &models.EventTemplate{ID: uint(uintTemplateID)} + if err := et.Get(); err != nil { + utils.Render(w, r, utils.ErrNotFound) + return + } + + ctx := context.WithValue(r.Context(), utils.EventTemplateKey{}, et) + next.ServeHTTP(w, r.WithContext(ctx)) + }) +} diff --git a/external/v3/event/struct.go b/external/v3/event/struct.go new file mode 100644 index 0000000..152b497 --- /dev/null +++ b/external/v3/event/struct.go @@ -0,0 +1,202 @@ +package event + +import ( + "github.com/VATUSA/primary-api/pkg/constants" + "github.com/VATUSA/primary-api/pkg/database/models" + "github.com/go-chi/render" + "net/http" + "time" +) + +type EventTemplateRequest struct { + Title string `json:"title" example:"KDEN FNO" validate:"required"` + Positions []string `json:"positions" example:"[\"ZDV_APP\", \"ZDV_TWR\"]" validate:"required"` + Facilities []constants.FacilityID `json:"facilities" example:"[\"ZDV\", \"ZAB\", \"ZLC\"]" validate:"required"` + Fields []string `json:"fields" example:"[\"KDEN\", \"KBJC\", \"KAPA\"]" validate:"required"` + Shifts bool `json:"shifts" example:"true"` +} + +func (req *EventTemplateRequest) Validate() error { + return nil +} + +func (req *EventTemplateRequest) Bind(r *http.Request) error { + return nil +} + +type EventTemplateResponse struct { + *models.EventTemplate +} + +func NewEventTemplateResponse(et *models.EventTemplate) *EventTemplateResponse { + return &EventTemplateResponse{EventTemplate: et} +} + +func (res *EventTemplateResponse) Render(w http.ResponseWriter, r *http.Request) error { + if res.EventTemplate == nil { + return nil + } + return nil +} + +func NewEventTemplateListResponse(ets []models.EventTemplate) []render.Renderer { + list := []render.Renderer{} + for idx := range ets { + list = append(list, NewEventTemplateResponse(&ets[idx])) + } + return list +} + +type EventRequest struct { + Title string `json:"title" example:"ZDV FNO" validate:"required"` + Description string `json:"description" example:"Join us for a fun night of flying in and out of Denver!" validate:"required"` + BannerURL string `json:"banner_url" example:"https://zdvartcc.org/banner.jpg"` + StartDate time.Time `json:"start_date" example:"2021-01-01T00:00:00Z" validate:"required"` + EndDate time.Time `json:"end_date" example:"2021-01-01T00:00:00Z" validate:"required"` + Fields []string `json:"fields" example:"[\"KDEN\", \"KBJC\", \"KAPA\"]" validate:"required"` + Facilities []constants.FacilityID `json:"facilities" example:"[\"ZDV\", \"ZAB\", \"ZLC\"]" validate:"required"` +} + +func (req *EventRequest) Validate() error { + return nil +} + +func (req *EventRequest) Bind(r *http.Request) error { + return nil +} + +type EventResponse struct { + *models.Event +} + +func NewEventResponse(e *models.Event) *EventResponse { + return &EventResponse{Event: e} +} + +func (res *EventResponse) Render(w http.ResponseWriter, r *http.Request) error { + if res.Event == nil { + return nil + } + return nil +} + +func NewEventListResponse(es []models.Event) []render.Renderer { + list := []render.Renderer{} + for idx := range es { + list = append(list, NewEventResponse(&es[idx])) + } + return list +} + +type EventPositionRequest struct { + Position string `json:"position" example:"ZDV_APP" validate:"required"` + Assignee *uint `json:"assignee" example:"1293257"` + Shifts *bool `json:"shifts" example:"true"` + SecondaryAssignee *uint `json:"secondary_assignee" example:"1293257"` +} + +func (req *EventPositionRequest) Validate() error { + return nil +} + +func (req *EventPositionRequest) Bind(r *http.Request) error { + return nil +} + +type EventPositionResponse struct { + *models.EventPosition +} + +func NewEventPositionResponse(ep *models.EventPosition) *EventPositionResponse { + return &EventPositionResponse{EventPosition: ep} +} + +func (res *EventPositionResponse) Render(w http.ResponseWriter, r *http.Request) error { + if res.EventPosition == nil { + return nil + } + return nil +} + +func NewEventPositionListResponse(eps []models.EventPosition) []render.Renderer { + list := []render.Renderer{} + for idx := range eps { + list = append(list, NewEventPositionResponse(&eps[idx])) + } + return list +} + +type EventSignupRequest struct { + PositionID uint `json:"position_id" example:"1" validate:"required"` + CID uint `json:"cid" example:"1293257" validate:"required"` + Shift uint `json:"shift" example:"1" validate:"required"` // 1 = Primary, 2 = Secondary +} + +func (req *EventSignupRequest) Validate() error { + return nil +} + +func (req *EventSignupRequest) Bind(r *http.Request) error { + return nil +} + +type EventSignupResponse struct { + *models.EventSignup +} + +func NewEventSignupResponse(es *models.EventSignup) *EventSignupResponse { + return &EventSignupResponse{EventSignup: es} +} + +func (res *EventSignupResponse) Render(w http.ResponseWriter, r *http.Request) error { + if res.EventSignup == nil { + return nil + } + return nil +} + +func NewEventSignupListResponse(ess []models.EventSignup) []render.Renderer { + list := []render.Renderer{} + for idx := range ess { + list = append(list, NewEventSignupResponse(&ess[idx])) + } + return list +} + +type EventRoutingRequest struct { + Origin string `json:"origin" example:"ZDV" validate:"required"` + Destination string `json:"destination" example:"ZAB" validate:"required"` + Routing string `json:"routing" example:"ZDV J80 DBL J80 FQF J80 HCT J80 HBU J80 HCT J80 FQF J80 DBL J80 ZAB" validate:"required"` + Notes string `json:"notes" example:"Expect vectors to final at DBL" validate:"required"` +} + +func (req *EventRoutingRequest) Validate() error { + return nil +} + +func (req *EventRoutingRequest) Bind(r *http.Request) error { + return nil +} + +type EventRoutingResponse struct { + *models.EventRouting +} + +func NewEventRoutingResponse(er *models.EventRouting) *EventRoutingResponse { + return &EventRoutingResponse{EventRouting: er} +} + +func (res *EventRoutingResponse) Render(w http.ResponseWriter, r *http.Request) error { + if res.EventRouting == nil { + return nil + } + return nil +} + +func NewEventRoutingListResponse(ers []models.EventRouting) []render.Renderer { + list := []render.Renderer{} + for idx := range ers { + list = append(list, NewEventRoutingResponse(&ers[idx])) + } + return list +} diff --git a/external/v3/facility/router.go b/external/v3/facility/router.go index d74353b..55a2c6e 100644 --- a/external/v3/facility/router.go +++ b/external/v3/facility/router.go @@ -2,6 +2,7 @@ package facility import ( "context" + "github.com/VATUSA/primary-api/external/v3/event" facility_log "github.com/VATUSA/primary-api/external/v3/facility-log" "github.com/VATUSA/primary-api/external/v3/faq" "github.com/VATUSA/primary-api/external/v3/feedback" @@ -28,6 +29,14 @@ func Router(r chi.Router) { r.With(middleware.NotGuest, middleware.CanEditFacility).Patch("/", PatchFacility) r.With(middleware.NotGuest, middleware.CanEditFacility).Post("/reset-api-key", ResetApiKey) + r.Route("/event-templates", func(r chi.Router) { + event.TemplateRouter(r) + }) + + r.Route("/events", func(r chi.Router) { + event.EventRouter(r) + }) + r.Route("/log", func(r chi.Router) { facility_log.Router(r) }) diff --git a/pkg/database/models/event.go b/pkg/database/models/event.go new file mode 100644 index 0000000..6943aa5 --- /dev/null +++ b/pkg/database/models/event.go @@ -0,0 +1,77 @@ +package models + +import ( + "database/sql/driver" + "encoding/json" + "github.com/VATUSA/primary-api/pkg/constants" + "github.com/VATUSA/primary-api/pkg/database" + "time" +) + +type Event struct { + ID uint `json:"id" gorm:"primaryKey" example:"1"` + + Title string `json:"title" gorm:"not null" example:"ZDV FNO"` + Description string `json:"description" gorm:"not null" example:"Join us for a fun night of flying in and out of Denver!"` + BannerURL string `json:"banner_url" example:"https://zdvartcc.org/banner.jpg"` + StartDate time.Time `json:"start_date" gorm:"not null" example:"2021-01-01T00:00:00Z"` + EndDate time.Time `json:"end_date" gorm:"not null" example:"2021-01-01T00:00:00Z"` + Fields Fields `json:"fields" gorm:"type:json" example:"[\"KDEN\", \"KBJC\", \"KAPA\"]"` + Facilities Facilities `json:"facilities" gorm:"type:json" example:"[\"ZDV\", \"ZAB\", \"ZLC\"]"` + + Positions []EventPosition `json:"positions" gorm:"foreignKey:EventID"` + Routing []EventRouting `json:"routing" gorm:"foreignKey:EventID"` + + CreatedAt time.Time `json:"created_at" example:"2021-01-01T00:00:00Z"` + UpdatedAt time.Time `json:"updated_at" example:"2021-01-01T00:00:00Z"` +} + +type Fields []string + +func (f *Fields) Scan(value interface{}) error { + return json.Unmarshal(value.([]byte), f) +} + +func (f *Fields) Value() (driver.Value, error) { + return json.Marshal(f) +} + +type Facilities []constants.FacilityID + +func (f *Facilities) Scan(value interface{}) error { + return json.Unmarshal(value.([]byte), f) +} + +func (f *Facilities) Value() (driver.Value, error) { + return json.Marshal(f) +} + +func (e *Event) Create() error { + return database.DB.Create(e).Error +} + +func (e *Event) Get() error { + return database.DB.Preload("Positions").Preload("Routing").First(e).Error +} + +func (e *Event) Update() error { + return database.DB.Save(e).Error +} + +func (e *Event) Delete() error { + return database.DB.Delete(e).Error +} + +func GetEventsFiltered(facilityID constants.FacilityID, afterDate time.Time) ([]Event, error) { + var events []Event + query := database.DB + if !afterDate.IsZero() { + query = query.Where("start_date > ?", afterDate) + } + if facilityID != "" { + // FIXME: idk if this query works + query = query.Where("facilities @> ?", []constants.FacilityID{facilityID}) + } + + return events, query.Find(&events).Error +} diff --git a/pkg/database/models/event_position.go b/pkg/database/models/event_position.go new file mode 100644 index 0000000..d302168 --- /dev/null +++ b/pkg/database/models/event_position.go @@ -0,0 +1,49 @@ +package models + +import ( + "github.com/VATUSA/primary-api/pkg/constants" + "github.com/VATUSA/primary-api/pkg/database" + "time" +) + +type EventPosition struct { + ID uint `json:"id" gorm:"primaryKey" example:"1"` + EventID uint `json:"event_id" gorm:"not null" example:"1"` + + Facility constants.FacilityID `json:"facility" example:"ZDV"` + Position string `json:"position" gorm:"not null" example:"ZDV_APP"` + Assignee uint `json:"assignee" example:"1293257"` + Shifts bool `json:"shifts" gorm:"not null;default:false" example:"true"` + SecondaryAssignee uint `json:"secondary_assignee" example:"1293257"` + + Signups []EventSignup `json:"signups" gorm:"foreignKey:PositionID"` + + CreatedAt time.Time `json:"created_at" example:"2021-01-01T00:00:00Z"` + UpdatedAt time.Time `json:"updated_at" example:"2021-01-01T00:00:00Z"` +} + +func (ep *EventPosition) Create() error { + return database.DB.Create(ep).Error +} + +func (ep *EventPosition) Get() error { + return database.DB.Preload("Signups").First(ep).Error +} + +func (ep *EventPosition) Update() error { + return database.DB.Save(ep).Error +} + +func (ep *EventPosition) Delete() error { + return database.DB.Delete(ep).Error +} + +func GetEventPositionsFiltered(eventId uint, facilityID constants.FacilityID) ([]EventPosition, error) { + var positions []EventPosition + query := database.DB.Where("event_id = ?", eventId) + if facilityID != "" { + query = query.Where("facility = ?", facilityID) + } + + return positions, query.Find(&positions).Error +} diff --git a/pkg/database/models/event_routing.go b/pkg/database/models/event_routing.go new file mode 100644 index 0000000..8662a71 --- /dev/null +++ b/pkg/database/models/event_routing.go @@ -0,0 +1,44 @@ +package models + +import ( + "github.com/VATUSA/primary-api/pkg/database" + "time" +) + +type EventRouting struct { + ID uint `json:"id" gorm:"primaryKey" example:"1"` + EventID uint `json:"event_id" gorm:"not null" example:"1"` + + Origin string `json:"origin" example:"KLAX"` + Destination string `json:"destination" example:"KDEN"` + Routing string `json:"routing" example:"DCT"` + Notes string `json:"notes" example:"JETS ONLY"` + + CreatedAt time.Time `json:"created_at" example:"2021-01-01T00:00:00Z"` + UpdatedAt time.Time `json:"updated_at" example:"2021-01-01T00:00:00Z"` +} + +func (er *EventRouting) Create() error { + return database.DB.Create(er).Error +} + +func (er *EventRouting) Get() error { + return database.DB.First(er).Error +} + +func (er *EventRouting) Update() error { + return database.DB.Save(er).Error +} + +func (er *EventRouting) Delete() error { + return database.DB.Delete(er).Error +} + +func GetEventRoutingFiltered(eventID uint) ([]EventRouting, error) { + var routing []EventRouting + query := database.DB + if eventID != 0 { + query = query.Where("event_id = ?", eventID) + } + return routing, query.Find(&routing).Error +} diff --git a/pkg/database/models/event_signup.go b/pkg/database/models/event_signup.go new file mode 100644 index 0000000..99f9a78 --- /dev/null +++ b/pkg/database/models/event_signup.go @@ -0,0 +1,43 @@ +package models + +import ( + "github.com/VATUSA/primary-api/pkg/database" + "time" +) + +type EventSignup struct { + ID uint `json:"id" gorm:"primaryKey" example:"1"` + EventID uint `json:"event_id" gorm:"not null" example:"1"` + + PositionID uint `json:"position_id" gorm:"not null" example:"1"` + CID uint `json:"cid" gorm:"not null" example:"1293257"` + Shift uint `json:"shift" gorm:"not null;default:1" example:"1"` // 1 = Primary, 2 = Secondary + + CreatedAt time.Time `json:"created_at" example:"2021-01-01T00:00:00Z"` + UpdatedAt time.Time `json:"updated_at" example:"2021-01-01T00:00:00Z"` +} + +func (es *EventSignup) Create() error { + return database.DB.Create(es).Error +} + +func (es *EventSignup) Get() error { + return database.DB.First(es).Error +} + +func (es *EventSignup) Update() error { + return database.DB.Save(es).Error +} + +func (es *EventSignup) Delete() error { + return database.DB.Delete(es).Error +} + +func GetEventSignupFiltered(eventID uint) ([]EventSignup, error) { + var signups []EventSignup + query := database.DB + if eventID != 0 { + query = query.Where("event_id = ?", eventID) + } + return signups, query.Find(&signups).Error +} diff --git a/pkg/database/models/event_template.go b/pkg/database/models/event_template.go new file mode 100644 index 0000000..10e2642 --- /dev/null +++ b/pkg/database/models/event_template.go @@ -0,0 +1,66 @@ +package models + +import ( + "database/sql/driver" + "encoding/json" + "github.com/VATUSA/primary-api/pkg/constants" + "github.com/VATUSA/primary-api/pkg/database" + "time" +) + +type EventTemplate struct { + ID uint `json:"id" gorm:"primaryKey" example:"1"` + + Title string `json:"title" gorm:"not null" example:"KDEN FNO Template"` + Positions DefaultPositions `json:"positions" gorm:"type:json" example:"[\"ZDV_APP\", \"ZDV_TWR\"]"` + Facilities DefaultFacilities `json:"facilities" gorm:"type:json" example:"[\"ZDV\", \"ZAB\", \"ZLC\"]"` + Fields Fields `json:"fields" gorm:"type:json" example:"[\"KDEN\", \"KBJC\", \"KAPA\"]"` + Shifts bool `json:"shifts" gorm:"not null;default:false" example:"true"` + + CreatedAt time.Time `json:"created_at" example:"2021-01-01T00:00:00Z"` + UpdatedAt time.Time `json:"updated_at" example:"2021-01-01T00:00:00Z"` +} + +type DefaultPositions []string + +func (f *DefaultPositions) Scan(value interface{}) error { + return json.Unmarshal(value.([]byte), f) +} + +func (f *DefaultPositions) Value() (driver.Value, error) { + return json.Marshal(f) +} + +type DefaultFacilities []constants.FacilityID + +func (f *DefaultFacilities) Scan(value interface{}) error { + return json.Unmarshal(value.([]byte), f) +} + +func (f *DefaultFacilities) Value() (driver.Value, error) { + return json.Marshal(f) +} + +func (et *EventTemplate) Create() error { + return database.DB.Create(et).Error +} + +func (et *EventTemplate) Get() error { + return database.DB.First(et).Error +} + +func (et *EventTemplate) Update() error { + return database.DB.Save(et).Error +} + +func (et *EventTemplate) Delete() error { + return database.DB.Delete(et).Error +} + +func GetEventTemplatesFiltered(facility constants.FacilityID) ([]EventTemplate, error) { + var ets []EventTemplate + if err := database.DB.Where("facilities LIKE ?", "%"+facility+"%").Find(&ets).Error; err != nil { + return nil, err + } + return ets, nil +} diff --git a/pkg/database/models/setup.go b/pkg/database/models/setup.go index d47e0d1..d14db72 100644 --- a/pkg/database/models/setup.go +++ b/pkg/database/models/setup.go @@ -12,6 +12,11 @@ func AutoMigrate() { &ActionLogEntry{}, &DisciplinaryLogEntry{}, &Document{}, + &Event{}, + &EventPosition{}, + &EventSignup{}, + &EventRouting{}, + &EventTemplate{}, &FacilityLogEntry{}, &FAQ{}, &Feedback{}, @@ -36,6 +41,11 @@ func DropTables() { &ActionLogEntry{}, &DisciplinaryLogEntry{}, &Document{}, + &EventPosition{}, + &EventSignup{}, + &EventRouting{}, + &EventTemplate{}, + &Event{}, &FacilityLogEntry{}, &FAQ{}, &Feedback{}, diff --git a/pkg/go-chi/middleware/auth/event.go b/pkg/go-chi/middleware/auth/event.go new file mode 100644 index 0000000..fcf12d2 --- /dev/null +++ b/pkg/go-chi/middleware/auth/event.go @@ -0,0 +1,123 @@ +package middleware + +import ( + "encoding/json" + "github.com/VATUSA/primary-api/pkg/utils" + log "github.com/sirupsen/logrus" + "net/http" +) + +func CanEditEvent(next http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + targetFacility := utils.GetFacilityCtx(r) + credentials := GetCredentials(r) + if credentials.User != nil { + if utils.IsVATUSAStaff(credentials.User) { + next.ServeHTTP(w, r) + return + } + + if utils.IsFacilitySeniorStaff(credentials.User, targetFacility.ID) { + next.ServeHTTP(w, r) + return + } + + if utils.IsFacilityEventsStaff(credentials.User, targetFacility.ID) { + next.ServeHTTP(w, r) + return + } + + log.Warnf("User %d, attempted to edit event: %s. No permissions.", credentials.User.CID, targetFacility.ID) + } + + if credentials.Facility != nil { + if credentials.Facility.ID == targetFacility.ID { + next.ServeHTTP(w, r) + return + } + + log.Warnf("Facility API Key %s, attempted to edit event: %s. No permissions.", credentials.Facility.ID, targetFacility.ID) + } + + utils.Render(w, r, utils.ErrForbidden) + }) +} + +type EventSignupRequest struct { + CID uint `json:"cid"` +} + +func CanEventSignup(next http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + targetFacility := utils.GetFacilityCtx(r) + req := EventSignupRequest{} + + if err := json.NewDecoder(r.Body).Decode(&req); err != nil { + utils.Render(w, r, utils.ErrBadRequest) + return + } + + credentials := GetCredentials(r) + if credentials.User != nil { + if credentials.User.CID == req.CID { + next.ServeHTTP(w, r) + return + } + + log.Warnf("User %d, attempted to signup for event as: %d. No permissions.", credentials.User.CID, req.CID) + } + + if credentials.Facility != nil { + if credentials.Facility.ID == targetFacility.ID { + next.ServeHTTP(w, r) + return + } + + log.Warnf("Facility API Key %s, attempted to signup for event: %s. No permissions.", credentials.Facility.ID, targetFacility.ID) + } + + utils.Render(w, r, utils.ErrForbidden) + }) +} + +func CanDeleteEventSignup(next http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + targetFacility := utils.GetFacilityCtx(r) + signup := utils.GetEventSignupCtx(r) + credentials := GetCredentials(r) + if credentials.User != nil { + if credentials.User.CID == signup.CID { + next.ServeHTTP(w, r) + return + } + + if utils.IsVATUSAStaff(credentials.User) { + next.ServeHTTP(w, r) + return + } + + if utils.IsFacilitySeniorStaff(credentials.User, targetFacility.ID) { + next.ServeHTTP(w, r) + return + } + + if utils.IsFacilityEventsStaff(credentials.User, targetFacility.ID) { + next.ServeHTTP(w, r) + return + } + + log.Warnf("User %d, attempted to delete event signup: %d. No permissions.", credentials.User.CID, signup.ID) + } + + if credentials.Facility != nil { + if credentials.Facility.ID == targetFacility.ID { + next.ServeHTTP(w, r) + return + } + + log.Warnf("Facility API Key %s, attempted to delete event signup: %d. No permissions.", credentials.Facility.ID, signup.ID) + } + + utils.Render(w, r, utils.ErrForbidden) + }) +} diff --git a/pkg/utils/context.go b/pkg/utils/context.go index cbbce25..0404fb5 100644 --- a/pkg/utils/context.go +++ b/pkg/utils/context.go @@ -35,6 +35,56 @@ func GetDocumentCtx(r *http.Request) *models.Document { return doc } +type EventKey struct{} + +func GetEventCtx(r *http.Request) *models.Event { + ev, ok := r.Context().Value(EventKey{}).(*models.Event) + if !ok { + return nil + } + return ev +} + +type EventPositionKey struct{} + +func GetEventPositionCtx(r *http.Request) *models.EventPosition { + ep, ok := r.Context().Value(EventPositionKey{}).(*models.EventPosition) + if !ok { + return nil + } + return ep +} + +type EventSignupKey struct{} + +func GetEventSignupCtx(r *http.Request) *models.EventSignup { + es, ok := r.Context().Value(EventSignupKey{}).(*models.EventSignup) + if !ok { + return nil + } + return es +} + +type EventRoutingKey struct{} + +func GetEventRoutingCtx(r *http.Request) *models.EventRouting { + er, ok := r.Context().Value(EventRoutingKey{}).(*models.EventRouting) + if !ok { + return nil + } + return er +} + +type EventTemplateKey struct{} + +func GetEventTemplateCtx(r *http.Request) *models.EventTemplate { + et, ok := r.Context().Value(EventTemplateKey{}).(*models.EventTemplate) + if !ok { + return nil + } + return et +} + type FacilityKey struct{} func GetFacilityCtx(r *http.Request) *models.Facility { diff --git a/pkg/utils/perms.go b/pkg/utils/perms.go index ef48ecb..55bc023 100644 --- a/pkg/utils/perms.go +++ b/pkg/utils/perms.go @@ -55,6 +55,20 @@ func IsFacilityStaff(user *models.User, facility constants.FacilityID) bool { return false } +func IsFacilityEventsStaff(user *models.User, facility constants.FacilityID) bool { + for _, roster := range user.Roster { + if roster.Facility == facility { + for _, roles := range roster.Roles { + if roles.RoleID == constants.EventCoordinatorRole || roles.RoleID == constants.AssistantEventCoordinator { + return true + } + } + } + } + + return false +} + func CanEditFacility(user *models.User, targetFacility *models.Facility) bool { if IsVATUSAStaff(user) { return true