diff --git a/.env.example b/.env.example index 2a87ecdaa..63f8fe1d9 100644 --- a/.env.example +++ b/.env.example @@ -1,4 +1,4 @@ -COMPOSE_PROJECT_NAME=dpl-cms +COMPOSE_PROJECT_NAME=dapple-cms COMPOSER_MEMORY_LIMIT=-1 # GraphQL Consumer environment variables used for local development diff --git a/Taskfile.yml b/Taskfile.yml index 6f4a7f0c8..8f666e4b5 100644 --- a/Taskfile.yml +++ b/Taskfile.yml @@ -34,6 +34,8 @@ vars: DOCKER_COMPOSE_FILES_DEFAULT: "-f docker-compose.yml" DOCKER_COMPOSE_FILES: "{{.DOCKER_COMPOSE_FILES | default .DOCKER_COMPOSE_FILES_DEFAULT }}" DOCKER_COMPOSE_FILES_CI: "{{.DOCKER_COMPOSE_FILES}} -f docker-compose.ci.yml" + DOCKER_COMPOSE_FILES_DEV: "{{.DOCKER_COMPOSE_FILES}} -f docker-compose.dev.yml" + # Sql dump files directory DIR_RESTORE_DATABASE: "restore/database" # Directory with lagoon backup files. @@ -176,6 +178,8 @@ tasks: - task dev:create-users # Show a one-time login to the local site. - task dev:cli -- drush user-login + env: + DOCKER_COMPOSE_FILES: "{{ .DOCKER_COMPOSE_FILES_DEV }}" dev:openid:configure: desc: Set openid connect settings based on .env variables. And run cron. diff --git a/assets/local.services.yml b/assets/local.services.yml index 19d9b757b..2f740baa3 100644 --- a/assets/local.services.yml +++ b/assets/local.services.yml @@ -2,6 +2,21 @@ parameters: twig.config: debug: true http.response.debug_cacheability_headers: true + cors.config: + enabled: true + # Specify allowed headers, like 'x-allowed-header'. + allowedHeaders: ['*'] + # Specify allowed request methods, specify ['*'] to allow all possible ones. + allowedMethods: ['*'] + # Configure requests allowed from specific origins. + allowedOrigins: ['*'] + # Sets the Access-Control-Expose-Headers header. + exposedHeaders: false + # Sets the Access-Control-Max-Age header. + maxAge: false + # Sets the Access-Control-Allow-Credentials header. + supportsCredentials: false + services: cache.backend.null: class: Drupal\Core\Cache\NullBackendFactory diff --git a/composer.json b/composer.json index 1bff81608..9bf6dd95c 100644 --- a/composer.json +++ b/composer.json @@ -40,10 +40,10 @@ "type": "package", "package": { "name": "danskernesdigitalebibliotek/dpl-react", - "version": "2024.48.0", + "version": "2024.49.0", "type": "drupal-library", "dist": { - "url": "https://github.com/danskernesdigitalebibliotek/dpl-react/releases/download/2024.48.0/dist-2024-48-0-6f62c8848cc77c7b83fa3c2591ec0dd2e345a2d3.zip", + "url": "https://github.com/danskernesdigitalebibliotek/dpl-react/releases/download/2024.49.0/dist-2024-49-0-ccdcf6397ae7b1acd328c0cdd5688c2d7bbf1bd4.zip", "type": "zip" }, "require": { @@ -56,9 +56,9 @@ "package": { "name": "danskernesdigitalebibliotek/dpl-design-system", "type": "drupal-library", - "version": "2024.48.0", + "version": "2024.49.0", "dist": { - "url": "https://github.com/danskernesdigitalebibliotek/dpl-design-system/releases/download/2024.48.0/dist-2024-48-0-41958fd47476e2ba316c4529ce2f0ae3e31dd1e4.zip", + "url": "https://github.com/danskernesdigitalebibliotek/dpl-design-system/releases/download/2024.49.0/dist-2024-49-0-6c2891c7ad8ebebab5a41646dc217693f0b49284.zip", "type": "zip" } } @@ -90,8 +90,8 @@ "composer/installers": "1.12.0", "cweagans/composer-patches": "1.7.3", "danskernesdigitalebibliotek/cms-api": "*", - "danskernesdigitalebibliotek/dpl-design-system": "2024.48.0", - "danskernesdigitalebibliotek/dpl-react": "2024.48.0", + "danskernesdigitalebibliotek/dpl-design-system": "2024.49.0", + "danskernesdigitalebibliotek/dpl-react": "2024.49.0", "danskernesdigitalebibliotek/fbs-client": "*", "dealerdirect/phpcodesniffer-composer-installer": "^1.0.0", "deoliveiralucas/array-keys-case-transform": "^1.1", @@ -102,7 +102,6 @@ "drupal/antibot": "^2.0", "drupal/azure_mailer": "^2.0", "drupal/color_field": "^3.0", - "drupal/config_filter": "^2.6", "drupal/config_ignore": "^3", "drupal/config_ignore_auto": "^3", "drupal/config_perms": "^2.1", diff --git a/composer.lock b/composer.lock index 59b54a721..834f682e8 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "6571567cbfd8156aa48a70c4c24581b6", + "content-hash": "d89be314a09f03f216ca135be18c887d", "packages": [ { "name": "amazeeio/drupal_integrations", @@ -1166,19 +1166,19 @@ }, { "name": "danskernesdigitalebibliotek/dpl-design-system", - "version": "2024.48.0", + "version": "2024.49.0", "dist": { "type": "zip", - "url": "https://github.com/danskernesdigitalebibliotek/dpl-design-system/releases/download/2024.48.0/dist-2024-48-0-41958fd47476e2ba316c4529ce2f0ae3e31dd1e4.zip" + "url": "https://github.com/danskernesdigitalebibliotek/dpl-design-system/releases/download/2024.49.0/dist-2024-49-0-6c2891c7ad8ebebab5a41646dc217693f0b49284.zip" }, "type": "drupal-library" }, { "name": "danskernesdigitalebibliotek/dpl-react", - "version": "2024.48.0", + "version": "2024.49.0", "dist": { "type": "zip", - "url": "https://github.com/danskernesdigitalebibliotek/dpl-react/releases/download/2024.48.0/dist-2024-48-0-6f62c8848cc77c7b83fa3c2591ec0dd2e345a2d3.zip" + "url": "https://github.com/danskernesdigitalebibliotek/dpl-react/releases/download/2024.49.0/dist-2024-49-0-ccdcf6397ae7b1acd328c0cdd5688c2d7bbf1bd4.zip" }, "require": { "composer/installers": "^1.2.0" @@ -2359,17 +2359,17 @@ }, { "name": "drupal/admin_toolbar", - "version": "3.5.0", + "version": "3.5.1", "source": { "type": "git", "url": "https://git.drupalcode.org/project/admin_toolbar.git", - "reference": "3.5.0" + "reference": "3.5.1" }, "dist": { "type": "zip", - "url": "https://ftp.drupal.org/files/projects/admin_toolbar-3.5.0.zip", - "reference": "3.5.0", - "shasum": "099e8d4dc98e1d551b4f9cffdc39599eb8ad04e8" + "url": "https://ftp.drupal.org/files/projects/admin_toolbar-3.5.1.zip", + "reference": "3.5.1", + "shasum": "b5215109836f7fade374fef531231e36c1c9b945" }, "require": { "drupal/core": "^9.5 || ^10 || ^11" @@ -2380,8 +2380,8 @@ "type": "drupal-module", "extra": { "drupal": { - "version": "3.5.0", - "datestamp": "1722639094", + "version": "3.5.1", + "datestamp": "1730409973", "security-coverage": { "status": "covered", "message": "Covered by Drupal's security advisory policy" @@ -2558,26 +2558,26 @@ }, { "name": "drupal/antibot", - "version": "2.0.3", + "version": "2.0.4", "source": { "type": "git", "url": "https://git.drupalcode.org/project/antibot.git", - "reference": "2.0.3" + "reference": "2.0.4" }, "dist": { "type": "zip", - "url": "https://ftp.drupal.org/files/projects/antibot-2.0.3.zip", - "reference": "2.0.3", - "shasum": "b26b603299ee3067e0aa3875ff752815f21d2f0b" + "url": "https://ftp.drupal.org/files/projects/antibot-2.0.4.zip", + "reference": "2.0.4", + "shasum": "58b215291b250ea410194693cbac2da6f54a8530" }, "require": { - "drupal/core": "^8.8 || ^9 || ^10" + "drupal/core": "^8.8 || ^9 || ^10 || ^11" }, "type": "drupal-module", "extra": { "drupal": { - "version": "2.0.3", - "datestamp": "1708358087", + "version": "2.0.4", + "datestamp": "1723819813", "security-coverage": { "status": "covered", "message": "Covered by Drupal's security advisory policy" @@ -2802,75 +2802,6 @@ "issues": "https://www.drupal.org/project/issues/color_field?version=8.x" } }, - { - "name": "drupal/config_filter", - "version": "2.6.0", - "source": { - "type": "git", - "url": "https://git.drupalcode.org/project/config_filter.git", - "reference": "8.x-2.6" - }, - "dist": { - "type": "zip", - "url": "https://ftp.drupal.org/files/projects/config_filter-8.x-2.6.zip", - "reference": "8.x-2.6", - "shasum": "fd1a057a402436fc906c63bf0a74722f73b9b155" - }, - "require": { - "drupal/core": "^8.8 || ^9 || ^10" - }, - "conflict": { - "drush/drush": "<10" - }, - "suggest": { - "drupal/config_split": "Split site configuration for different environments." - }, - "type": "drupal-module", - "extra": { - "drupal": { - "version": "8.x-2.6", - "datestamp": "1698308577", - "security-coverage": { - "status": "covered", - "message": "Covered by Drupal's security advisory policy" - } - } - }, - "notification-url": "https://packages.drupal.org/8/downloads", - "license": [ - "GPL-2.0-or-later" - ], - "authors": [ - { - "name": "Fabian Bircher", - "homepage": "https://www.drupal.org/u/bircher", - "email": "opensource@fabianbircher.com", - "role": "Maintainer" - }, - { - "name": "Nuvole Web", - "homepage": "http://nuvole.org", - "email": "info@nuvole.org", - "role": "Maintainer" - }, - { - "name": "pescetti", - "homepage": "https://www.drupal.org/user/436244" - } - ], - "description": "Config Filter allows other modules to interact with a ConfigStorage through filter plugins.", - "homepage": "https://www.drupal.org/project/config_filter", - "keywords": [ - "Drupal", - "configuration", - "configuration management" - ], - "support": { - "source": "https://git.drupalcode.org/project/config_filter", - "issues": "https://www.drupal.org/project/issues/config_filter", - "slack": "https://drupal.slack.com/archives/C45342CDD" - } - }, { "name": "drupal/config_ignore", "version": "3.3.0", diff --git a/config/sync/core.extension.yml b/config/sync/core.extension.yml index f9cfe5e8b..d97c95b6f 100644 --- a/config/sync/core.extension.yml +++ b/config/sync/core.extension.yml @@ -50,6 +50,7 @@ module: dpl_library_token: 0 dpl_link: 0 dpl_loans: 0 + dpl_logging: 0 dpl_login: 0 dpl_mail: 0 dpl_mapp: 0 diff --git a/docker-compose.dev.yml b/docker-compose.dev.yml new file mode 100644 index 000000000..729d2cdd5 --- /dev/null +++ b/docker-compose.dev.yml @@ -0,0 +1,22 @@ +--- +services: + https: + image: ghcr.io/reload/https-proxy:proxy + ports: + - '80' + - '443' + depends_on: + - varnish + volumes: + - 'projectroot:/app' + - '${HOME}/.local/share/mkcert:/rootCA:ro' + - '${HOME}/.local/share/dev_certificates:/cert:rw' + environment: + NGINX_DOCUMENT_ROOT: /app/web + NGINX_PROXY_PASS: http://varnish:8080 + VIRTUAL_PROTO: https + VIRTUAL_HOST: >- + ${COMPOSE_PROJECT_NAME:-dapple-cms}.${DEV_TLD:-docker} + labels: + - dev.orbstack.domains=${COMPOSE_PROJECT_NAME:-dapple-cms}.local + working_dir: /app diff --git a/docker-compose.yml b/docker-compose.yml index decadeb59..912075aff 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -52,7 +52,7 @@ services: << : *default-environment # VARNISH_BYPASS: "true" # Add this to disable caching in varnish. # Is used by [nginx-proxy](https://github.com/nginx-proxy/nginx-proxy) or [dory](https://github.com/FreedomBen/dory) - VIRTUAL_HOST: ${COMPOSE_PROJECT_NAME}.docker + VIRTUAL_HOST: ${COMPOSE_PROJECT_NAME}-varnish.docker VIRTUAL_PORT: 8080 cli: # cli container, will be used for executing composer and any local commands (drush, drupal, etc.) diff --git a/docs/local-development.md b/docs/local-development.md index a13120f3e..27fd2a3c3 100644 --- a/docs/local-development.md +++ b/docs/local-development.md @@ -29,6 +29,27 @@ volumes in docker-compose, to speed up the containers. ![OSX preference pane providing access to VirtioFS](docs/images/virtiofs.png) +### Configuration of https + +Install mkcert on your host machine and generate and install a root certificate +by running mkcert -install on your host machine (one time only). + +Mac users can run: `brew install mkcert` to install mkcert + +Then run: + +`mkcert -install` + +Mac users should then do (one time only): + +$ mkdir -p ~/.local/share && ln -s "$(mkcert -CAROOT)" ~/.local/share + +#### If you are using Dory + +Update the ssl_certs_dir as described here: + +[https://github.com/reload/drupal-apache-fpm?tab=readme-ov-file#using-autogenerated-certificates](https://github.com/reload/drupal-apache-fpm?tab=readme-ov-file#using-autogenerated-certificates) + ## Howtos ### Enable XDebug diff --git a/openapi.json b/openapi.json index 24d7fb509..f942b0242 100644 --- a/openapi.json +++ b/openapi.json @@ -1002,6 +1002,14 @@ "description": "The name of a tag." } }, + "partners": { + "type": "array", + "description": "The partners associated with the event.", + "items": { + "type": "string", + "description": "The name of a partner." + } + }, "ticket_categories": { "type": "array", "description": "Ticket categories used for the event. Not present for events without ticketing.", diff --git a/package.json b/package.json index 294bf9099..c113e39bf 100644 --- a/package.json +++ b/package.json @@ -7,8 +7,8 @@ "@lhci/cli": "^0.8", "@testing-library/cypress": "^10.0.2", "@types/node": "^18.11.11", - "@typescript-eslint/eslint-plugin": "^8.16.0", - "@typescript-eslint/parser": "^8.16.0", + "@typescript-eslint/eslint-plugin": "^8.17.0", + "@typescript-eslint/parser": "^8.17.0", "chrome-launcher": "^0.15.1", "cypress": "^13.16.0", "cypress-if": "^1.13.1", diff --git a/packages/cms-api/Model/EventsGET200ResponseInner.php b/packages/cms-api/Model/EventsGET200ResponseInner.php index 18ae190f1..ef7ce5407 100644 --- a/packages/cms-api/Model/EventsGET200ResponseInner.php +++ b/packages/cms-api/Model/EventsGET200ResponseInner.php @@ -180,6 +180,18 @@ class EventsGET200ResponseInner */ protected ?array $tags = null; + /** + * The partners associated with the event. + * + * @var string[]|null + * @SerializedName("partners") + * @Assert\All({ + * @Assert\Type("string") + * }) + * @Type("array") + */ + protected ?array $partners = null; + /** * Ticket categories used for the event. Not present for events without ticketing. * @@ -248,6 +260,7 @@ public function __construct(array $data = null) $this->branches = array_key_exists('branches', $data) ? $data['branches'] : $this->branches; $this->address = array_key_exists('address', $data) ? $data['address'] : $this->address; $this->tags = array_key_exists('tags', $data) ? $data['tags'] : $this->tags; + $this->partners = array_key_exists('partners', $data) ? $data['partners'] : $this->partners; $this->ticketCategories = array_key_exists('ticketCategories', $data) ? $data['ticketCategories'] : $this->ticketCategories; $this->ticketCapacity = array_key_exists('ticketCapacity', $data) ? $data['ticketCapacity'] : $this->ticketCapacity; $this->series = array_key_exists('series', $data) ? $data['series'] : $this->series; @@ -594,6 +607,32 @@ public function setTags(?array $tags = null): self return $this; } + /** + * Gets partners. + * + * @return string[]|null + */ + public function getPartners(): ?array + { + return $this->partners; + } + + + + /** + * Sets partners. + * + * @param string[]|null $partners The partners associated with the event. + * + * @return $this + */ + public function setPartners(?array $partners = null): self + { + $this->partners = $partners; + + return $this; + } + /** * Gets ticketCategories. * diff --git a/packages/fbs-client/README.md b/packages/fbs-client/README.md index 6653c4b9c..9c1470c10 100644 --- a/packages/fbs-client/README.md +++ b/packages/fbs-client/README.md @@ -78,7 +78,7 @@ All URIs are relative to *http://localhost* Class | Method | HTTP request | Description ------------ | ------------- | ------------- | ------------- *ExternalAgencyidCatalogApi* | [**getAvailabilityV3**](docs/Api/ExternalAgencyidCatalogApi.md#getavailabilityv3) | **GET** /external/agencyid/catalog/availability/v3 | Get availability of bibliographical records. -*ExternalAgencyidCatalogApi* | [**getHoldingsV3**](docs/Api/ExternalAgencyidCatalogApi.md#getholdingsv3) | **GET** /external/agencyid/catalog/holdings/v3 | Get placement holdings for bibliographical records. +*ExternalAgencyidCatalogApi* | [**getHoldingsLogisticsV1**](docs/Api/ExternalAgencyidCatalogApi.md#getholdingslogisticsv1) | **GET** /external/agencyid/catalog/holdingsLogistics/v1 | Get placement holdings for bibliographical records. *ExternalAgencyidPatronPatronidApi* | [**getFeesV2**](docs/Api/ExternalAgencyidPatronPatronidApi.md#getfeesv2) | **GET** /external/agencyid/patron/patronid/fees/v2 | List of fees in FBS for the patron with all available information about the fee. *ExternalAgencyidPatronsApi* | [**createV4**](docs/Api/ExternalAgencyidPatronsApi.md#createv4) | **POST** /external/agencyid/patrons/v4 | Create a new patron who is a person. *ExternalAgencyidPatronsApi* | [**createWithGuardian**](docs/Api/ExternalAgencyidPatronsApi.md#createwithguardian) | **POST** /external/agencyid/patrons/withGuardian/v1 | Creates a person patron with a guardian (eg A financial responsible). diff --git a/web/modules/custom/dpl_event/src/EventWrapper.php b/web/modules/custom/dpl_event/src/EventWrapper.php index a49251456..58c1011a8 100644 --- a/web/modules/custom/dpl_event/src/EventWrapper.php +++ b/web/modules/custom/dpl_event/src/EventWrapper.php @@ -2,10 +2,13 @@ namespace Drupal\dpl_event; +use Brick\Math\BigDecimal; use Drupal\Core\Field\FieldItemListInterface; use Drupal\drupal_typed\DrupalTyped; +use Drupal\paragraphs\ParagraphInterface; use Drupal\recurring_events\Entity\EventInstance; use Psr\Log\LoggerInterface; +use Safe\DateTime; use Safe\DateTimeImmutable; /** @@ -147,6 +150,79 @@ public function getState(): ?EventState { return NULL; } + /** + * Get the url of the event if available. + * + * The url will usually be the place where visitors can by tickets for the + * event. + */ + public function getLink() : ?string { + $linkField = $this->getField('event_link'); + return $linkField?->getString(); + } + + /** + * Get the price(s) for the event. + * + * @return int[]|float[] + * Price(s) for the available ticket categories. + */ + public function getTicketPrices(): array { + $field = $this->getField('event_ticket_categories'); + if (!$field instanceof FieldItemListInterface) { + return []; + } + + $ticketCategories = $field->referencedEntities(); + return array_map(function (ParagraphInterface $ticketCategory) { + return $ticketCategory->get('field_ticket_category_price')->value; + }, $ticketCategories); + } + + /** + * Returns whether the event can be freely attended. + * + * This means that the event does not require ticketing or that all ticket + * categories are free. + */ + public function isFreeToAttend(): bool { + $nonFreePrice = array_filter($this->getTicketPrices(), function (int|float $price) { + $price = BigDecimal::of($price); + return !$price->isZero(); + }); + return empty($nonFreePrice); + } + + /** + * Getting relevant updated date - either the series or instance. + * + * As we use inheritance, we want an updated series to also reflect update. + * We could implement this, by programmatically saving all instances when + * the series is saved, but this may have unforseen consequences, as it is + * working against the Drupal system. + * Instead, we'll look up the instance and series changed dates, and take + * which ever is newer. + */ + public function getUpdatedDate(): ?DateTime { + $series = $this->event->getEventSeries(); + + $changed_instance = $this->event->getChangedTime(); + $changed_series = $series->getChangedTime(); + + // Setting the timestamp to whichever is the larger. + $timestamp = ($changed_instance > $changed_series) ? + $changed_instance : $changed_series; + + if (empty($timestamp)) { + return NULL; + } + + $date = new DateTime(); + $date->setTimestamp(intval($timestamp)); + + return $date; + } + /** * Loading the field if it exists. * diff --git a/web/modules/custom/dpl_event/src/Plugin/rest/resource/v1/EventsResource.php b/web/modules/custom/dpl_event/src/Plugin/rest/resource/v1/EventsResource.php index d7425cfaa..af9555ac9 100644 --- a/web/modules/custom/dpl_event/src/Plugin/rest/resource/v1/EventsResource.php +++ b/web/modules/custom/dpl_event/src/Plugin/rest/resource/v1/EventsResource.php @@ -167,6 +167,14 @@ public function getPluginDefinition(): array { 'description' => 'The name of a tag.', ], ], + 'partners' => [ + 'type' => 'array', + 'description' => 'The partners associated with the event.', + 'items' => [ + 'type' => 'string', + 'description' => 'The name of a partner.', + ], + ], 'ticket_categories' => [ 'type' => 'array', 'description' => 'Ticket categories used for the event. Not present for events without ticketing.', diff --git a/web/modules/custom/dpl_event/src/PriceFormatter.php b/web/modules/custom/dpl_event/src/PriceFormatter.php index f63619239..5bc7c9c41 100644 --- a/web/modules/custom/dpl_event/src/PriceFormatter.php +++ b/web/modules/custom/dpl_event/src/PriceFormatter.php @@ -149,4 +149,31 @@ public function formatRawPrice(string $price_string): string { } } + /** + * Formats a range of numeric prices into a string. + * + * Sorts and formats raw prices without currency pre/suffixes or rewriting 0 + * to "Free". + * + * @param float[]|int[] $prices + * Array of price values (numbers). + * + * @return string + * Formatted price range string. + */ + public function formatRawPriceRange(array $prices): string { + sort($prices); + $lowest_price = min($prices); + $highest_price = max($prices); + + if ($lowest_price != $highest_price) { + $lowest_price = $this->formatRawPrice((string) $lowest_price); + $highest_price = $this->formatRawPrice((string) $highest_price); + + return "$lowest_price - $highest_price"; + } + + return $this->formatRawPrice((string) $lowest_price); + } + } diff --git a/web/modules/custom/dpl_event/src/Services/EventRestMapper.php b/web/modules/custom/dpl_event/src/Services/EventRestMapper.php index b23b095f1..fef806c26 100644 --- a/web/modules/custom/dpl_event/src/Services/EventRestMapper.php +++ b/web/modules/custom/dpl_event/src/Services/EventRestMapper.php @@ -63,10 +63,11 @@ public function getResponse(EventInstance $event_instance): EventsGET200Response 'branches' => $this->getBranches(), 'address' => $this->getAddress(), 'tags' => $this->getTags(), + 'partners' => $this->getMultiValue('event_partners'), 'ticketCapacity' => $this->getValue('event_ticket_capacity'), 'ticketCategories' => $this->getTicketCategories(), 'createdAt' => $this->getDateField('created'), - 'updatedAt' => $this->getDateField('changed'), + 'updatedAt' => $this->eventWrapper->getUpdatedDate(), 'dateTime' => $this->getDate(), 'externalData' => $this->getExternalData(), 'series' => new EventsGET200ResponseInnerSeries([ @@ -252,6 +253,25 @@ private function getDateField(string $field_name): ?DateTime { return $date; } + /** + * Get multiple string values as an array output. + * + * @return string[] + * An array of string values. + */ + private function getMultiValue(string $field_name): array { + $field = $this->eventWrapper->getField($field_name); + + if (!($field instanceof FieldItemListInterface)) { + return []; + } + + $values = $field->getValue(); + + // Turning the value keys into a simple, one-level array of strings. + return array_column($values, 'value'); + } + /** * Get string value of a possible field (or fallback field). */ diff --git a/web/modules/custom/dpl_event/tests/src/Unit/PriceFormatterTest.php b/web/modules/custom/dpl_event/tests/src/Unit/PriceFormatterTest.php index f9371b07f..ec4ddd6dc 100644 --- a/web/modules/custom/dpl_event/tests/src/Unit/PriceFormatterTest.php +++ b/web/modules/custom/dpl_event/tests/src/Unit/PriceFormatterTest.php @@ -148,4 +148,53 @@ public function testPriceRangeFormatting( ); } + /** + * Provides examples of price arrays and their expected range formatting. + * + * @return array, string}> + * Array of examples. Each example contains an array of prices and how + * they should be formatted. This matches signature of + * testPriceRangeFormatting(). + */ + public function rawPriceRangeProvider(): array { + return [ + // Only free prices. + [[0], "0"], + // Free and a single price. + [[0, 20], "0 - 20"], + // Range of prices. + [[20, 30], "20 - 30"], + // Single price. + [[20], "20"], + // Multiple prices. + [[10, 20, 30], "10 - 30"], + // Free with multiple prices. + [[0, 10, 20], "0 - 20"], + // Larger range of prices. + [[50, 100, 150], "50 - 150"], + [[0, 1000], "0 - 1000"], + ]; + } + + /** + * Test raw price range formatting. + * + * @param int[] $prices + * Array of integers representing prices. + * @param string $expected + * Expected formatted string. + * + * @dataProvider rawPriceRangeProvider + */ + public function testRawPriceRangeFormatting( + array $prices, + string $expected, + ): void { + $priceFormatter = new PriceFormatter($this->getStringTranslationStub(), $this->getConfigFactoryStub($this->mockConfig)); + $this->assertSame( + $expected, + $priceFormatter->formatRawPriceRange($prices) + ); + } + } diff --git a/web/modules/custom/dpl_logging/dpl_logging.info.yml b/web/modules/custom/dpl_logging/dpl_logging.info.yml new file mode 100644 index 000000000..715ebbde0 --- /dev/null +++ b/web/modules/custom/dpl_logging/dpl_logging.info.yml @@ -0,0 +1,6 @@ +--- +name: DPL logging +description: Logging custom DDF events, such as updates to users. +package: DPL +type: module +core_version_requirement: ^10 || ^11 diff --git a/web/modules/custom/dpl_logging/dpl_logging.module b/web/modules/custom/dpl_logging/dpl_logging.module new file mode 100644 index 000000000..b8489127b --- /dev/null +++ b/web/modules/custom/dpl_logging/dpl_logging.module @@ -0,0 +1,112 @@ +original; + + if (!($original_user instanceof User)) { + return; + } + + $original_values = $original_user->toArray(); + $values = $user->toArray(); + $updates = DiffArray::diffAssocRecursive($values, $original_values); + + // Some fields are so meta that we do not need to log their changes. + $ignored_updates = ['changed', 'metatag', 'field_password_expiration']; + $diffs = []; + + foreach ($updates as $key => $value) { + if (in_array($key, $ignored_updates)) { + continue; + } + + if ($key === 'roles') { + $original_roles = $original_user->getRoles(); + $new_roles = $user->getRoles(); + + // This results in the machine names of the roles. + // We could also do a look up, to find the translated labels also, + // but it is probably overkill for what this log will be used for. + $added_roles = array_diff($new_roles, $original_roles); + $removed_roles = array_diff($original_roles, $new_roles); + + if (!empty($added_roles)) { + $diffs['roles_added'] = t( + 'New roles added to user: @roles', + ['@roles' => implode(', ', $added_roles)], + ['context' => 'DPL logging'] + )->render(); + } + + if (!empty($removed_roles)) { + $diffs['roles_removed'] = t( + 'Old roles removed from user: @roles', + ['@roles' => implode(', ', $removed_roles)], + ['context' => 'DPL logging'] + )->render(); + } + + continue; + } + + if ($key === 'pass') { + // The password always shows up, but it does not always have an actual + // value inserted - in that case, we'll filter it out. + if (!empty(array_filter($value[0]))) { + // We don't want to log the actual password change. It is useless as + // it is hashed regardless. + $diffs[$key] = t( + 'Password was updated', + [], + ['context' => 'DPL logging'] + )->render(); + } + + continue; + } + + // Getting human-readable label, with machine name as fallback. + $label = $user->getFieldDefinition($key)?->getLabel() ?? $key; + + // Make sure the label is a string. Sometimes getLabel returns translation. + $label = (string) $label; + + $original_value = $original_values[$key] ?? NULL; + + $diffs[$key] = t( + '@label (@key): @original_value changed to @value', + [ + '@label' => $label, + '@key' => $key, + '@original_value' => $original_value[0]['value'], + '@value' => $value[0]['value'] ?? NULL, + ], + ['context' => 'DPL logging'] + )->render(); + } + + if (empty($diffs)) { + return; + } + + $values_string = implode("\r\n | ", $diffs); + + $logger = \Drupal::logger('dpl_logging'); + + $logger->notice('User @name (@id) has been updated with the following values: @values', [ + '@name' => $user->getAccountName(), + '@id' => $user->id(), + '@values' => $values_string, + ]); +} diff --git a/web/modules/custom/dpl_redia_legacy/src/Controller/RssFeeds/EventsController.php b/web/modules/custom/dpl_redia_legacy/src/Controller/RssFeeds/EventsController.php index 17f7acbce..0f2b24f13 100644 --- a/web/modules/custom/dpl_redia_legacy/src/Controller/RssFeeds/EventsController.php +++ b/web/modules/custom/dpl_redia_legacy/src/Controller/RssFeeds/EventsController.php @@ -2,15 +2,16 @@ namespace Drupal\dpl_redia_legacy\Controller\RssFeeds; +use Drupal\Core\Cache\CacheableMetadata; use Drupal\Core\Cache\CacheableResponse; use Drupal\Core\Controller\ControllerBase; use Drupal\Core\Datetime\DateFormatterInterface; use Drupal\Core\File\FileUrlGeneratorInterface; use Drupal\Core\Url; +use Drupal\dpl_event\PriceFormatter; use Drupal\dpl_redia_legacy\RediaEvent; use Symfony\Component\DependencyInjection\ContainerInterface; use Symfony\Component\HttpFoundation\Request; -use Symfony\Component\HttpFoundation\Response; use function Safe\strtotime; /** @@ -24,6 +25,7 @@ class EventsController extends ControllerBase { public function __construct( protected FileUrlGeneratorInterface $fileUrlGenerator, protected DateFormatterInterface $dateFormatter, + protected PriceFormatter $priceFormatter, ) {} /** @@ -33,6 +35,7 @@ public static function create(ContainerInterface $container): static { return new static( $container->get('file_url_generator'), $container->get('date.formatter'), + $container->get('dpl_event.price_formatter'), ); } @@ -40,9 +43,6 @@ public static function create(ContainerInterface $container): static { * Getting the RSS/XML feed of the items. */ public function getFeed(Request $request): CacheableResponse { - // Disable the feed while we wait for validation by Redia. - return new CacheableResponse("Feed disabled temporarily", Response::HTTP_NOT_FOUND); - /* $items = $this->getItems(); $rss_content = $this->buildRss($items, $request); @@ -59,7 +59,6 @@ public function getFeed(Request $request): CacheableResponse { $response->headers->set('Content-Type', 'application/rss+xml'); return $response; - */ } /** @@ -68,7 +67,7 @@ public function getFeed(Request $request): CacheableResponse { * @return \Drupal\dpl_redia_legacy\RediaEvent[] * An array of necessary item fields, used in buildRss(). */ - protected function getItems(): array { + private function getItems(): array { $storage = $this->entityTypeManager()->getStorage('eventinstance'); $query = $storage->getQuery() @@ -84,7 +83,7 @@ protected function getItems(): array { $items = []; foreach ($events as $event) { - $items[] = new RediaEvent($event); + $items[] = new RediaEvent($event, $this->priceFormatter); } return $items; @@ -98,7 +97,7 @@ protected function getItems(): array { * @param \Symfony\Component\HttpFoundation\Request $request * The request, for looking up the current site info. */ - protected function buildRss(array $items, Request $request): string { + private function buildRss(array $items, Request $request): string { $config = $this->config('system.site'); $site_title = $config->get('name'); $site_url = $request->getSchemeAndHttpHost(); @@ -108,48 +107,101 @@ protected function buildRss(array $items, Request $request): string { $date = $this->dateFormatter->format(time(), 'custom', 'r'); - $rss_feed = << - - - $site_title - $site_url - - da - $date - $date -RSS; - - foreach ($items as $item) { - $rss_feed .= << - {$item->title} - {$item->description} - {$item->author} - {$item->id} - {$item->date} - $site_title - - {$item->media?->md5} - - - {$item->subtitle} - {$item->startTime} - {$item->endTime} - {$item->branch?->label()} - {$item->branch?->id()} - {$item->promoted} - -ITEM; - } - - $rss_feed .= << - -RSS; - - return $rss_feed; - + // Disable formatting rules. We use indentation to mark start/end elements. + // phpcs:disable Drupal.WhiteSpace.ScopeIndent.IncorrectExact + // @formatter:off + $xml = new \XMLWriter(); + $xml->openMemory(); + $xml->startDocument('1.0', 'UTF-8'); + $xml->startElement('rss'); + $xml->writeAttribute('version', '2.0'); + $xml->writeAttribute('xml:base', $site_url); + // We intentionally do not use the built-in XML Writer namespace handling. + // This allows us to produce output that matches the existing + // implementation as closely as possible. + $xml->writeAttribute('xmlns:atom', 'http://www.w3.org/2005/Atom'); + $xml->writeAttribute('xmlns:media', 'http://search.yahoo.com/mrss/'); + $xml->writeAttribute('xmlns:content-rss', 'http://xml.redia.dk/rss'); + + $xml->startElement('channel'); + $xml->writeElement('title', $site_title); + $xml->writeElement('link', $site_url); + $xml->startElement('atom:link'); + $xml->writeAttribute('rel', 'self'); + $xml->writeAttribute('href', $feed_url); + $xml->endElement(); + $xml->writeElement('language', 'da'); + $xml->writeElement('pubDate', $date); + $xml->writeElement('lastBuildDate', $date); + + foreach ($items as $item) { + $xml->startElement('item'); + $xml->writeElement('title', $item->title); + $xml->writeElement('description', $item->description); + $xml->writeElement('author', $item->author); + $xml->startElement('guid'); + $xml->writeAttribute('isPermaLink', 'false'); + $xml->text((string) $item->id); + $xml->endElement(); + $xml->writeElement('pubDate', $item->date); + $xml->startElement('source'); + $xml->writeAttribute('url', $feed_url); + $xml->text($site_title); + $xml->endElement(); + + if ($item->media && $item->media->url) { + $xml->startElement('media:content'); + $xml->writeAttribute('url', $item->media->url); + $xml->writeAttribute('fileSize', (string) $item->media->size); + $xml->writeAttribute('type', (string) $item->media->type); + $xml->writeAttribute('medium', $item->media->medium); + $xml->writeAttribute('width', (string) $item->media->width); + $xml->writeAttribute('height', (string) $item->media->height); + if ($item->media->md5) { + $xml->startElement('media:hash'); + $xml->writeAttribute('algo', 'md5'); + $xml->text($item->media->md5); + $xml->endElement(); + } + $xml->endElement(); + } + + if ($item->mediaThumbnail && $item->mediaThumbnail->url) { + $xml->startElement('media:thumbnail'); + $xml->writeAttribute('url', $item->mediaThumbnail->url); + $xml->writeAttribute('width', (string) $item->mediaThumbnail->width); + $xml->writeAttribute('height', (string) $item->mediaThumbnail->height); + $xml->endElement(); + } + + $xml->writeElement('content-rss:subheadline', $item->subtitle); + $xml->writeElement('content-rss:arrangement-starttime', $item->startTime); + $xml->writeElement('content-rss:arrangement-endtime', $item->endTime); + + if ($item->branch) { + $xml->writeElement('content-rss:arrangement-location', $item->branch->label()); + $xml->writeElement('content-rss:library-id', (string) $item->branch->id()); + } + + if ($item->bookingUrl) { + $xml->writeElement('content-rss:booking-url', $item->bookingUrl); + } + + // Events without a price element are interpreted as free. + if ($item->prices) { + $xml->writeElement('content-rss:arrangement-price', $item->prices); + } + + $xml->writeElement('content-rss:promoted', $item->promoted); + $xml->endElement(); + } + $xml->endElement(); + + $xml->endElement(); + $xml->endDocument(); + return $xml->outputMemory(); + // @formatter:on + // phpcs:enable Drupal.WhiteSpace.ScopeIndent.IncorrectExact } } diff --git a/web/modules/custom/dpl_redia_legacy/src/RediaEvent.php b/web/modules/custom/dpl_redia_legacy/src/RediaEvent.php index 21b8f7852..1d6ff2ed1 100644 --- a/web/modules/custom/dpl_redia_legacy/src/RediaEvent.php +++ b/web/modules/custom/dpl_redia_legacy/src/RediaEvent.php @@ -6,6 +6,7 @@ use Drupal\Core\Datetime\DrupalDateTime; use Drupal\Core\Field\FieldItemListInterface; use Drupal\dpl_event\EventWrapper; +use Drupal\dpl_event\PriceFormatter; use Drupal\node\NodeInterface; use Drupal\recurring_events\Entity\EventInstance; @@ -27,6 +28,8 @@ class RediaEvent extends ControllerBase { public ?NodeInterface $branch; public ?RediaEventMedia $media; public ?RediaEventMedia $mediaThumbnail; + public ?string $bookingUrl; + public ?string $prices; // phpcs:enable /** @@ -37,7 +40,7 @@ class RediaEvent extends ControllerBase { */ public string $promoted; - public function __construct(EventInstance $event_instance) { + public function __construct(EventInstance $event_instance, PriceFormatter $price_formatter) { $event_wrapper = new EventWrapper($event_instance); $branch = $event_wrapper->getBranches()[0] ?? NULL; @@ -54,9 +57,7 @@ public function __construct(EventInstance $event_instance) { } $this->title = $event_instance->label(); - // The description for an event may contain HTML tags which are not allowed - // in an RSS/XML feed. Encode them. - $this->description = htmlspecialchars($event_wrapper->getDescription() ?? ""); + $this->description = $event_wrapper->getDescription(); $this->author = $event_instance->getOwner()->get('field_author_name')->getString(); $this->id = $event_instance->id(); $this->date = $changed_date->format('r'); @@ -72,6 +73,15 @@ public function __construct(EventInstance $event_instance) { } $this->branch = $branch; + $this->bookingUrl = $event_wrapper->getLink(); + + if (!$event_wrapper->isFreeToAttend()) { + $prices = $event_wrapper->getTicketPrices(); + $this->prices = $price_formatter->formatRawPriceRange($prices); + } + else { + $this->prices = NULL; + } // In the old system, there was a way for editors to mark content a // promoted. However, this does not exist in the new CMS, so we wil diff --git a/web/modules/custom/dpl_update/dpl_update.install b/web/modules/custom/dpl_update/dpl_update.install index 91c7e54d7..4991bd440 100644 --- a/web/modules/custom/dpl_update/dpl_update.install +++ b/web/modules/custom/dpl_update/dpl_update.install @@ -326,3 +326,10 @@ function dpl_update_update_10027(): string { function dpl_update_update_10028(): string { return _dpl_update_uninstall_modules(['config_filter']); } + +/** + * Installing dpl_logging. + */ +function dpl_update_update_10029(): string { + return _dpl_update_install_modules(['dpl_logging']); +} diff --git a/web/profiles/dpl_cms/translations/da.combined.po b/web/profiles/dpl_cms/translations/da.combined.po index c063fbf4f..535d79eb2 100644 --- a/web/profiles/dpl_cms/translations/da.combined.po +++ b/web/profiles/dpl_cms/translations/da.combined.po @@ -7,8 +7,8 @@ msgid "" msgstr "" "Project-Id-Version: PROJECT VERSION\n" -"POT-Creation-Date: 2024-11-26 17:18+0100\n" -"PO-Revision-Date: 2024-11-26 17:18+0100\n" +"POT-Creation-Date: 2024-12-03 17:44+0100\n" +"PO-Revision-Date: 2024-12-03 17:44+0100\n" "Last-Translator: NAME \n" "Language-Team: LANGUAGE \n" "MIME-Version: 1.0\n" @@ -3476,6 +3476,22 @@ msgctxt "List size (settings)" msgid "Fees list size on mobile" msgstr "Antal elementer der vises i en liste på mobil" +msgctxt "DPL logging" +msgid "New roles added to user: @roles" +msgstr "" + +msgctxt "DPL logging" +msgid "Old roles removed from user: @roles" +msgstr "" + +msgctxt "DPL logging" +msgid "Password was updated" +msgstr "" + +msgctxt "DPL logging" +msgid "@label (@key): @original_value changed to @value" +msgstr "" + msgid "Mock-title field" msgstr "Mock-titelfelt" @@ -4641,6 +4657,12 @@ msgstr "DPL Cookies" msgid "A module that handles cookie functionality." msgstr "En modul, der håndterer cookie-funktionalitet." +msgid "DPL logging" +msgstr "" + +msgid "Logging custom DDF events, such as updates to users." +msgstr "" + msgid "Event settings" msgstr "Arrangementsindstillinger" diff --git a/wiremock/src/mappings/work/createMappingsForWorkPage.ts b/wiremock/src/mappings/work/createMappingsForWorkPage.ts index 73b37625b..09494a128 100644 --- a/wiremock/src/mappings/work/createMappingsForWorkPage.ts +++ b/wiremock/src/mappings/work/createMappingsForWorkPage.ts @@ -43,7 +43,8 @@ export default (baseUri?: string, options?: Options) => { wiremock(baseUri, options).mappings.createMapping({ request: { method: "GET", - urlPattern: "/external/agencyid/catalog/holdings/v3\\?recordid=.*", + urlPattern: + "/external/agencyid/catalog/holdingsLogistics/v1\\?recordid=.*", }, response: { jsonBody: json.default, diff --git a/wiremock/src/mappings/work/data/fbs/holdings.json b/wiremock/src/mappings/work/data/fbs/holdings.json index d3022da63..805278575 100644 --- a/wiremock/src/mappings/work/data/fbs/holdings.json +++ b/wiremock/src/mappings/work/data/fbs/holdings.json @@ -1,238 +1,2513 @@ [ + { + "recordId": "24880605", + "reservable": true, + "reservations": 0, + "holdings": [ + { + "branch": { + "branchId": "FBS-101007", + "title": "HB Rigshospitalet" + }, + "lmsPlacement": { + "department": { + "departmentId": "bø", + "title": "Børn" + }, + "section": null, + "location": null, + "sublocation": { + "sublocationId": "fant", + "title": "Fantasy" + } + }, + "logisticsPlacement": [], + "materials": [ + { + "itemNumber": "5205371674", + "available": true, + "periodical": null, + "materialGroup": { + "name": "RHnonres", + "description": "Nonres materialer på rigshospitalet (lavet så kun personer på RH kan låne og reserverer på disse)" + } + } + ] + }, + { + "branch": { + "branchId": "FBS-101004", + "title": "HB Københavns fængsler" + }, + "lmsPlacement": { + "department": { + "departmentId": "vo", + "title": "Voksen" + }, + "section": null, + "location": null, + "sublocation": { + "sublocationId": "skøn", + "title": "Skønlitteratur" + } + }, + "logisticsPlacement": [], + "materials": [ + { + "itemNumber": "3764991841", + "available": true, + "periodical": null, + "materialGroup": { + "name": "nonres", + "description": "nonres" + } + }, + { + "itemNumber": "3764991361", + "available": true, + "periodical": null, + "materialGroup": { + "name": "nonres", + "description": "nonres" + } + }, + { + "itemNumber": "3764991116", + "available": true, + "periodical": null, + "materialGroup": { + "name": "nonres", + "description": "nonres" + } + }, + { + "itemNumber": "3764991825", + "available": true, + "periodical": null, + "materialGroup": { + "name": "nonres", + "description": "nonres" + } + } + ] + }, + { + "branch": { + "branchId": "DK-710122", + "title": "Ørestad" + }, + "lmsPlacement": null, + "logisticsPlacement": ["Ørestad", "1.sal", "Taskebøger & klassesæt"], + "materials": [ + { + "itemNumber": "4892314638", + "available": true, + "periodical": null, + "materialGroup": { + "name": "sko28", + "description": "Skole 28 dages udlån" + } + } + ] + } + ] + }, { "recordId": "134693959", "reservable": true, - "reservations": 3, + "reservations": 1, "holdings": [ { - "branch": { "branchId": "FBS-751026", "title": "Fælles undervejs" }, - "department": { "departmentId": "bø", "title": "Børn" }, - "location": { "locationId": "fanta", "title": "Fantasy" }, - "sublocation": null, + "branch": { + "branchId": "DK-710114", + "title": "Bibliotekshuset" + }, + "lmsPlacement": { + "department": { + "departmentId": "bø", + "title": "Børn" + }, + "section": null, + "location": null, + "sublocation": { + "sublocationId": "fant", + "title": "Fantasy" + } + }, + "logisticsPlacement": [], "materials": [ { - "itemNumber": "A1011423955A", + "itemNumber": "5387480031", + "available": false, + "periodical": null, + "materialGroup": { + "name": "Standard", + "description": "Std. materialegruppe" + } + }, + { + "itemNumber": "5387480236", "available": false, "periodical": null, "materialGroup": { - "name": "standard", - "description": "31 dages lånetid til alm lånere" + "name": "Standard", + "description": "Std. materialegruppe" } }, { - "itemNumber": "A1011423956A", + "itemNumber": "5387480368", "available": false, "periodical": null, "materialGroup": { - "name": "standard", - "description": "31 dages lånetid til alm lånere" + "name": "Standard", + "description": "Std. materialegruppe" + } + }, + { + "itemNumber": "5387480384", + "available": false, + "periodical": null, + "materialGroup": { + "name": "Standard", + "description": "Std. materialegruppe" + } + } + ] + }, + { + "branch": { + "branchId": "FBS-101008", + "title": "Fjernlager 1" + }, + "lmsPlacement": { + "department": { + "departmentId": "bø", + "title": "Børn" + }, + "section": null, + "location": null, + "sublocation": { + "sublocationId": "fant", + "title": "Fantasy" + } + }, + "logisticsPlacement": [], + "materials": [ + { + "itemNumber": "5387480422", + "available": true, + "periodical": null, + "materialGroup": { + "name": "Standard", + "description": "Std. materialegruppe" + } + } + ] + }, + { + "branch": { + "branchId": "DK-710109", + "title": "Øbro Jagtvej" + }, + "lmsPlacement": null, + "logisticsPlacement": ["Øbro Jagtvej", "Stuen", "Børn", "Fantasy"], + "materials": [ + { + "itemNumber": "5387480007", + "available": true, + "periodical": null, + "materialGroup": { + "name": "Standard", + "description": "Std. materialegruppe" } + } + ] + }, + { + "branch": { + "branchId": "DK-710104", + "title": "Blågården" + }, + "lmsPlacement": { + "department": { + "departmentId": "bø", + "title": "Børn" }, + "section": null, + "location": null, + "sublocation": { + "sublocationId": "fant", + "title": "Fantasy" + } + }, + "logisticsPlacement": [], + "materials": [ { - "itemNumber": "A1011423957A", + "itemNumber": "5387480171", "available": false, "periodical": null, "materialGroup": { - "name": "standard", - "description": "31 dages lånetid til alm lånere" + "name": "Standard", + "description": "Std. materialegruppe" } }, { - "itemNumber": "A1011423958A", + "itemNumber": "5402754038", "available": false, "periodical": null, "materialGroup": { - "name": "standard", - "description": "31 dages lånetid til alm lånere" + "name": "Standard", + "description": "Std. materialegruppe" + } + } + ] + }, + { + "branch": { + "branchId": "DK-710115", + "title": "Sydhavn" + }, + "lmsPlacement": null, + "logisticsPlacement": ["Sydhavn", "Børn", "Fantasy"], + "materials": [ + { + "itemNumber": "5387479971", + "available": true, + "periodical": null, + "materialGroup": { + "name": "Standard", + "description": "Std. materialegruppe" } + } + ] + }, + { + "branch": { + "branchId": "DK-710112", + "title": "Solvang" + }, + "lmsPlacement": { + "department": { + "departmentId": "bø", + "title": "Børn" }, + "section": null, + "location": null, + "sublocation": { + "sublocationId": "fant", + "title": "Fantasy" + } + }, + "logisticsPlacement": [], + "materials": [ { - "itemNumber": "A1011423959A", + "itemNumber": "5402754054", "available": false, "periodical": null, "materialGroup": { - "name": "standard", - "description": "31 dages lånetid til alm lånere" + "name": "Standard", + "description": "Std. materialegruppe" } + } + ] + }, + { + "branch": { + "branchId": "DK-710111", + "title": "Nørrebro" + }, + "lmsPlacement": { + "department": { + "departmentId": "bø", + "title": "Børn" }, + "section": null, + "location": null, + "sublocation": { + "sublocationId": "fant", + "title": "Fantasy" + } + }, + "logisticsPlacement": [], + "materials": [ { - "itemNumber": "A1011423960A", + "itemNumber": "5387480139", "available": false, "periodical": null, "materialGroup": { - "name": "standard", - "description": "31 dages lånetid til alm lånere" + "name": "Standard", + "description": "Std. materialegruppe" } }, { - "itemNumber": "A1011423961A", + "itemNumber": "5387480279", "available": false, "periodical": null, "materialGroup": { - "name": "standard", - "description": "31 dages lånetid til alm lånere" + "name": "Standard", + "description": "Std. materialegruppe" } }, { - "itemNumber": "A1011423962A", + "itemNumber": "5387480287", "available": false, "periodical": null, "materialGroup": { - "name": "standard", - "description": "31 dages lånetid til alm lånere" + "name": "Standard", + "description": "Std. materialegruppe" } + } + ] + }, + { + "branch": { + "branchId": "DK-710121", + "title": "Østerbro" + }, + "lmsPlacement": { + "department": { + "departmentId": "bø", + "title": "Børn" }, + "section": null, + "location": null, + "sublocation": { + "sublocationId": "fant", + "title": "Fantasy" + } + }, + "logisticsPlacement": [], + "materials": [ { - "itemNumber": "A1011423963A", + "itemNumber": "5387480120", "available": false, "periodical": null, "materialGroup": { - "name": "standard", - "description": "31 dages lånetid til alm lånere" + "name": "Standard", + "description": "Std. materialegruppe" } }, { - "itemNumber": "A1011423964A", + "itemNumber": "5402754011", "available": false, "periodical": null, "materialGroup": { - "name": "standard", - "description": "31 dages lånetid til alm lånere" + "name": "Standard", + "description": "Std. materialegruppe" + } + } + ] + }, + { + "branch": { + "branchId": "DK-710111", + "title": "Nørrebro" + }, + "lmsPlacement": null, + "logisticsPlacement": ["Nørrebro", "Børn", "Fantasy"], + "materials": [ + { + "itemNumber": "5387479998", + "available": true, + "periodical": null, + "materialGroup": { + "name": "Standard", + "description": "Std. materialegruppe" + } + } + ] + }, + { + "branch": { + "branchId": "FBS-101007", + "title": "HB Rigshospitalet" + }, + "lmsPlacement": { + "department": { + "departmentId": "bø", + "title": "Børn" + }, + "section": null, + "location": null, + "sublocation": { + "sublocationId": "oplæs", + "title": "Højtlæsning" + } + }, + "logisticsPlacement": [], + "materials": [ + { + "itemNumber": "5420305267", + "available": true, + "periodical": null, + "materialGroup": { + "name": "RHnonres", + "description": "Nonres materialer på rigshospitalet (lavet så kun personer på RH kan låne og reserverer på disse)" + } + } + ] + }, + { + "branch": { + "branchId": "DK-710108", + "title": "Islands Brygge" + }, + "lmsPlacement": null, + "logisticsPlacement": ["Islands Brygge", "Børn", "Fantasy"], + "materials": [ + { + "itemNumber": "5387480295", + "available": true, + "periodical": null, + "materialGroup": { + "name": "Standard", + "description": "Std. materialegruppe" } + } + ] + }, + { + "branch": { + "branchId": "DK-710107", + "title": "Husum" + }, + "lmsPlacement": { + "department": { + "departmentId": "bø", + "title": "Børn" }, + "section": null, + "location": null, + "sublocation": { + "sublocationId": "fant", + "title": "Fantasy" + } + }, + "logisticsPlacement": [], + "materials": [ { - "itemNumber": "A1011423965A", + "itemNumber": "5387480104", "available": false, "periodical": null, "materialGroup": { - "name": "standard", - "description": "31 dages lånetid til alm lånere" + "name": "Standard", + "description": "Std. materialegruppe" } + } + ] + }, + { + "branch": { + "branchId": "DK-710119", + "title": "Vesterbro" + }, + "lmsPlacement": { + "department": { + "departmentId": "bø", + "title": "Børn" }, + "section": null, + "location": null, + "sublocation": { + "sublocationId": "fant", + "title": "Fantasy" + } + }, + "logisticsPlacement": [], + "materials": [ { - "itemNumber": "A1011423966A", + "itemNumber": "5387480090", "available": false, "periodical": null, "materialGroup": { - "name": "standard", - "description": "31 dages lånetid til alm lånere" + "name": "Standard", + "description": "Std. materialegruppe" } }, { - "itemNumber": "A1011423967A", + "itemNumber": "5387480163", "available": false, "periodical": null, "materialGroup": { - "name": "standard", - "description": "31 dages lånetid til alm lånere" + "name": "Standard", + "description": "Std. materialegruppe" } }, { - "itemNumber": "A1011423968A", + "itemNumber": "5402754070", "available": false, "periodical": null, "materialGroup": { - "name": "standard", - "description": "31 dages lånetid til alm lånere" + "name": "Standard", + "description": "Std. materialegruppe" } + } + ] + }, + { + "branch": { + "branchId": "DK-710110", + "title": "Biblioteket Rentemestervej" + }, + "lmsPlacement": null, + "logisticsPlacement": [ + "Biblioteket Rentemestervej", + "1. sal", + "Børn", + "Fantasy" + ], + "materials": [ + { + "itemNumber": "5387480155", + "available": true, + "periodical": null, + "materialGroup": { + "name": "Standard", + "description": "Std. materialegruppe" + } + } + ] + }, + { + "branch": { + "branchId": "DK-710108", + "title": "Islands Brygge" + }, + "lmsPlacement": { + "department": { + "departmentId": "bø", + "title": "Børn" }, + "section": null, + "location": null, + "sublocation": { + "sublocationId": "fant", + "title": "Fantasy" + } + }, + "logisticsPlacement": [], + "materials": [ { - "itemNumber": "A1011423969A", + "itemNumber": "5387480023", "available": false, "periodical": null, "materialGroup": { - "name": "standard", - "description": "31 dages lånetid til alm lånere" + "name": "Standard", + "description": "Std. materialegruppe" } + } + ] + }, + { + "branch": { + "branchId": "DK-710118", + "title": "Vanløse" + }, + "lmsPlacement": { + "department": { + "departmentId": "bø", + "title": "Børn" }, + "section": null, + "location": null, + "sublocation": { + "sublocationId": "fant", + "title": "Fantasy" + } + }, + "logisticsPlacement": [], + "materials": [ { - "itemNumber": "A1011423970A", + "itemNumber": "5387480317", "available": false, "periodical": null, "materialGroup": { - "name": "standard", - "description": "31 dages lånetid til alm lånere" + "name": "Standard", + "description": "Std. materialegruppe" + } + } + ] + }, + { + "branch": { + "branchId": "DK-710122", + "title": "Ørestad" + }, + "lmsPlacement": null, + "logisticsPlacement": ["Ørestad", "Stuen", "Børn", "Fantasy"], + "materials": [ + { + "itemNumber": "5387480228", + "available": true, + "periodical": null, + "materialGroup": { + "name": "Standard", + "description": "Std. materialegruppe" + } + }, + { + "itemNumber": "5402754062", + "available": true, + "periodical": null, + "materialGroup": { + "name": "Standard", + "description": "Std. materialegruppe" } + } + ] + }, + { + "branch": { + "branchId": "DK-710115", + "title": "Sydhavn" + }, + "lmsPlacement": { + "department": { + "departmentId": "bø", + "title": "Børn" }, + "section": null, + "location": null, + "sublocation": { + "sublocationId": "fant", + "title": "Fantasy" + } + }, + "logisticsPlacement": [], + "materials": [ { - "itemNumber": "A1011423971A", + "itemNumber": "5387480260", "available": false, "periodical": null, "materialGroup": { - "name": "standard", - "description": "31 dages lånetid til alm lånere" + "name": "Standard", + "description": "Std. materialegruppe" + } + } + ] + }, + { + "branch": { + "branchId": "DK-710116", + "title": "Tingbjerg" + }, + "lmsPlacement": { + "department": { + "departmentId": "bø", + "title": "Børn" + }, + "section": null, + "location": null, + "sublocation": { + "sublocationId": "fant", + "title": "Fantasy" + } + }, + "logisticsPlacement": [], + "materials": [ + { + "itemNumber": "5387479955", + "available": true, + "periodical": null, + "materialGroup": { + "name": "Standard", + "description": "Std. materialegruppe" + } + }, + { + "itemNumber": "5387480058", + "available": true, + "periodical": null, + "materialGroup": { + "name": "Standard", + "description": "Std. materialegruppe" + } + } + ] + }, + { + "branch": { + "branchId": "FBS-101009", + "title": "Fjernlager 2" + }, + "lmsPlacement": { + "department": { + "departmentId": "bø", + "title": "Børn" + }, + "section": null, + "location": null, + "sublocation": { + "sublocationId": "fant", + "title": "Fantasy" + } + }, + "logisticsPlacement": [], + "materials": [ + { + "itemNumber": "5402753988", + "available": true, + "periodical": null, + "materialGroup": { + "name": "Standard", + "description": "Std. materialegruppe" + } + }, + { + "itemNumber": "5402754003", + "available": true, + "periodical": null, + "materialGroup": { + "name": "Standard", + "description": "Std. materialegruppe" + } + }, + { + "itemNumber": "5402754046", + "available": true, + "periodical": null, + "materialGroup": { + "name": "Standard", + "description": "Std. materialegruppe" + } + }, + { + "itemNumber": "5402754089", + "available": true, + "periodical": null, + "materialGroup": { + "name": "Standard", + "description": "Std. materialegruppe" } + } + ] + }, + { + "branch": { + "branchId": "DK-710105", + "title": "Brønshøj" + }, + "lmsPlacement": { + "department": { + "departmentId": "bø", + "title": "Børn" }, + "section": null, + "location": null, + "sublocation": { + "sublocationId": "fant", + "title": "Fantasy" + } + }, + "logisticsPlacement": [], + "materials": [ { - "itemNumber": "A1011423972A", + "itemNumber": "5387480074", "available": false, "periodical": null, "materialGroup": { - "name": "standard", - "description": "31 dages lånetid til alm lånere" + "name": "Standard", + "description": "Std. materialegruppe" } }, { - "itemNumber": "A1011423973A", + "itemNumber": "5387480082", "available": false, "periodical": null, "materialGroup": { - "name": "standard", - "description": "31 dages lånetid til alm lånere" + "name": "Standard", + "description": "Std. materialegruppe" } }, { - "itemNumber": "A1011423974A", + "itemNumber": "5387480201", "available": false, "periodical": null, "materialGroup": { - "name": "standard", - "description": "31 dages lånetid til alm lånere" + "name": "Standard", + "description": "Std. materialegruppe" + } + } + ] + }, + { + "branch": { + "branchId": "DK-710100", + "title": "Hovedbiblioteket" + }, + "lmsPlacement": null, + "logisticsPlacement": [ + "Hovedbiblioteket", + "2. sal > LÆS", + "Ny Fantasy" + ], + "materials": [ + { + "itemNumber": "5387480414", + "available": true, + "periodical": null, + "materialGroup": { + "name": "Standard", + "description": "Std. materialegruppe" } + } + ] + }, + { + "branch": { + "branchId": "DK-710120", + "title": "Vigerslev" + }, + "lmsPlacement": { + "department": { + "departmentId": "bø", + "title": "Børn" }, + "section": null, + "location": null, + "sublocation": { + "sublocationId": "fant", + "title": "Fantasy" + } + }, + "logisticsPlacement": [], + "materials": [ { - "itemNumber": "A1011423975A", + "itemNumber": "5387480147", "available": false, "periodical": null, "materialGroup": { - "name": "standard", - "description": "31 dages lånetid til alm lånere" + "name": "Standard", + "description": "Std. materialegruppe" } }, { - "itemNumber": "A1011423976A", + "itemNumber": "5402753996", "available": false, "periodical": null, "materialGroup": { - "name": "standard", - "description": "31 dages lånetid til alm lånere" + "name": "Standard", + "description": "Std. materialegruppe" + } + } + ] + }, + { + "branch": { + "branchId": "DK-710118", + "title": "Vanløse" + }, + "lmsPlacement": null, + "logisticsPlacement": ["Vanløse", "Børn", "Fantasy"], + "materials": [ + { + "itemNumber": "5387480252", + "available": true, + "periodical": null, + "materialGroup": { + "name": "Standard", + "description": "Std. materialegruppe" } + } + ] + }, + { + "branch": { + "branchId": "DK-710106", + "title": "Christianshavn" + }, + "lmsPlacement": { + "department": { + "departmentId": "bø", + "title": "Børn" }, + "section": null, + "location": null, + "sublocation": { + "sublocationId": "fant", + "title": "Fantasy" + } + }, + "logisticsPlacement": [], + "materials": [ { - "itemNumber": "A1011423977A", + "itemNumber": "5387479963", "available": false, "periodical": null, "materialGroup": { - "name": "standard", - "description": "31 dages lånetid til alm lånere" + "name": "Standard", + "description": "Std. materialegruppe" } + } + ] + }, + { + "branch": { + "branchId": "DK-710117", + "title": "Valby" + }, + "lmsPlacement": { + "department": { + "departmentId": "bø", + "title": "Børn" }, + "section": null, + "location": null, + "sublocation": { + "sublocationId": "fant", + "title": "Fantasy" + } + }, + "logisticsPlacement": [], + "materials": [ { - "itemNumber": "A1011423978A", + "itemNumber": "5387480066", "available": false, "periodical": null, "materialGroup": { - "name": "standard", - "description": "31 dages lånetid til alm lånere" + "name": "Standard", + "description": "Std. materialegruppe" } }, { - "itemNumber": "A1011423979A", + "itemNumber": "5387480309", + "available": false, + "periodical": null, + "materialGroup": { + "name": "Standard", + "description": "Std. materialegruppe" + } + }, + { + "itemNumber": "5387480333", + "available": false, + "periodical": null, + "materialGroup": { + "name": "Standard", + "description": "Std. materialegruppe" + } + } + ] + }, + { + "branch": { + "branchId": "DK-710100", + "title": "Hovedbiblioteket" + }, + "lmsPlacement": { + "department": { + "departmentId": "bø", + "title": "Børn" + }, + "section": null, + "location": null, + "sublocation": { + "sublocationId": "fant", + "title": "Fantasy" + } + }, + "logisticsPlacement": [], + "materials": [ + { + "itemNumber": "5387480112", + "available": false, + "periodical": null, + "materialGroup": { + "name": "Standard", + "description": "Std. materialegruppe" + } + }, + { + "itemNumber": "5387480341", + "available": false, + "periodical": null, + "materialGroup": { + "name": "Standard", + "description": "Std. materialegruppe" + } + }, + { + "itemNumber": "5387480392", + "available": false, + "periodical": null, + "materialGroup": { + "name": "Standard", + "description": "Std. materialegruppe" + } + }, + { + "itemNumber": "5387480430", + "available": false, + "periodical": null, + "materialGroup": { + "name": "Standard", + "description": "Std. materialegruppe" + } + } + ] + }, + { + "branch": { + "branchId": "DK-710113", + "title": "Sundby" + }, + "lmsPlacement": null, + "logisticsPlacement": ["Sundby", "Stuen", "Børn", "Fantasy"], + "materials": [ + { + "itemNumber": "5387480325", + "available": true, + "periodical": null, + "materialGroup": { + "name": "Standard", + "description": "Std. materialegruppe" + } + } + ] + }, + { + "branch": { + "branchId": "DK-710113", + "title": "Sundby" + }, + "lmsPlacement": { + "department": { + "departmentId": "bø", + "title": "Børn" + }, + "section": null, + "location": null, + "sublocation": { + "sublocationId": "fant", + "title": "Fantasy" + } + }, + "logisticsPlacement": [], + "materials": [ + { + "itemNumber": "5387480406", + "available": false, + "periodical": null, + "materialGroup": { + "name": "Standard", + "description": "Std. materialegruppe" + } + } + ] + }, + { + "branch": { + "branchId": "DK-710109", + "title": "Øbro Jagtvej" + }, + "lmsPlacement": { + "department": { + "departmentId": "bø", + "title": "Børn" + }, + "section": null, + "location": null, + "sublocation": { + "sublocationId": "fant", + "title": "Fantasy" + } + }, + "logisticsPlacement": [], + "materials": [ + { + "itemNumber": "5387480198", + "available": false, + "periodical": null, + "materialGroup": { + "name": "Standard", + "description": "Std. materialegruppe" + } + }, + { + "itemNumber": "5387480376", + "available": false, + "periodical": null, + "materialGroup": { + "name": "Standard", + "description": "Std. materialegruppe" + } + } + ] + } + ] + }, + { + "recordId": "29368872", + "reservable": false, + "reservations": 0, + "holdings": [] + }, + { + "recordId": "51980204", + "reservable": true, + "reservations": 0, + "holdings": [ + { + "branch": { + "branchId": "DK-710116", + "title": "Tingbjerg" + }, + "lmsPlacement": { + "department": { + "departmentId": "bø", + "title": "Børn" + }, + "section": null, + "location": null, + "sublocation": { + "sublocationId": "fant", + "title": "Fantasy" + } + }, + "logisticsPlacement": [], + "materials": [ + { + "itemNumber": "5385974649", + "available": true, + "periodical": null, + "materialGroup": { + "name": "Standard", + "description": "Std. materialegruppe" + } + } + ] + }, + { + "branch": { + "branchId": "FBS-101009", + "title": "Fjernlager 2" + }, + "lmsPlacement": { + "department": { + "departmentId": "bø", + "title": "Børn" + }, + "section": null, + "location": null, + "sublocation": { + "sublocationId": "fant", + "title": "Fantasy" + } + }, + "logisticsPlacement": [], + "materials": [ + { + "itemNumber": "5159370277", + "available": true, + "periodical": null, + "materialGroup": { + "name": "Standard", + "description": "Std. materialegruppe" + } + }, + { + "itemNumber": "5159370269", + "available": true, + "periodical": null, + "materialGroup": { + "name": "Standard", + "description": "Std. materialegruppe" + } + }, + { + "itemNumber": "5417428382", + "available": true, + "periodical": null, + "materialGroup": { + "name": "Standard", + "description": "Std. materialegruppe" + } + }, + { + "itemNumber": "5417428390", + "available": true, + "periodical": null, + "materialGroup": { + "name": "Standard", + "description": "Std. materialegruppe" + } + }, + { + "itemNumber": "5417428404", + "available": true, + "periodical": null, + "materialGroup": { + "name": "Standard", + "description": "Std. materialegruppe" + } + }, + { + "itemNumber": "5417428412", + "available": true, + "periodical": null, + "materialGroup": { + "name": "Standard", + "description": "Std. materialegruppe" + } + }, + { + "itemNumber": "5417428420", + "available": true, + "periodical": null, + "materialGroup": { + "name": "Standard", + "description": "Std. materialegruppe" + } + }, + { + "itemNumber": "5417428439", + "available": true, + "periodical": null, + "materialGroup": { + "name": "Standard", + "description": "Std. materialegruppe" + } + }, + { + "itemNumber": "5417428447", + "available": true, + "periodical": null, + "materialGroup": { + "name": "Standard", + "description": "Std. materialegruppe" + } + }, + { + "itemNumber": "5402753309", + "available": true, + "periodical": null, + "materialGroup": { + "name": "Standard", + "description": "Std. materialegruppe" + } + }, + { + "itemNumber": "5417428463", + "available": true, + "periodical": null, + "materialGroup": { + "name": "Standard", + "description": "Std. materialegruppe" + } + } + ] + }, + { + "branch": { + "branchId": "DK-710112", + "title": "Solvang" + }, + "lmsPlacement": null, + "logisticsPlacement": ["Solvang Centret", "Børn", "Fantasy"], + "materials": [ + { + "itemNumber": "5130673920", + "available": true, + "periodical": null, + "materialGroup": { + "name": "Standard", + "description": "Std. materialegruppe" + } + } + ] + }, + { + "branch": { + "branchId": "DK-710110", + "title": "Biblioteket Rentemestervej" + }, + "lmsPlacement": { + "department": { + "departmentId": "bø", + "title": "Børn" + }, + "section": null, + "location": null, + "sublocation": { + "sublocationId": "fant", + "title": "Fantasy" + } + }, + "logisticsPlacement": [], + "materials": [ + { + "itemNumber": "5159359990", + "available": false, + "periodical": null, + "materialGroup": { + "name": "Standard", + "description": "Std. materialegruppe" + } + }, + { + "itemNumber": "5402753287", + "available": false, + "periodical": null, + "materialGroup": { + "name": "Standard", + "description": "Std. materialegruppe" + } + } + ] + }, + { + "branch": { + "branchId": "DK-710106", + "title": "Christianshavn" + }, + "lmsPlacement": null, + "logisticsPlacement": ["Christianshavn", "Børn", "Fantasy"], + "materials": [ + { + "itemNumber": "5402753236", + "available": true, + "periodical": null, + "materialGroup": { + "name": "Standard", + "description": "Std. materialegruppe" + } + } + ] + }, + { + "branch": { + "branchId": "DK-710122", + "title": "Ørestad" + }, + "lmsPlacement": { + "department": { + "departmentId": "bø", + "title": "Børn" + }, + "section": null, + "location": null, + "sublocation": { + "sublocationId": "fant", + "title": "Fantasy" + } + }, + "logisticsPlacement": [], + "materials": [ + { + "itemNumber": "5385974614", + "available": false, + "periodical": null, + "materialGroup": { + "name": "Standard", + "description": "Std. materialegruppe" + } + } + ] + }, + { + "branch": { + "branchId": "DK-710121", + "title": "Østerbro" + }, + "lmsPlacement": null, + "logisticsPlacement": ["Østerbro", "Børn", "Fantasy"], + "materials": [ + { + "itemNumber": "5159370226", + "available": true, + "periodical": null, + "materialGroup": { + "name": "Standard", + "description": "Std. materialegruppe" + } + } + ] + }, + { + "branch": { + "branchId": "DK-710114", + "title": "Bibliotekshuset" + }, + "lmsPlacement": { + "department": { + "departmentId": "bø", + "title": "Børn" + }, + "section": null, + "location": null, + "sublocation": { + "sublocationId": "fant", + "title": "Fantasy" + } + }, + "logisticsPlacement": [], + "materials": [ + { + "itemNumber": "5159370188", + "available": false, + "periodical": null, + "materialGroup": { + "name": "Standard", + "description": "Std. materialegruppe" + } + }, + { + "itemNumber": "5417428455", + "available": false, + "periodical": null, + "materialGroup": { + "name": "Standard", + "description": "Std. materialegruppe" + } + } + ] + }, + { + "branch": { + "branchId": "DK-710120", + "title": "Vigerslev" + }, + "lmsPlacement": { + "department": { + "departmentId": "bø", + "title": "Børn" + }, + "section": null, + "location": null, + "sublocation": { + "sublocationId": "fant", + "title": "Fantasy" + } + }, + "logisticsPlacement": [], + "materials": [ + { + "itemNumber": "5417428374", + "available": false, + "periodical": null, + "materialGroup": { + "name": "Standard", + "description": "Std. materialegruppe" + } + } + ] + }, + { + "branch": { + "branchId": "FBS-101008", + "title": "Fjernlager 1" + }, + "lmsPlacement": { + "department": { + "departmentId": "bø", + "title": "Børn" + }, + "section": null, + "location": null, + "sublocation": { + "sublocationId": "fant", + "title": "Fantasy" + } + }, + "logisticsPlacement": [], + "materials": [ + { + "itemNumber": "5385974665", + "available": true, + "periodical": null, + "materialGroup": { + "name": "Standard", + "description": "Std. materialegruppe" + } + } + ] + }, + { + "branch": { + "branchId": "DK-710119", + "title": "Vesterbro" + }, + "lmsPlacement": null, + "logisticsPlacement": ["Vesterbro", "1. Sal", "Børn", "Fantasy"], + "materials": [ + { + "itemNumber": "5130665243", + "available": true, + "periodical": null, + "materialGroup": { + "name": "Standard", + "description": "Std. materialegruppe" + } + } + ] + }, + { + "branch": { + "branchId": "DK-710104", + "title": "Blågården" + }, + "lmsPlacement": { + "department": { + "departmentId": "bø", + "title": "Børn" + }, + "section": null, + "location": null, + "sublocation": { + "sublocationId": "fant", + "title": "Fantasy" + } + }, + "logisticsPlacement": [], + "materials": [ + { + "itemNumber": "5402753228", + "available": false, + "periodical": null, + "materialGroup": { + "name": "Standard", + "description": "Std. materialegruppe" + } + }, + { + "itemNumber": "5402753260", + "available": false, + "periodical": null, + "materialGroup": { + "name": "Standard", + "description": "Std. materialegruppe" + } + } + ] + }, + { + "branch": { + "branchId": "DK-710118", + "title": "Vanløse" + }, + "lmsPlacement": null, + "logisticsPlacement": ["Vanløse", "Børn", "Fantasy"], + "materials": [ + { + "itemNumber": "5385974576", + "available": true, + "periodical": null, + "materialGroup": { + "name": "Standard", + "description": "Std. materialegruppe" + } + } + ] + }, + { + "branch": { + "branchId": "DK-710117", + "title": "Valby" + }, + "lmsPlacement": null, + "logisticsPlacement": ["Valby", "Børn", "Fantasy"], + "materials": [ + { + "itemNumber": "5402753244", + "available": true, + "periodical": null, + "materialGroup": { + "name": "Standard", + "description": "Std. materialegruppe" + } + } + ] + }, + { + "branch": { + "branchId": "DK-710111", + "title": "Nørrebro" + }, + "lmsPlacement": { + "department": { + "departmentId": "bø", + "title": "Børn" + }, + "section": null, + "location": null, + "sublocation": { + "sublocationId": "fant", + "title": "Fantasy" + } + }, + "logisticsPlacement": [], + "materials": [ + { + "itemNumber": "5130550357", + "available": false, + "periodical": null, + "materialGroup": { + "name": "Standard", + "description": "Std. materialegruppe" + } + } + ] + }, + { + "branch": { + "branchId": "DK-710121", + "title": "Østerbro" + }, + "lmsPlacement": { + "department": { + "departmentId": "bø", + "title": "Børn" + }, + "section": null, + "location": null, + "sublocation": { + "sublocationId": "fant", + "title": "Fantasy" + } + }, + "logisticsPlacement": [], + "materials": [ + { + "itemNumber": "5385974568", + "available": false, + "periodical": null, + "materialGroup": { + "name": "Standard", + "description": "Std. materialegruppe" + } + } + ] + }, + { + "branch": { + "branchId": "DK-710117", + "title": "Valby" + }, + "lmsPlacement": { + "department": { + "departmentId": "bø", + "title": "Børn" + }, + "section": null, + "location": null, + "sublocation": { + "sublocationId": "fant", + "title": "Fantasy" + } + }, + "logisticsPlacement": [], + "materials": [ + { + "itemNumber": "5402753279", + "available": false, + "periodical": null, + "materialGroup": { + "name": "Standard", + "description": "Std. materialegruppe" + } + }, + { + "itemNumber": "5159388095", + "available": false, + "periodical": null, + "materialGroup": { + "name": "Standard", + "description": "Std. materialegruppe" + } + } + ] + }, + { + "branch": { + "branchId": "FBS-101004", + "title": "HB Københavns fængsler" + }, + "lmsPlacement": { + "department": { + "departmentId": "bø", + "title": "Børn" + }, + "section": null, + "location": null, + "sublocation": { + "sublocationId": "fant", + "title": "Fantasy" + } + }, + "logisticsPlacement": [], + "materials": [ + { + "itemNumber": "5159370201", + "available": false, + "periodical": null, + "materialGroup": { + "name": "Standard", + "description": "Std. materialegruppe" + } + } + ] + }, + { + "branch": { + "branchId": "DK-710119", + "title": "Vesterbro" + }, + "lmsPlacement": { + "department": { + "departmentId": "bø", + "title": "Børn" + }, + "section": null, + "location": null, + "sublocation": { + "sublocationId": "fant", + "title": "Fantasy" + } + }, + "logisticsPlacement": [], + "materials": [ + { + "itemNumber": "5402753317", + "available": false, + "periodical": null, + "materialGroup": { + "name": "Standard", + "description": "Std. materialegruppe" + } + }, + { + "itemNumber": "5402753295", + "available": false, + "periodical": null, + "materialGroup": { + "name": "Standard", + "description": "Std. materialegruppe" + } + } + ] + }, + { + "branch": { + "branchId": "DK-710100", + "title": "Hovedbiblioteket" + }, + "lmsPlacement": { + "department": { + "departmentId": "bø", + "title": "Børn" + }, + "section": null, + "location": null, + "sublocation": { + "sublocationId": "fant", + "title": "Fantasy" + } + }, + "logisticsPlacement": [], + "materials": [ + { + "itemNumber": "5385974606", + "available": false, + "periodical": null, + "materialGroup": { + "name": "Standard", + "description": "Std. materialegruppe" + } + }, + { + "itemNumber": "5385974630", + "available": false, + "periodical": null, + "materialGroup": { + "name": "Standard", + "description": "Std. materialegruppe" + } + } + ] + }, + { + "branch": { + "branchId": "DK-710108", + "title": "Islands Brygge" + }, + "lmsPlacement": { + "department": { + "departmentId": "bø", + "title": "Børn" + }, + "section": null, + "location": null, + "sublocation": { + "sublocationId": "fant", + "title": "Fantasy" + } + }, + "logisticsPlacement": [], + "materials": [ + { + "itemNumber": "5159388087", + "available": false, + "periodical": null, + "materialGroup": { + "name": "Standard", + "description": "Std. materialegruppe" + } + } + ] + }, + { + "branch": { + "branchId": "DK-710118", + "title": "Vanløse" + }, + "lmsPlacement": { + "department": { + "departmentId": "bø", + "title": "Børn" + }, + "section": null, + "location": null, + "sublocation": { + "sublocationId": "fant", + "title": "Fantasy" + } + }, + "logisticsPlacement": [], + "materials": [ + { + "itemNumber": "5385974622", + "available": false, + "periodical": null, + "materialGroup": { + "name": "Standard", + "description": "Std. materialegruppe" + } + }, + { + "itemNumber": "5385974592", + "available": false, + "periodical": null, + "materialGroup": { + "name": "Standard", + "description": "Std. materialegruppe" + } + }, + { + "itemNumber": "5402753252", + "available": false, + "periodical": null, + "materialGroup": { + "name": "Standard", + "description": "Std. materialegruppe" + } + } + ] + }, + { + "branch": { + "branchId": "DK-710109", + "title": "Øbro Jagtvej" + }, + "lmsPlacement": { + "department": { + "departmentId": "bø", + "title": "Børn" + }, + "section": null, + "location": null, + "sublocation": { + "sublocationId": "fant", + "title": "Fantasy" + } + }, + "logisticsPlacement": [], + "materials": [ + { + "itemNumber": "5385974657", + "available": false, + "periodical": null, + "materialGroup": { + "name": "Standard", + "description": "Std. materialegruppe" + } + }, + { + "itemNumber": "5385974584", + "available": false, + "periodical": null, + "materialGroup": { + "name": "Standard", + "description": "Std. materialegruppe" + } + } + ] + } + ] + }, + { + "recordId": "54871996", + "reservable": true, + "reservations": 1, + "holdings": [ + { + "branch": { + "branchId": "DK-710116", + "title": "Tingbjerg" + }, + "lmsPlacement": { + "department": { + "departmentId": "bø", + "title": "Børn" + }, + "section": null, + "location": null, + "sublocation": { + "sublocationId": "fant", + "title": "Fantasy" + } + }, + "logisticsPlacement": [], + "materials": [ + { + "itemNumber": "5417430174", + "available": true, + "periodical": null, + "materialGroup": { + "name": "Standard", + "description": "Std. materialegruppe" + } + } + ] + }, + { + "branch": { + "branchId": "FBS-101009", + "title": "Fjernlager 2" + }, + "lmsPlacement": { + "department": { + "departmentId": "bø", + "title": "Børn" + }, + "section": null, + "location": null, + "sublocation": { + "sublocationId": "fant", + "title": "Fantasy" + } + }, + "logisticsPlacement": [], + "materials": [ + { + "itemNumber": "5402755719", + "available": true, + "periodical": null, + "materialGroup": { + "name": "Standard", + "description": "Std. materialegruppe" + } + }, + { + "itemNumber": "5402755735", + "available": true, + "periodical": null, + "materialGroup": { + "name": "Standard", + "description": "Std. materialegruppe" + } + }, + { + "itemNumber": "5417430239", + "available": true, + "periodical": null, + "materialGroup": { + "name": "Standard", + "description": "Std. materialegruppe" + } + } + ] + }, + { + "branch": { + "branchId": "DK-710105", + "title": "Brønshøj" + }, + "lmsPlacement": { + "department": { + "departmentId": "bø", + "title": "Børn" + }, + "section": null, + "location": null, + "sublocation": { + "sublocationId": "fant", + "title": "Fantasy" + } + }, + "logisticsPlacement": [], + "materials": [ + { + "itemNumber": "5402755778", + "available": false, + "periodical": null, + "materialGroup": { + "name": "Standard", + "description": "Std. materialegruppe" + } + } + ] + }, + { + "branch": { + "branchId": "DK-710121", + "title": "Østerbro" + }, + "lmsPlacement": null, + "logisticsPlacement": ["Østerbro", "Børn", "Fantasy"], + "materials": [ + { + "itemNumber": "5402755700", + "available": true, + "periodical": null, + "materialGroup": { + "name": "Standard", + "description": "Std. materialegruppe" + } + } + ] + }, + { + "branch": { + "branchId": "DK-710114", + "title": "Bibliotekshuset" + }, + "lmsPlacement": { + "department": { + "departmentId": "bø", + "title": "Børn" + }, + "section": null, + "location": null, + "sublocation": { + "sublocationId": "fant", + "title": "Fantasy" + } + }, + "logisticsPlacement": [], + "materials": [ + { + "itemNumber": "5402755743", + "available": false, + "periodical": null, + "materialGroup": { + "name": "Standard", + "description": "Std. materialegruppe" + } + }, + { + "itemNumber": "5417430182", + "available": false, + "periodical": null, + "materialGroup": { + "name": "Standard", + "description": "Std. materialegruppe" + } + }, + { + "itemNumber": "5417430220", + "available": false, + "periodical": null, + "materialGroup": { + "name": "Standard", + "description": "Std. materialegruppe" + } + } + ] + }, + { + "branch": { + "branchId": "DK-710104", + "title": "Blågården" + }, + "lmsPlacement": { + "department": { + "departmentId": "bø", + "title": "Børn" + }, + "section": null, + "location": null, + "sublocation": { + "sublocationId": "fant", + "title": "Fantasy" + } + }, + "logisticsPlacement": [], + "materials": [ + { + "itemNumber": "5385975556", + "available": false, + "periodical": null, + "materialGroup": { + "name": "Standard", + "description": "Std. materialegruppe" + } + } + ] + }, + { + "branch": { + "branchId": "DK-710106", + "title": "Christianshavn" + }, + "lmsPlacement": { + "department": { + "departmentId": "bø", + "title": "Børn" + }, + "section": null, + "location": null, + "sublocation": { + "sublocationId": "fant", + "title": "Fantasy" + } + }, + "logisticsPlacement": [], + "materials": [ + { + "itemNumber": "5402755697", + "available": false, + "periodical": null, + "materialGroup": { + "name": "Standard", + "description": "Std. materialegruppe" + } + }, + { + "itemNumber": "5402755751", + "available": false, + "periodical": null, + "materialGroup": { + "name": "Standard", + "description": "Std. materialegruppe" + } + } + ] + }, + { + "branch": { + "branchId": "DK-710112", + "title": "Solvang" + }, + "lmsPlacement": { + "department": { + "departmentId": "bø", + "title": "Børn" + }, + "section": null, + "location": null, + "sublocation": { + "sublocationId": "fant", + "title": "Fantasy" + } + }, + "logisticsPlacement": [], + "materials": [ + { + "itemNumber": "5385975572", + "available": false, + "periodical": null, + "materialGroup": { + "name": "Standard", + "description": "Std. materialegruppe" + } + } + ] + }, + { + "branch": { + "branchId": "DK-710111", + "title": "Nørrebro" + }, + "lmsPlacement": { + "department": { + "departmentId": "bø", + "title": "Børn" + }, + "section": null, + "location": null, + "sublocation": { + "sublocationId": "fant", + "title": "Fantasy" + } + }, + "logisticsPlacement": [], + "materials": [ + { + "itemNumber": "5215062033", + "available": false, + "periodical": null, + "materialGroup": { + "name": "Standard", + "description": "Std. materialegruppe" + } + }, + { + "itemNumber": "5385975564", + "available": false, + "periodical": null, + "materialGroup": { + "name": "Standard", + "description": "Std. materialegruppe" + } + } + ] + }, + { + "branch": { + "branchId": "DK-710117", + "title": "Valby" + }, + "lmsPlacement": { + "department": { + "departmentId": "bø", + "title": "Børn" + }, + "section": null, + "location": null, + "sublocation": { + "sublocationId": "fant", + "title": "Fantasy" + } + }, + "logisticsPlacement": [], + "materials": [ + { + "itemNumber": "5417430190", + "available": false, + "periodical": null, + "materialGroup": { + "name": "Standard", + "description": "Std. materialegruppe" + } + }, + { + "itemNumber": "5417430204", + "available": false, + "periodical": null, + "materialGroup": { + "name": "Standard", + "description": "Std. materialegruppe" + } + }, + { + "itemNumber": "5417430212", + "available": false, + "periodical": null, + "materialGroup": { + "name": "Standard", + "description": "Std. materialegruppe" + } + } + ] + }, + { + "branch": { + "branchId": "DK-710119", + "title": "Vesterbro" + }, + "lmsPlacement": { + "department": { + "departmentId": "bø", + "title": "Børn" + }, + "section": null, + "location": null, + "sublocation": { + "sublocationId": "fant", + "title": "Fantasy" + } + }, + "logisticsPlacement": [], + "materials": [ + { + "itemNumber": "5417430247", + "available": false, + "periodical": null, + "materialGroup": { + "name": "Standard", + "description": "Std. materialegruppe" + } + } + ] + }, + { + "branch": { + "branchId": "DK-710100", + "title": "Hovedbiblioteket" + }, + "lmsPlacement": { + "department": { + "departmentId": "bø", + "title": "Børn" + }, + "section": null, + "location": null, + "sublocation": { + "sublocationId": "fant", + "title": "Fantasy" + } + }, + "logisticsPlacement": [], + "materials": [ + { + "itemNumber": "5385975599", + "available": false, + "periodical": null, + "materialGroup": { + "name": "Standard", + "description": "Std. materialegruppe" + } + }, + { + "itemNumber": "5417430263", + "available": true, + "periodical": null, + "materialGroup": { + "name": "Standard", + "description": "Std. materialegruppe" + } + } + ] + }, + { + "branch": { + "branchId": "DK-710110", + "title": "Biblioteket Rentemestervej" + }, + "lmsPlacement": null, + "logisticsPlacement": [ + "Biblioteket Rentemestervej", + "1. sal", + "Børn", + "Fantasy" + ], + "materials": [ + { + "itemNumber": "5385975580", + "available": true, + "periodical": null, + "materialGroup": { + "name": "Standard", + "description": "Std. materialegruppe" + } + } + ] + }, + { + "branch": { + "branchId": "DK-710120", + "title": "Vigerslev" + }, + "lmsPlacement": null, + "logisticsPlacement": ["Vigerslev", "Børn", "Fantasy"], + "materials": [ + { + "itemNumber": "5402755727", + "available": true, + "periodical": null, + "materialGroup": { + "name": "Standard", + "description": "Std. materialegruppe" + } + } + ] + }, + { + "branch": { + "branchId": "DK-710113", + "title": "Sundby" + }, + "lmsPlacement": { + "department": { + "departmentId": "bø", + "title": "Børn" + }, + "section": null, + "location": null, + "sublocation": { + "sublocationId": "fant", + "title": "Fantasy" + } + }, + "logisticsPlacement": [], + "materials": [ + { + "itemNumber": "5417430255", + "available": false, + "periodical": null, + "materialGroup": { + "name": "Standard", + "description": "Std. materialegruppe" + } + } + ] + }, + { + "branch": { + "branchId": "DK-710118", + "title": "Vanløse" + }, + "lmsPlacement": { + "department": { + "departmentId": "bø", + "title": "Børn" + }, + "section": null, + "location": null, + "sublocation": { + "sublocationId": "fant", + "title": "Fantasy" + } + }, + "logisticsPlacement": [], + "materials": [ + { + "itemNumber": "5385975548", + "available": false, + "periodical": null, + "materialGroup": { + "name": "Standard", + "description": "Std. materialegruppe" + } + } + ] + }, + { + "branch": { + "branchId": "DK-710109", + "title": "Øbro Jagtvej" + }, + "lmsPlacement": { + "department": { + "departmentId": "bø", + "title": "Børn" + }, + "section": null, + "location": null, + "sublocation": { + "sublocationId": "fant", + "title": "Fantasy" + } + }, + "logisticsPlacement": [], + "materials": [ + { + "itemNumber": "5385975602", "available": false, "periodical": null, "materialGroup": { - "name": "standard", - "description": "31 dages lånetid til alm lånere" + "name": "Standard", + "description": "Std. materialegruppe" } } ] diff --git a/yarn.lock b/yarn.lock index 5fd6a7d42..c8ac81cfc 100644 --- a/yarn.lock +++ b/yarn.lock @@ -318,62 +318,62 @@ dependencies: "@types/node" "*" -"@typescript-eslint/eslint-plugin@^8.16.0": - version "8.16.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.16.0.tgz#ac56825bcdf3b392fc76a94b1315d4a162f201a6" - integrity sha512-5YTHKV8MYlyMI6BaEG7crQ9BhSc8RxzshOReKwZwRWN0+XvvTOm+L/UYLCYxFpfwYuAAqhxiq4yae0CMFwbL7Q== +"@typescript-eslint/eslint-plugin@^8.17.0": + version "8.17.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.17.0.tgz#2ee073c421f4e81e02d10e731241664b6253b23c" + integrity sha512-HU1KAdW3Tt8zQkdvNoIijfWDMvdSweFYm4hWh+KwhPstv+sCmWb89hCIP8msFm9N1R/ooh9honpSuvqKWlYy3w== dependencies: "@eslint-community/regexpp" "^4.10.0" - "@typescript-eslint/scope-manager" "8.16.0" - "@typescript-eslint/type-utils" "8.16.0" - "@typescript-eslint/utils" "8.16.0" - "@typescript-eslint/visitor-keys" "8.16.0" + "@typescript-eslint/scope-manager" "8.17.0" + "@typescript-eslint/type-utils" "8.17.0" + "@typescript-eslint/utils" "8.17.0" + "@typescript-eslint/visitor-keys" "8.17.0" graphemer "^1.4.0" ignore "^5.3.1" natural-compare "^1.4.0" ts-api-utils "^1.3.0" -"@typescript-eslint/parser@^8.16.0": - version "8.16.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-8.16.0.tgz#ee5b2d6241c1ab3e2e53f03fd5a32d8e266d8e06" - integrity sha512-D7DbgGFtsqIPIFMPJwCad9Gfi/hC0PWErRRHFnaCWoEDYi5tQUDiJCTmGUbBiLzjqAck4KcXt9Ayj0CNlIrF+w== +"@typescript-eslint/parser@^8.17.0": + version "8.17.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-8.17.0.tgz#2ee972bb12fa69ac625b85813dc8d9a5a053ff52" + integrity sha512-Drp39TXuUlD49F7ilHHCG7TTg8IkA+hxCuULdmzWYICxGXvDXmDmWEjJYZQYgf6l/TFfYNE167m7isnc3xlIEg== dependencies: - "@typescript-eslint/scope-manager" "8.16.0" - "@typescript-eslint/types" "8.16.0" - "@typescript-eslint/typescript-estree" "8.16.0" - "@typescript-eslint/visitor-keys" "8.16.0" + "@typescript-eslint/scope-manager" "8.17.0" + "@typescript-eslint/types" "8.17.0" + "@typescript-eslint/typescript-estree" "8.17.0" + "@typescript-eslint/visitor-keys" "8.17.0" debug "^4.3.4" -"@typescript-eslint/scope-manager@8.16.0": - version "8.16.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-8.16.0.tgz#ebc9a3b399a69a6052f3d88174456dd399ef5905" - integrity sha512-mwsZWubQvBki2t5565uxF0EYvG+FwdFb8bMtDuGQLdCCnGPrDEDvm1gtfynuKlnpzeBRqdFCkMf9jg1fnAK8sg== +"@typescript-eslint/scope-manager@8.17.0": + version "8.17.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-8.17.0.tgz#a3f49bf3d4d27ff8d6b2ea099ba465ef4dbcaa3a" + integrity sha512-/ewp4XjvnxaREtqsZjF4Mfn078RD/9GmiEAtTeLQ7yFdKnqwTOgRMSvFz4et9U5RiJQ15WTGXPLj89zGusvxBg== dependencies: - "@typescript-eslint/types" "8.16.0" - "@typescript-eslint/visitor-keys" "8.16.0" + "@typescript-eslint/types" "8.17.0" + "@typescript-eslint/visitor-keys" "8.17.0" -"@typescript-eslint/type-utils@8.16.0": - version "8.16.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-8.16.0.tgz#585388735f7ac390f07c885845c3d185d1b64740" - integrity sha512-IqZHGG+g1XCWX9NyqnI/0CX5LL8/18awQqmkZSl2ynn8F76j579dByc0jhfVSnSnhf7zv76mKBQv9HQFKvDCgg== +"@typescript-eslint/type-utils@8.17.0": + version "8.17.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-8.17.0.tgz#d326569f498cdd0edf58d5bb6030b4ad914e63d3" + integrity sha512-q38llWJYPd63rRnJ6wY/ZQqIzPrBCkPdpIsaCfkR3Q4t3p6sb422zougfad4TFW9+ElIFLVDzWGiGAfbb/v2qw== dependencies: - "@typescript-eslint/typescript-estree" "8.16.0" - "@typescript-eslint/utils" "8.16.0" + "@typescript-eslint/typescript-estree" "8.17.0" + "@typescript-eslint/utils" "8.17.0" debug "^4.3.4" ts-api-utils "^1.3.0" -"@typescript-eslint/types@8.16.0": - version "8.16.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-8.16.0.tgz#49c92ae1b57942458ab83d9ec7ccab3005e64737" - integrity sha512-NzrHj6thBAOSE4d9bsuRNMvk+BvaQvmY4dDglgkgGC0EW/tB3Kelnp3tAKH87GEwzoxgeQn9fNGRyFJM/xd+GQ== +"@typescript-eslint/types@8.17.0": + version "8.17.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-8.17.0.tgz#ef84c709ef8324e766878834970bea9a7e3b72cf" + integrity sha512-gY2TVzeve3z6crqh2Ic7Cr+CAv6pfb0Egee7J5UAVWCpVvDI/F71wNfolIim4FE6hT15EbpZFVUj9j5i38jYXA== -"@typescript-eslint/typescript-estree@8.16.0": - version "8.16.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-8.16.0.tgz#9d741e56e5b13469b5190e763432ce5551a9300c" - integrity sha512-E2+9IzzXMc1iaBy9zmo+UYvluE3TW7bCGWSF41hVWUE01o8nzr1rvOQYSxelxr6StUvRcTMe633eY8mXASMaNw== +"@typescript-eslint/typescript-estree@8.17.0": + version "8.17.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-8.17.0.tgz#40b5903bc929b1e8dd9c77db3cb52cfb199a2a34" + integrity sha512-JqkOopc1nRKZpX+opvKqnM3XUlM7LpFMD0lYxTqOTKQfCWAmxw45e3qlOCsEqEB2yuacujivudOFpCnqkBDNMw== dependencies: - "@typescript-eslint/types" "8.16.0" - "@typescript-eslint/visitor-keys" "8.16.0" + "@typescript-eslint/types" "8.17.0" + "@typescript-eslint/visitor-keys" "8.17.0" debug "^4.3.4" fast-glob "^3.3.2" is-glob "^4.0.3" @@ -381,22 +381,22 @@ semver "^7.6.0" ts-api-utils "^1.3.0" -"@typescript-eslint/utils@8.16.0": - version "8.16.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-8.16.0.tgz#c71264c437157feaa97842809836254a6fc833c3" - integrity sha512-C1zRy/mOL8Pj157GiX4kaw7iyRLKfJXBR3L82hk5kS/GyHcOFmy4YUq/zfZti72I9wnuQtA/+xzft4wCC8PJdA== +"@typescript-eslint/utils@8.17.0": + version "8.17.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-8.17.0.tgz#41c05105a2b6ab7592f513d2eeb2c2c0236d8908" + integrity sha512-bQC8BnEkxqG8HBGKwG9wXlZqg37RKSMY7v/X8VEWD8JG2JuTHuNK0VFvMPMUKQcbk6B+tf05k+4AShAEtCtJ/w== dependencies: "@eslint-community/eslint-utils" "^4.4.0" - "@typescript-eslint/scope-manager" "8.16.0" - "@typescript-eslint/types" "8.16.0" - "@typescript-eslint/typescript-estree" "8.16.0" + "@typescript-eslint/scope-manager" "8.17.0" + "@typescript-eslint/types" "8.17.0" + "@typescript-eslint/typescript-estree" "8.17.0" -"@typescript-eslint/visitor-keys@8.16.0": - version "8.16.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-8.16.0.tgz#d5086afc060b01ff7a4ecab8d49d13d5a7b07705" - integrity sha512-pq19gbaMOmFE3CbL0ZB8J8BFCo2ckfHBfaIsaOZgBIF4EoISJIdLX5xRhd0FGB0LlHReNRuzoJoMGpTjq8F2CQ== +"@typescript-eslint/visitor-keys@8.17.0": + version "8.17.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-8.17.0.tgz#4dbcd0e28b9bf951f4293805bf34f98df45e1aa8" + integrity sha512-1Hm7THLpO6ww5QU6H/Qp+AusUUl+z/CAm3cNZZ0jQvon9yicgO7Rwd+/WWRpMKLYV6p2UvdbR27c86rzCPpreg== dependencies: - "@typescript-eslint/types" "8.16.0" + "@typescript-eslint/types" "8.17.0" eslint-visitor-keys "^4.2.0" "@ungap/structured-clone@^1.2.0":