From 6408aa7f93ae829c732719f90fad0c85e2dfaed7 Mon Sep 17 00:00:00 2001 From: Mia Bennett Date: Wed, 21 Aug 2024 16:30:43 +0930 Subject: [PATCH] feat(bookings): update_induction endpoint (#331) * test(bookings): visitor-kiosk visitor checkin flow * test(bookings): match observed flow * chore(shards): update shards * test(induction): test update_induction endpoint * feat(bookings): add update_induction endpoint * fix(bookings): enum name * doc(openapi): doc gen * test(bookings): test visitor checking flow * chore(ameba): update ameba config * doc(bookings): lowercase example --- .ameba.yml | 23 +++++- OPENAPI_DOC.yml | 127 +++++++++++++++++++++++++++++- shard.lock | 2 +- spec/controllers/bookings_spec.cr | 42 ++++++++++ src/controllers/bookings.cr | 17 +++- 5 files changed, 205 insertions(+), 6 deletions(-) diff --git a/.ameba.yml b/.ameba.yml index 1a6e988c..d41f6687 100644 --- a/.ameba.yml +++ b/.ameba.yml @@ -36,4 +36,25 @@ Style/PredicateName: Enabled: false Lint/LiteralsComparison: - Enabled: false \ No newline at end of file + Enabled: false + +Naming/BlockParameterName: + Enabled: false + +Naming/AccessorMethodName: + Enabled: false + +Naming/PredicateName: + Enabled: false + +Documentation/DocumentationAdmonition: + Enabled: false + +Lint/SpecFilename: + Excluded: + - spec/controllers/helpers/* + - spec/migration/**/* + +Lint/UselessAssign: + Excluded: + - spec/**/* diff --git a/OPENAPI_DOC.yml b/OPENAPI_DOC.yml index 1022fa5c..38941416 100644 --- a/OPENAPI_DOC.yml +++ b/OPENAPI_DOC.yml @@ -2375,6 +2375,119 @@ paths: application/json: schema: $ref: '#/components/schemas/Bookings__BookingError' + /api/staff/v1/bookings/{id}/update_induction: + post: + summary: update the induction status + tags: + - Bookings + operationId: Bookings_update_induction + parameters: + - name: id + in: path + required: true + schema: + type: integer + format: Int64 + - name: induction + in: query + description: the induction status of the booking + example: accepted + required: true + schema: + type: string + enum: + - tentative + - accepted + - declined + - name: utm_source + in: query + description: provided for use with analytics + example: mobile + required: false + schema: + type: string + nullable: true + responses: + 200: + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/PlaceOS__Model__Booking' + 429: + description: Too Many Requests + content: + application/json: + schema: + $ref: '#/components/schemas/Application__CommonError' + 400: + description: Bad Request + content: + application/json: + schema: + $ref: '#/components/schemas/Application__CommonError' + 401: + description: Unauthorized + content: + application/json: + schema: + $ref: '#/components/schemas/Application__CommonError' + 403: + description: Forbidden + 404: + description: Not Found + content: + application/json: + schema: + $ref: '#/components/schemas/Application__CommonError' + 511: + description: Network Authentication Required + content: + application/json: + schema: + $ref: '#/components/schemas/Application__CommonError' + 406: + description: Not Acceptable + content: + application/json: + schema: + $ref: '#/components/schemas/Application__ContentError' + 415: + description: Unsupported Media Type + content: + application/json: + schema: + $ref: '#/components/schemas/Application__ContentError' + 422: + description: Unprocessable Entity + content: + application/json: + schema: + $ref: '#/components/schemas/Application__ValidationError' + 500: + description: Internal Server Error + content: + application/json: + schema: + $ref: '#/components/schemas/Application__CommonError' + 405: + description: Method Not Allowed + content: + application/json: + schema: + $ref: '#/components/schemas/Application__CommonError' + 409: + description: Conflict + content: + application/json: + schema: + $ref: '#/components/schemas/Bookings__BookingError' + 410: + description: Gone + content: + application/json: + schema: + $ref: '#/components/schemas/Bookings__BookingError' /api/staff/v1/bookings/{id}/guests: get: summary: returns a list of guests associated with a booking @@ -9651,7 +9764,12 @@ components: type: string nullable: true induction: - type: boolean + type: string + enum: + - tentative + - accepted + - declined + description: The induction status of the booking. Defaults to TENTATIVE. nullable: true permission: type: string @@ -12073,7 +12191,12 @@ components: type: string nullable: true induction: - type: boolean + type: string + enum: + - tentative + - accepted + - declined + description: The induction status of the booking. Defaults to TENTATIVE. nullable: true permission: type: string diff --git a/shard.lock b/shard.lock index 5fad4bbf..0dcd8431 100644 --- a/shard.lock +++ b/shard.lock @@ -167,7 +167,7 @@ shards: placeos-models: git: https://github.com/placeos/models.git - version: 9.56.5 + version: 9.57.5 pool: git: https://github.com/ysbaddaden/pool.git diff --git a/spec/controllers/bookings_spec.cr b/spec/controllers/bookings_spec.cr index 321e473d..20b55ad5 100644 --- a/spec/controllers/bookings_spec.cr +++ b/spec/controllers/bookings_spec.cr @@ -2234,4 +2234,46 @@ describe Bookings do booking.deleted.should be_true booking.children.not_nil!.all? { |b| b.deleted == true }.should be_true end + + context "[visitor-kiosk]", tags: ["visitor-kiosk"] do + it "checks in a visitor" do + WebMock.stub(:post, "#{ENV["PLACE_URI"]}/auth/oauth/token") + .to_return(body: File.read("./spec/fixtures/tokens/placeos_token.json")) + WebMock.stub(:post, "#{ENV["PLACE_URI"]}/api/engine/v2/signal?channel=staff/booking/changed") + .to_return(body: "") + WebMock.stub(:post, "#{ENV["PLACE_URI"]}/api/engine/v2/signal?channel=staff/guest/attending") + .to_return(body: "") + + tenant = get_tenant + + starting = Random.new.rand(5..19).minutes.from_now.to_unix + ending = Random.new.rand(25..39).minutes.from_now.to_unix + + visitor_email = Faker::Internet.email + + # Create booking with attendee + create_booking_response = client.post(BOOKINGS_BASE, headers: headers, + body: %({"asset_id":"room_one","booking_start":#{starting},"booking_end":#{ending},"booking_type":"room","attendees": [ + { + "name": "#{Faker::Name.first_name}", + "email": "#{visitor_email}", + "checked_in": false, + "visit_expected": true + }]}) + ) + create_booking_response.status_code.should eq(201) + booking_id = JSON.parse(create_booking_response.body)["id"] + + # Find guest by email + guest_response = client.get("#{GUESTS_BASE}/#{visitor_email}", headers: headers) + guest_response.status_code.should eq(200) + guest_id = JSON.parse(guest_response.body)["id"] + + # update induction state on booking + update_induction_response = client.post("#{BOOKINGS_BASE}/#{booking_id}/update_induction?induction=accepted", headers: headers) + update_induction_response.status_code.should eq(200) + booking = Booking.from_json(update_induction_response.body) + booking.induction.should eq(PlaceOS::Model::Booking::Induction::ACCEPTED) + end + end end diff --git a/src/controllers/bookings.cr b/src/controllers/bookings.cr index 40d15b36..8c96025e 100644 --- a/src/controllers/bookings.cr +++ b/src/controllers/bookings.cr @@ -56,7 +56,7 @@ class Bookings < Application .limit(1).to_a.first { raise Error::NotFound.new("could not find booking with id: #{id}") } end - @[AC::Route::Filter(:before_action, only: [:update, :update_alt, :destroy, :update_state])] + @[AC::Route::Filter(:before_action, only: [:update, :update_alt, :destroy, :update_state, :update_induction])] private def confirm_access return if is_support? if user = current_user @@ -515,7 +515,7 @@ class Bookings < Application original_assets = existing_booking.asset_ids existing_booking.instance = instance - {% for key in [:asset_id, :asset_ids, :zones, :booking_start, :booking_end, :title, :description, :images] %} + {% for key in [:asset_id, :asset_ids, :zones, :booking_start, :booking_end, :title, :description, :images, :induction] %} begin existing_booking.{{key.id}} = changes.{{key.id}} if changes.{{key.id}}_present? rescue NilAssertionError @@ -807,6 +807,19 @@ class Bookings < Application update_booking(booking, "process_state") end + # update the induction status + @[AC::Route::POST("/:id/update_induction")] + def update_induction( + @[AC::Param::Info(description: "the induction status of the booking", example: "accepted")] + induction : PlaceOS::Model::Booking::Induction, + @[AC::Param::Info(description: "provided for use with analytics", example: "mobile")] + utm_source : String? = nil + ) : Booking + booking.induction = induction + booking.utm_source = utm_source + update_booking(booking, "induction") + end + # returns a list of guests associated with a booking @[AC::Route::GET("/:id/guests")] def guest_list : Array(Guest)