openactive / openactive-test-suite

Test suite for OpenActive implementations
MIT License
2 stars 9 forks source link

Support for IndividualFacilityUse Slots #484

Closed nickevansuk closed 2 years ago

nickevansuk commented 2 years ago

The broker-microservice does not currently track IndividualFacilityUseSlot

The following is an example of a Slot for an IndividualFacilityUse within a FacilityUse represented in a C1 response:

{
  "@context": "https://openactive.io/",
  "@type": "Slot",
  "@id": "http://www.example.org/facility-uses/1/individual-facility-uses/1#/slots/2018-03-01T10:00:00Z",
  "facilityUse": {
    "@type": "IndividualFacilityUse",
    "@id": "http://www.example.org/facility-uses/1/individual-facility-uses/1",
    "name": "Outdoor Tennis on Main Tennis Court 1",
    "beta:sportsActivityLocation": [
      {
        "type": "SportsActivityLocation",
        "name": "Main Tennis Court 1",
        "containedInPlace": "http://www.example.org/api/locations/8958f9b8-2004-4e90-80ff-50c98a9b121d"
      }
    ],
    "aggregateFacilityUse": {
      "@type": "FacilityUse",
      "@id": "http://www.example.org/facility-uses/1",
      "name": "Outdoor Tennis",
      "description": "Tennis courts are available to hire for thirty minute slots",
      "facilityType": [
        {
          "@type": "Concept",
          "@id": "https://openactive.io/facility-types#bba8ae59-d152-40bc-85cc-88c5375696d4",
          "prefLabel": "Tennis Court",
          "inScheme": "https://openactive.io/facility-types"
        }
      ],
      "provider": {
        "@type": "Organization",
        "@id": "https://id.bookingsystem.example.com/organizers/1",
        "name": "Everyone Active",
        "url": "https://www.everyoneactive.com/",
        "logo": {
          "@type": "ImageObject",
          "url": "https://s3-eu-west-2.amazonaws.com/prod-everyoneactive-wp/wp-content/uploads/2017/10/26092236/Ea-Image2.jpg"
        },
        "telephone": "01455 890508",
        "sameAs": [
          "https://www.facebook.com/everyoneactive/",
          "https://twitter.com/everyoneactive"
        ]
      },
      "location": {
        "@type": "Place",
        "@id": "http://www.example.org/api/locations/8958f9b8-2004-4e90-80ff-50c98a9b121d",
        "url": "https://www.everyoneactive.com/centres/The-Castle-Centre",
        "name": "Example Leisure Centre",
        "description": "You can enjoy a huge range of different sports and activities at Middlesbrough Sports Village. There’s a state-of-the-art gym, a full programme of group exercise classes, indoor badminton sessions and a soft play area for kids. Outside, the Village boasts a full range of athletics facilities, including an eight-lane 400m track, a 10-lane 100m track, as well as four long jump pits. There’s also high jump, hammer throwing, javelin and pole vaulting facilities. The latest addition to the facility is a £1.6m, 250m velodrome, alongside five all-weather outdoor pitches and a skate park for BMX bikes, scooters and inline skaters.",
        "address": {
          "@type": "PostalAddress",
          "streetAddress": "1 High Street",
          "addressLocality": "Bristol",
          "addressRegion": "West England",
          "postalCode": "BS1 4SD",
          "addressCountry": "GB"
        },
        "telephone": "08448 933888",
        "geo": {
          "@type": "GeoCoordinates",
          "latitude": 51.5282923438685,
          "longitude": -0.206177163090501
        },
        "image": [
          {
            "@type": "ImageObject",
            "url": "https://s3-eu-west-2.amazonaws.com/prod-everyoneactive-wp/wp-content/uploads/2018/04/13112215/Acton_HeaderBanner.png"
          }
        ],
        "amenityFeature": [
          {
            "name": "Changing Facilities",
            "value": true,
            "@type": "ChangingFacilities"
          },
          {
            "name": "Showers",
            "value": false,
            "@type": "Showers"
          },
          {
            "name": "Lockers",
            "value": true,
            "@type": "Lockers"
          },
          {
            "name": "Towels",
            "value": false,
            "@type": "Towels"
          },
          {
            "name": "Creche",
            "value": false,
            "@type": "Creche"
          },
          {
            "name": "Parking",
            "value": true,
            "@type": "Parking"
          }
        ],
        "openingHoursSpecification": [
          {
            "@type": "OpeningHoursSpecification",
            "dayOfWeek": [
              "https://schema.org/Monday",
              "https://schema.org/Tuesday",
              "https://schema.org/Wednesday",
              "https://schema.org/Thursday"
            ],
            "opens": "06:00",
            "closes": "13:00"
          },
          {
            "@type": "OpeningHoursSpecification",
            "dayOfWeek": [
              "https://schema.org/Monday",
              "https://schema.org/Tuesday",
              "https://schema.org/Wednesday",
              "https://schema.org/Thursday"
            ],
            "opens": "16:00",
            "closes": "20:00"
          },
          {
            "@type": "OpeningHoursSpecification",
            "dayOfWeek": [
              "https://schema.org/Friday"
            ],
            "opens": "06:00",
            "closes": "12:00"
          },
          {
            "@type": "OpeningHoursSpecification",
            "dayOfWeek": [
              "https://schema.org/Saturday",
              "https://schema.org/Sunday",
              "https://schema.org/PublicHolidays"
            ],
            "opens": "08:00",
            "closes": "12:00"
          }
        ],
        "specialOpeningHoursSpecification": [
          {
            "@type": "OpeningHoursSpecification",
            "opens": "00:00",
            "closes": "00:00",
            "validFrom": "2021-01-01",
            "validThrough": "2021-01-31"
          }
        ]
      },
      "url": "https://www.example.com/booking/deep/link",
      "image": [
        {
          "@type": "ImageObject",
          "url": "http://example.org/images/1.jpg"
        }
      ],
      "beta:facilitySetting": "https://openactive.io/ns-beta#OutdoorFacility",
      "offers": [
        {
          "@type": "Offer",
          "name": "30 minute hire",
          "price": 10,
          "priceCurrency": "GBP"
        }
      ],
      "hoursAvailable": [
        {
          "@type": "OpeningHoursSpecification",
          "dayOfWeek": [
            "https://schema.org/Sunday"
          ],
          "opens": "09:00",
          "closes": "17:30"
        },
        {
          "@type": "OpeningHoursSpecification",
          "dayOfWeek": [
            "https://schema.org/Monday",
            "https://schema.org/Tuesday",
            "https://schema.org/Wednesday",
            "https://schema.org/Thursday"
          ],
          "opens": "06:30",
          "closes": "21:30"
        },
        {
          "@type": "OpeningHoursSpecification",
          "dayOfWeek": [
            "https://schema.org/Friday"
          ],
          "opens": "06:30",
          "closes": "20:30"
        },
        {
          "@type": "OpeningHoursSpecification",
          "dayOfWeek": [
            "https://schema.org/Saturday"
          ],
          "opens": "07:15",
          "closes": "17:30"
        }
      ]
    }
  },
  "startDate": "2018-03-01T11:00:00Z",
  "endDate": "2018-03-01T11:30:00Z",
  "duration": "PT30M",
  "remainingUses": 1,
  "maximumUses": 1,
  "offers": [
    {
      "@type": "Offer",
      "name": "30 minute hire",
      "price": 10,
      "priceCurrency": "GBP",
      "url": "https://profile.everyoneactive.com/booking?Site=0140&Activities=1402CBP20150217&Culture=en-GB"
    }
  ]
}

The broker-microservice should extract this from a feed pair such as the example specified here and here.

However, it currently does not recognise the embedded individualFacilityUse array in the FacilityUse feed.

Implementation hints

Consider how ingestParentOpportunityPage and ingestOpportunityPage should work considering the multiple embedded individualFacilityUse array in the parent opportunity feed.

Testing the fix

The following should succeed if this fix is made successfully

npm run validate-feeds "https://better-admin.org.uk/api/openactive/better"

Additionally the GET request shown here should return the Slot -> IndividualFacilityUse -> FacilityUse model in the JSON example above (rather than the Slot ->FacilityUse model shown at the link to the test suite output). Note the test suite doesn't need to be run to prove this, the GET request can be created manually using any Slot @id from the feed.

To speed up development, it might be helpful to turn off waitForHarvestCompletion. If the fix works, then once the broker-microservice has consumed the relevant FacilityUse with embedded matching IndividualFacilityUse for the Slot, the GET request above should then successfully return with the model in the JSON example above.

nickevansuk commented 2 years ago

Also note that a common error is for Slots to reference FacilityUse @id instead of IndividualFacilityUse @id when there's IndividualFacilityUses present within the FacilityUses in the feed.

So the logic here should exclusively use IndividualFacilityUses if they exist, and hence it should not match a Slot to a FacilityUse if it has IndividualFacilityUses.