diff --git a/.eslintignore b/.eslintignore new file mode 100644 index 0000000..fcef398 --- /dev/null +++ b/.eslintignore @@ -0,0 +1 @@ +amplify-codegen-temp\models\models \ No newline at end of file diff --git a/.github/workflows/back-sync.yml b/.github/workflows/back-sync.yml new file mode 100644 index 0000000..11aaf66 --- /dev/null +++ b/.github/workflows/back-sync.yml @@ -0,0 +1,25 @@ +name: Back Sync Master to Develop + +on: + push: + branches: + - master + +jobs: + back-sync: + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@v3 + with: + fetch-depth: 0 + + - name: Sync Master to Develop + uses: tretuna/sync-branches@main + with: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + FROM_BRANCH: master + TO_BRANCH: develop + PULL_REQUEST_TITLE: "Auto Sync: master → develop" + PULL_REQUEST_BODY: "This pull request was created automatically to back-sync master to develop." + PULL_REQUEST_AUTO_MERGE_METHOD: merge diff --git a/CHANGELOG.md b/CHANGELOG.md index 8aad02e..ba7300e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,7 +4,30 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). -## [UNRELEASED] - 2023-##-## +## [UNRELEASED] - 2025-##-## + +## [1.21.4] - 2025-03-28 +### Fixed +- algolia updated upon show changes +- show metadata updater adheres to AWS network request concurrency limit +- OMDB API client handles invalid IMDB ratings + +### Changed +- lambda func node versions upgraded (v14 --> v18) + +### Removed +- obsolete algolia graphql transformer + +## [1.21.3] - 2024-01-28 +### Fixed +- plex svg width attribute using valid syntax (auto --> 100%) + +## [1.21.2] - 2024-01-28 +### Changed +- graphql getUser query watchlist items limit increased (100 --> 1000) + +### Fixed +- deprecated default import of aws-amplify replaced with named import ## [1.21.1] - 2023-10-17 ### Fixed diff --git a/amplify/backend/api/universalratings/schema.graphql b/amplify/backend/api/universalratings/schema.graphql index 871bfeb..570847d 100644 --- a/amplify/backend/api/universalratings/schema.graphql +++ b/amplify/backend/api/universalratings/schema.graphql @@ -25,7 +25,7 @@ type User @model color: String! themePref: String plexSearchEnabled: Boolean - watchlist: [WatchlistItem] @connection(fields: ["id"]) + watchlist: [WatchlistItem] @connection(fields: ["id"], limit: 1000) } type Review @model @@ -57,7 +57,6 @@ type Show @model { allow: private, operations: [update] }, { allow: owner, identityClaim: "sub", operations: [create, delete] } ]) - @algolia(fields: { include: ["tmdbId", "title", "type", "releaseDate", "source"] }) @key(fields: ["id"]) @key(name: "recentlyRated", fields: ["source", "createdAt"], queryField: "recentlyRated") @key(name: "showsByType", fields: ["type", "createdAt"], queryField: "showsByType") { diff --git a/amplify/backend/api/universalratings/transform.conf.json b/amplify/backend/api/universalratings/transform.conf.json index 478a7de..f4bae60 100644 --- a/amplify/backend/api/universalratings/transform.conf.json +++ b/amplify/backend/api/universalratings/transform.conf.json @@ -1,7 +1,5 @@ { "Version": 5, "ElasticsearchWarning": true, - "transformers": [ - "graphql-algolia-transformer" - ] + "transformers": [] } \ No newline at end of file diff --git a/amplify/backend/backend-config.json b/amplify/backend/backend-config.json index daa49ef..377f539 100644 --- a/amplify/backend/backend-config.json +++ b/amplify/backend/backend-config.json @@ -66,6 +66,20 @@ "providerPlugin": "awscloudformation", "service": "Lambda" }, + "ShowChangedAlgoliaUpdater": { + "build": true, + "dependsOn": [ + { + "attributes": [ + "GraphQLAPIIdOutput" + ], + "category": "api", + "resourceName": "universalratings" + } + ], + "providerPlugin": "awscloudformation", + "service": "Lambda" + }, "UpdateShowsMetadata": { "build": true, "dependsOn": [ @@ -111,6 +125,54 @@ } ] }, + "AMPLIFY_function_ShowChangedAlgoliaUpdater_algoliaApiKey": { + "usedBy": [ + { + "category": "function", + "resourceName": "ShowChangedAlgoliaUpdater" + } + ] + }, + "AMPLIFY_function_ShowChangedAlgoliaUpdater_algoliaAppId": { + "usedBy": [ + { + "category": "function", + "resourceName": "ShowChangedAlgoliaUpdater" + } + ] + }, + "AMPLIFY_function_ShowChangedAlgoliaUpdater_deploymentBucketName": { + "usedBy": [ + { + "category": "function", + "resourceName": "ShowChangedAlgoliaUpdater" + } + ] + }, + "AMPLIFY_function_ShowChangedAlgoliaUpdater_s3Key": { + "usedBy": [ + { + "category": "function", + "resourceName": "ShowChangedAlgoliaUpdater" + } + ] + }, + "AMPLIFY_function_ShowChangedAlgoliaUpdater_secretsPathAmplifyAppId": { + "usedBy": [ + { + "category": "function", + "resourceName": "ShowChangedAlgoliaUpdater" + } + ] + }, + "AMPLIFY_function_UpdateShowsMetadata_awsNodejsConnectionReuseEnabled": { + "usedBy": [ + { + "category": "function", + "resourceName": "UpdateShowsMetadata" + } + ] + }, "AMPLIFY_function_UpdateShowsMetadata_deploymentBucketName": { "usedBy": [ { diff --git a/amplify/backend/function/ShowAddedDiscordWebhookPublisher/ShowAddedDiscordWebhookPublisher-cloudformation-template.json b/amplify/backend/function/ShowAddedDiscordWebhookPublisher/ShowAddedDiscordWebhookPublisher-cloudformation-template.json index 8d84eb3..56eceec 100644 --- a/amplify/backend/function/ShowAddedDiscordWebhookPublisher/ShowAddedDiscordWebhookPublisher-cloudformation-template.json +++ b/amplify/backend/function/ShowAddedDiscordWebhookPublisher/ShowAddedDiscordWebhookPublisher-cloudformation-template.json @@ -92,7 +92,7 @@ "Arn" ] }, - "Runtime": "nodejs14.x", + "Runtime": "nodejs18.x", "Layers": [], "Timeout": 25 } diff --git a/amplify/backend/function/ShowChangedAlgoliaUpdater/ShowChangedAlgoliaUpdater-cloudformation-template.json b/amplify/backend/function/ShowChangedAlgoliaUpdater/ShowChangedAlgoliaUpdater-cloudformation-template.json new file mode 100644 index 0000000..0b51959 --- /dev/null +++ b/amplify/backend/function/ShowChangedAlgoliaUpdater/ShowChangedAlgoliaUpdater-cloudformation-template.json @@ -0,0 +1,378 @@ +{ + "AWSTemplateFormatVersion": "2010-09-09", + "Description": "{\"createdOn\":\"Windows\",\"createdBy\":\"Amplify\",\"createdWith\":\"12.12.6\",\"stackType\":\"function-Lambda\",\"metadata\":{}}", + "Parameters": { + "CloudWatchRule": { + "Type": "String", + "Default": "NONE", + "Description": " Schedule Expression" + }, + "deploymentBucketName": { + "Type": "String" + }, + "env": { + "Type": "String" + }, + "s3Key": { + "Type": "String" + }, + "algoliaAppId": { + "Type": "String" + }, + "algoliaApiKey": { + "Type": "String" + }, + "apiuniversalratingsGraphQLAPIIdOutput": { + "Type": "String", + "Default": "apiuniversalratingsGraphQLAPIIdOutput" + }, + "secretsPathAmplifyAppId": { + "Type": "String" + } + }, + "Conditions": { + "ShouldNotCreateEnvResources": { + "Fn::Equals": [ + { + "Ref": "env" + }, + "NONE" + ] + } + }, + "Resources": { + "LambdaFunction": { + "Type": "AWS::Lambda::Function", + "Metadata": { + "aws:asset:path": "./src", + "aws:asset:property": "Code" + }, + "Properties": { + "Code": { + "S3Bucket": { + "Ref": "deploymentBucketName" + }, + "S3Key": { + "Ref": "s3Key" + } + }, + "Handler": "index.handler", + "FunctionName": { + "Fn::If": [ + "ShouldNotCreateEnvResources", + "ShowChangedAlgoliaUpdater", + { + "Fn::Join": [ + "", + [ + "ShowChangedAlgoliaUpdater", + "-", + { + "Ref": "env" + } + ] + ] + } + ] + }, + "Environment": { + "Variables": { + "ENV": { + "Ref": "env" + }, + "REGION": { + "Ref": "AWS::Region" + }, + "API_UNIVERSALRATINGS_SHOWTABLE_NAME": { + "Fn::ImportValue": { + "Fn::Sub": "${apiuniversalratingsGraphQLAPIIdOutput}:GetAtt:ShowTable:Name" + } + }, + "API_UNIVERSALRATINGS_SHOWTABLE_ARN": { + "Fn::Join": [ + "", + [ + "arn:aws:dynamodb:", + { + "Ref": "AWS::Region" + }, + ":", + { + "Ref": "AWS::AccountId" + }, + ":table/", + { + "Fn::ImportValue": { + "Fn::Sub": "${apiuniversalratingsGraphQLAPIIdOutput}:GetAtt:ShowTable:Name" + } + } + ] + ] + }, + "API_UNIVERSALRATINGS_GRAPHQLAPIIDOUTPUT": { + "Ref": "apiuniversalratingsGraphQLAPIIdOutput" + }, + "ALGOLIA_APP_ID": { + "Fn::Join": [ + "", + [ + { + "Fn::Sub": [ + "/amplify/${appId}/${env}/AMPLIFY_${functionName}_", + { + "appId": { + "Ref": "secretsPathAmplifyAppId" + }, + "env": { + "Ref": "env" + }, + "functionName": "ShowChangedAlgoliaUpdater" + } + ] + }, + "ALGOLIA_APP_ID" + ] + ] + }, + "ALGOLIA_API_KEY": { + "Fn::Join": [ + "", + [ + { + "Fn::Sub": [ + "/amplify/${appId}/${env}/AMPLIFY_${functionName}_", + { + "appId": { + "Ref": "secretsPathAmplifyAppId" + }, + "env": { + "Ref": "env" + }, + "functionName": "ShowChangedAlgoliaUpdater" + } + ] + }, + "ALGOLIA_API_KEY" + ] + ] + } + } + }, + "Role": { + "Fn::GetAtt": [ + "LambdaExecutionRole", + "Arn" + ] + }, + "Runtime": "nodejs18.x", + "Layers": [], + "Timeout": 25 + } + }, + "LambdaExecutionRole": { + "Type": "AWS::IAM::Role", + "Properties": { + "RoleName": { + "Fn::If": [ + "ShouldNotCreateEnvResources", + "universalratingsLambdaRole79b7a93e", + { + "Fn::Join": [ + "", + [ + "universalratingsLambdaRole79b7a93e", + "-", + { + "Ref": "env" + } + ] + ] + } + ] + }, + "AssumeRolePolicyDocument": { + "Version": "2012-10-17", + "Statement": [ + { + "Effect": "Allow", + "Principal": { + "Service": [ + "lambda.amazonaws.com" + ] + }, + "Action": [ + "sts:AssumeRole" + ] + } + ] + } + } + }, + "lambdaexecutionpolicy": { + "DependsOn": [ + "LambdaExecutionRole" + ], + "Type": "AWS::IAM::Policy", + "Properties": { + "PolicyName": "lambda-execution-policy", + "Roles": [ + { + "Ref": "LambdaExecutionRole" + } + ], + "PolicyDocument": { + "Version": "2012-10-17", + "Statement": [ + { + "Effect": "Allow", + "Action": [ + "logs:CreateLogGroup", + "logs:CreateLogStream", + "logs:PutLogEvents" + ], + "Resource": { + "Fn::Sub": [ + "arn:aws:logs:${region}:${account}:log-group:/aws/lambda/${lambda}:log-stream:*", + { + "region": { + "Ref": "AWS::Region" + }, + "account": { + "Ref": "AWS::AccountId" + }, + "lambda": { + "Ref": "LambdaFunction" + } + } + ] + } + } + ] + } + } + }, + "AmplifyResourcesPolicy": { + "DependsOn": [ + "LambdaExecutionRole" + ], + "Type": "AWS::IAM::Policy", + "Properties": { + "PolicyName": "amplify-lambda-execution-policy", + "Roles": [ + { + "Ref": "LambdaExecutionRole" + } + ], + "PolicyDocument": { + "Version": "2012-10-17", + "Statement": [ + { + "Effect": "Allow", + "Action": [ + "dynamodb:DescribeStream", + "dynamodb:GetRecords", + "dynamodb:GetShardIterator", + "dynamodb:ListStreams" + ], + "Resource": { + "Fn::ImportValue": { + "Fn::Sub": "${apiuniversalratingsGraphQLAPIIdOutput}:GetAtt:ShowTable:StreamArn" + } + } + } + ] + } + } + }, + "AmplifyFunctionSecretsPolicy": { + "Type": "AWS::IAM::Policy", + "Properties": { + "PolicyName": "amplify-function-secrets-policy", + "Roles": [ + { + "Ref": "LambdaExecutionRole" + } + ], + "PolicyDocument": { + "Version": "2012-10-17", + "Statement": [ + { + "Effect": "Allow", + "Action": [ + "ssm:GetParameter", + "ssm:GetParameters" + ], + "Resource": { + "Fn::Join": [ + "", + [ + "arn:aws:ssm:", + { + "Ref": "AWS::Region" + }, + ":", + { + "Ref": "AWS::AccountId" + }, + ":parameter", + { + "Fn::Sub": [ + "/amplify/${appId}/${env}/AMPLIFY_${functionName}_", + { + "appId": { + "Ref": "secretsPathAmplifyAppId" + }, + "env": { + "Ref": "env" + }, + "functionName": "ShowChangedAlgoliaUpdater" + } + ] + }, + "*" + ] + ] + } + } + ] + } + }, + "DependsOn": [ + "LambdaExecutionRole" + ] + } + }, + "Outputs": { + "Name": { + "Value": { + "Ref": "LambdaFunction" + } + }, + "Arn": { + "Value": { + "Fn::GetAtt": [ + "LambdaFunction", + "Arn" + ] + } + }, + "Region": { + "Value": { + "Ref": "AWS::Region" + } + }, + "LambdaExecutionRole": { + "Value": { + "Ref": "LambdaExecutionRole" + } + }, + "LambdaExecutionRoleArn": { + "Value": { + "Fn::GetAtt": [ + "LambdaExecutionRole", + "Arn" + ] + } + } + } +} \ No newline at end of file diff --git a/amplify/backend/function/ShowChangedAlgoliaUpdater/amplify.state b/amplify/backend/function/ShowChangedAlgoliaUpdater/amplify.state new file mode 100644 index 0000000..c68fd54 --- /dev/null +++ b/amplify/backend/function/ShowChangedAlgoliaUpdater/amplify.state @@ -0,0 +1,9 @@ +{ + "pluginId": "amplify-nodejs-function-runtime-provider", + "functionRuntime": "nodejs", + "useLegacyBuild": true, + "defaultEditorFile": "src\\index.js", + "scripts": { + "build": "yarn --no-bin-links --production" + } +} \ No newline at end of file diff --git a/amplify/backend/function/ShowChangedAlgoliaUpdater/custom-policies.json b/amplify/backend/function/ShowChangedAlgoliaUpdater/custom-policies.json new file mode 100644 index 0000000..528c94f --- /dev/null +++ b/amplify/backend/function/ShowChangedAlgoliaUpdater/custom-policies.json @@ -0,0 +1,6 @@ +[ + { + "Action": [], + "Resource": [] + } +] \ No newline at end of file diff --git a/amplify/backend/function/ShowChangedAlgoliaUpdater/function-parameters.json b/amplify/backend/function/ShowChangedAlgoliaUpdater/function-parameters.json new file mode 100644 index 0000000..a2bf486 --- /dev/null +++ b/amplify/backend/function/ShowChangedAlgoliaUpdater/function-parameters.json @@ -0,0 +1,24 @@ +{ + "permissions": { + "storage": { + "Show:@model(appsync)": [ + "read" + ] + } + }, + "lambdaLayers": [], + "environmentVariableList": [ + { + "cloudFormationParameterName": "algoliaAppId", + "environmentVariableName": "ALGOLIA_APP_ID" + }, + { + "cloudFormationParameterName": "algoliaApiKey", + "environmentVariableName": "ALGOLIA_API_KEY" + } + ], + "secretNames": [ + "ALGOLIA_API_KEY", + "ALGOLIA_APP_ID" + ] +} \ No newline at end of file diff --git a/amplify/backend/function/ShowChangedAlgoliaUpdater/parameters.json b/amplify/backend/function/ShowChangedAlgoliaUpdater/parameters.json new file mode 100644 index 0000000..9e26dfe --- /dev/null +++ b/amplify/backend/function/ShowChangedAlgoliaUpdater/parameters.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/amplify/backend/function/ShowChangedAlgoliaUpdater/src/event.json b/amplify/backend/function/ShowChangedAlgoliaUpdater/src/event.json new file mode 100644 index 0000000..fd2722e --- /dev/null +++ b/amplify/backend/function/ShowChangedAlgoliaUpdater/src/event.json @@ -0,0 +1,5 @@ +{ + "key1": "value1", + "key2": "value2", + "key3": "value3" +} diff --git a/amplify/backend/function/ShowChangedAlgoliaUpdater/src/index.js b/amplify/backend/function/ShowChangedAlgoliaUpdater/src/index.js new file mode 100644 index 0000000..21ade03 --- /dev/null +++ b/amplify/backend/function/ShowChangedAlgoliaUpdater/src/index.js @@ -0,0 +1,88 @@ +/* eslint-disable no-await-in-loop */ +import algoliasearch from 'algoliasearch'; + +const algolia = algoliasearch('QVUO52LVSK', process.env.ALGOLIA_API_KEY); +const index = algolia.initIndex('show'); + +/* +Use the following code to retrieve configured secrets from SSM: + +const aws = require('aws-sdk'); + +const { Parameters } = await (new aws.SSM()) + .getParameters({ + Names: ["ALGOLIA_API_KEY","ALGOLIA_APP_ID"].map(secretName => process.env[secretName]), + WithDecryption: true, + }) + .promise(); + +Parameters will be of the form { Name: 'secretName', Value: 'secretValue', ... }[] +*/ +/* Amplify Params - DO NOT EDIT + ENV + REGION + API_UNIVERSALRATINGS_SHOWTABLE_NAME + API_UNIVERSALRATINGS_SHOWTABLE_ARN + API_UNIVERSALRATINGS_GRAPHQLAPIIDOUTPUT + ALGOLIA_APP_ID + ALGOLIA_API_KEY +Amplify Params - DO NOT EDIT */ + +async function handleShowChange(event) { + console.log(`EVENT: ${JSON.stringify(event)}`); + + for (const record of event.Records) { + const operation = record.eventName; + const showId = record.dynamodb.Keys.id.S; + + console.log(`${operation} for showId: ${showId}`); + + switch (operation) { + case 'INSERT': + const showData = record.dynamodb.NewImage; + const algoliaObject = { + objectID: showId, + tmdbId: showData.tmdbId.S, + title: showData.title.S, + type: showData.type.S, + releaseDate: showData.releaseDate.S, + source: showData.source.S + }; + + await index.saveObject(algoliaObject); + break; + case 'REMOVE': + await index.deleteObject(showId); + break; + case 'MODIFY': + const updatedShowData = record.dynamodb.NewImage; + const updatedAlgoliaObject = { + objectID: showId, + tmdbId: updatedShowData.tmdbId.S, + title: updatedShowData.title.S, + type: updatedShowData.type.S, + releaseDate: updatedShowData.releaseDate.S, + source: updatedShowData.source.S + }; + + await index.partialUpdateObject(updatedAlgoliaObject); + break; + default: + break; + } + } + + return { + statusCode: 200, + body: 'Function executed successfully' + }; +} + +export const handler = async (event) => { + try { + await handleShowChange(event); + } catch (err) { + console.error('Failed to handle change to show.', err); + throw err; + } +}; \ No newline at end of file diff --git a/amplify/backend/function/ShowChangedAlgoliaUpdater/src/package.json b/amplify/backend/function/ShowChangedAlgoliaUpdater/src/package.json new file mode 100644 index 0000000..06b5316 --- /dev/null +++ b/amplify/backend/function/ShowChangedAlgoliaUpdater/src/package.json @@ -0,0 +1,15 @@ +{ + "name": "showchangedalgoliaupdater", + "version": "2.0.0", + "description": "Lambda function generated by Amplify", + "main": "index.js", + "type": "module", + "license": "Apache-2.0", + "devDependencies": { + "@types/aws-lambda": "^8.10.92", + "aws-sdk": "^2.1553.0" + }, + "dependencies": { + "algoliasearch": "^4.22.1" + } +} diff --git a/amplify/backend/function/ShowChangedAlgoliaUpdater/src/yarn.lock b/amplify/backend/function/ShowChangedAlgoliaUpdater/src/yarn.lock new file mode 100644 index 0000000..6eeb9c5 --- /dev/null +++ b/amplify/backend/function/ShowChangedAlgoliaUpdater/src/yarn.lock @@ -0,0 +1,422 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + +"@algolia/cache-browser-local-storage@4.24.0": + version "4.24.0" + resolved "https://registry.yarnpkg.com/@algolia/cache-browser-local-storage/-/cache-browser-local-storage-4.24.0.tgz#97bc6d067a9fd932b9c922faa6b7fd6e546e1348" + integrity sha512-t63W9BnoXVrGy9iYHBgObNXqYXM3tYXCjDSHeNwnsc324r4o5UiVKUiAB4THQ5z9U5hTj6qUvwg/Ez43ZD85ww== + dependencies: + "@algolia/cache-common" "4.24.0" + +"@algolia/cache-common@4.24.0": + version "4.24.0" + resolved "https://registry.yarnpkg.com/@algolia/cache-common/-/cache-common-4.24.0.tgz#81a8d3a82ceb75302abb9b150a52eba9960c9744" + integrity sha512-emi+v+DmVLpMGhp0V9q9h5CdkURsNmFC+cOS6uK9ndeJm9J4TiqSvPYVu+THUP8P/S08rxf5x2P+p3CfID0Y4g== + +"@algolia/cache-in-memory@4.24.0": + version "4.24.0" + resolved "https://registry.yarnpkg.com/@algolia/cache-in-memory/-/cache-in-memory-4.24.0.tgz#ffcf8872f3a10cb85c4f4641bdffd307933a6e44" + integrity sha512-gDrt2so19jW26jY3/MkFg5mEypFIPbPoXsQGQWAi6TrCPsNOSEYepBMPlucqWigsmEy/prp5ug2jy/N3PVG/8w== + dependencies: + "@algolia/cache-common" "4.24.0" + +"@algolia/client-account@4.24.0": + version "4.24.0" + resolved "https://registry.yarnpkg.com/@algolia/client-account/-/client-account-4.24.0.tgz#eba7a921d828e7c8c40a32d4add21206c7fe12f1" + integrity sha512-adcvyJ3KjPZFDybxlqnf+5KgxJtBjwTPTeyG2aOyoJvx0Y8dUQAEOEVOJ/GBxX0WWNbmaSrhDURMhc+QeevDsA== + dependencies: + "@algolia/client-common" "4.24.0" + "@algolia/client-search" "4.24.0" + "@algolia/transporter" "4.24.0" + +"@algolia/client-analytics@4.24.0": + version "4.24.0" + resolved "https://registry.yarnpkg.com/@algolia/client-analytics/-/client-analytics-4.24.0.tgz#9d2576c46a9093a14e668833c505ea697a1a3e30" + integrity sha512-y8jOZt1OjwWU4N2qr8G4AxXAzaa8DBvyHTWlHzX/7Me1LX8OayfgHexqrsL4vSBcoMmVw2XnVW9MhL+Y2ZDJXg== + dependencies: + "@algolia/client-common" "4.24.0" + "@algolia/client-search" "4.24.0" + "@algolia/requester-common" "4.24.0" + "@algolia/transporter" "4.24.0" + +"@algolia/client-common@4.24.0": + version "4.24.0" + resolved "https://registry.yarnpkg.com/@algolia/client-common/-/client-common-4.24.0.tgz#77c46eee42b9444a1d1c1583a83f7df4398a649d" + integrity sha512-bc2ROsNL6w6rqpl5jj/UywlIYC21TwSSoFHKl01lYirGMW+9Eek6r02Tocg4gZ8HAw3iBvu6XQiM3BEbmEMoiA== + dependencies: + "@algolia/requester-common" "4.24.0" + "@algolia/transporter" "4.24.0" + +"@algolia/client-personalization@4.24.0": + version "4.24.0" + resolved "https://registry.yarnpkg.com/@algolia/client-personalization/-/client-personalization-4.24.0.tgz#8b47789fb1cb0f8efbea0f79295b7c5a3850f6ae" + integrity sha512-l5FRFm/yngztweU0HdUzz1rC4yoWCFo3IF+dVIVTfEPg906eZg5BOd1k0K6rZx5JzyyoP4LdmOikfkfGsKVE9w== + dependencies: + "@algolia/client-common" "4.24.0" + "@algolia/requester-common" "4.24.0" + "@algolia/transporter" "4.24.0" + +"@algolia/client-search@4.24.0": + version "4.24.0" + resolved "https://registry.yarnpkg.com/@algolia/client-search/-/client-search-4.24.0.tgz#75e6c02d33ef3e0f34afd9962c085b856fc4a55f" + integrity sha512-uRW6EpNapmLAD0mW47OXqTP8eiIx5F6qN9/x/7HHO6owL3N1IXqydGwW5nhDFBrV+ldouro2W1VX3XlcUXEFCA== + dependencies: + "@algolia/client-common" "4.24.0" + "@algolia/requester-common" "4.24.0" + "@algolia/transporter" "4.24.0" + +"@algolia/logger-common@4.24.0": + version "4.24.0" + resolved "https://registry.yarnpkg.com/@algolia/logger-common/-/logger-common-4.24.0.tgz#28d439976019ec0a46ba7a1a739ef493d4ef8123" + integrity sha512-LLUNjkahj9KtKYrQhFKCzMx0BY3RnNP4FEtO+sBybCjJ73E8jNdaKJ/Dd8A/VA4imVHP5tADZ8pn5B8Ga/wTMA== + +"@algolia/logger-console@4.24.0": + version "4.24.0" + resolved "https://registry.yarnpkg.com/@algolia/logger-console/-/logger-console-4.24.0.tgz#c6ff486036cd90b81d07a95aaba04461da7e1c65" + integrity sha512-X4C8IoHgHfiUROfoRCV+lzSy+LHMgkoEEU1BbKcsfnV0i0S20zyy0NLww9dwVHUWNfPPxdMU+/wKmLGYf96yTg== + dependencies: + "@algolia/logger-common" "4.24.0" + +"@algolia/recommend@4.24.0": + version "4.24.0" + resolved "https://registry.yarnpkg.com/@algolia/recommend/-/recommend-4.24.0.tgz#8a3f78aea471ee0a4836b78fd2aad4e9abcaaf34" + integrity sha512-P9kcgerfVBpfYHDfVZDvvdJv0lEoCvzNlOy2nykyt5bK8TyieYyiD0lguIJdRZZYGre03WIAFf14pgE+V+IBlw== + dependencies: + "@algolia/cache-browser-local-storage" "4.24.0" + "@algolia/cache-common" "4.24.0" + "@algolia/cache-in-memory" "4.24.0" + "@algolia/client-common" "4.24.0" + "@algolia/client-search" "4.24.0" + "@algolia/logger-common" "4.24.0" + "@algolia/logger-console" "4.24.0" + "@algolia/requester-browser-xhr" "4.24.0" + "@algolia/requester-common" "4.24.0" + "@algolia/requester-node-http" "4.24.0" + "@algolia/transporter" "4.24.0" + +"@algolia/requester-browser-xhr@4.24.0": + version "4.24.0" + resolved "https://registry.yarnpkg.com/@algolia/requester-browser-xhr/-/requester-browser-xhr-4.24.0.tgz#313c5edab4ed73a052e75803855833b62dd19c16" + integrity sha512-Z2NxZMb6+nVXSjF13YpjYTdvV3032YTBSGm2vnYvYPA6mMxzM3v5rsCiSspndn9rzIW4Qp1lPHBvuoKJV6jnAA== + dependencies: + "@algolia/requester-common" "4.24.0" + +"@algolia/requester-common@4.24.0": + version "4.24.0" + resolved "https://registry.yarnpkg.com/@algolia/requester-common/-/requester-common-4.24.0.tgz#1c60c198031f48fcdb9e34c4057a3ea987b9a436" + integrity sha512-k3CXJ2OVnvgE3HMwcojpvY6d9kgKMPRxs/kVohrwF5WMr2fnqojnycZkxPoEg+bXm8fi5BBfFmOqgYztRtHsQA== + +"@algolia/requester-node-http@4.24.0": + version "4.24.0" + resolved "https://registry.yarnpkg.com/@algolia/requester-node-http/-/requester-node-http-4.24.0.tgz#4461593714031d02aa7da221c49df675212f482f" + integrity sha512-JF18yTjNOVYvU/L3UosRcvbPMGT9B+/GQWNWnenIImglzNVGpyzChkXLnrSf6uxwVNO6ESGu6oN8MqcGQcjQJw== + dependencies: + "@algolia/requester-common" "4.24.0" + +"@algolia/transporter@4.24.0": + version "4.24.0" + resolved "https://registry.yarnpkg.com/@algolia/transporter/-/transporter-4.24.0.tgz#226bb1f8af62430374c1972b2e5c8580ab275102" + integrity sha512-86nI7w6NzWxd1Zp9q3413dRshDqAzSbsQjhcDhPIatEFiZrL1/TjnHL8S7jVKFePlIMzDsZWXAXwXzcok9c5oA== + dependencies: + "@algolia/cache-common" "4.24.0" + "@algolia/logger-common" "4.24.0" + "@algolia/requester-common" "4.24.0" + +"@types/aws-lambda@^8.10.92": + version "8.10.145" + resolved "https://registry.yarnpkg.com/@types/aws-lambda/-/aws-lambda-8.10.145.tgz#b2d31a987f4888e5553ff1819f57cafa475594d9" + integrity sha512-dtByW6WiFk5W5Jfgz1VM+YPA21xMXTuSFoLYIDY0L44jDLLflVPtZkYuu3/YxpGcvjzKFBZLU+GyKjR0HOYtyw== + +algoliasearch@^4.22.1: + version "4.24.0" + resolved "https://registry.yarnpkg.com/algoliasearch/-/algoliasearch-4.24.0.tgz#b953b3e2309ef8f25da9de311b95b994ac918275" + integrity sha512-bf0QV/9jVejssFBmz2HQLxUadxk574t4iwjCKp5E7NBzwKkrDEhKPISIIjAU/p6K5qDx3qoeh4+26zWN1jmw3g== + dependencies: + "@algolia/cache-browser-local-storage" "4.24.0" + "@algolia/cache-common" "4.24.0" + "@algolia/cache-in-memory" "4.24.0" + "@algolia/client-account" "4.24.0" + "@algolia/client-analytics" "4.24.0" + "@algolia/client-common" "4.24.0" + "@algolia/client-personalization" "4.24.0" + "@algolia/client-search" "4.24.0" + "@algolia/logger-common" "4.24.0" + "@algolia/logger-console" "4.24.0" + "@algolia/recommend" "4.24.0" + "@algolia/requester-browser-xhr" "4.24.0" + "@algolia/requester-common" "4.24.0" + "@algolia/requester-node-http" "4.24.0" + "@algolia/transporter" "4.24.0" + +available-typed-arrays@^1.0.7: + version "1.0.7" + resolved "https://registry.yarnpkg.com/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz#a5cc375d6a03c2efc87a553f3e0b1522def14846" + integrity sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ== + dependencies: + possible-typed-array-names "^1.0.0" + +aws-sdk@^2.1553.0: + version "2.1691.0" + resolved "https://registry.yarnpkg.com/aws-sdk/-/aws-sdk-2.1691.0.tgz#9d6ccdcbae03c806fc62667b76eb3e33e5294dcc" + integrity sha512-/F2YC+DlsY3UBM2Bdnh5RLHOPNibS/+IcjUuhP8XuctyrN+MlL+fWDAiela32LTDk7hMy4rx8MTgvbJ+0blO5g== + dependencies: + buffer "4.9.2" + events "1.1.1" + ieee754 "1.1.13" + jmespath "0.16.0" + querystring "0.2.0" + sax "1.2.1" + url "0.10.3" + util "^0.12.4" + uuid "8.0.0" + xml2js "0.6.2" + +base64-js@^1.0.2: + version "1.5.1" + resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a" + integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA== + +buffer@4.9.2: + version "4.9.2" + resolved "https://registry.yarnpkg.com/buffer/-/buffer-4.9.2.tgz#230ead344002988644841ab0244af8c44bbe3ef8" + integrity sha512-xq+q3SRMOxGivLhBNaUdC64hDTQwejJ+H0T/NB1XMtTVEwNTrfFF3gAxiyW0Bu/xWEGhjVKgUcMhCrUy2+uCWg== + dependencies: + base64-js "^1.0.2" + ieee754 "^1.1.4" + isarray "^1.0.0" + +call-bind@^1.0.2, call-bind@^1.0.7: + version "1.0.7" + resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.7.tgz#06016599c40c56498c18769d2730be242b6fa3b9" + integrity sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w== + dependencies: + es-define-property "^1.0.0" + es-errors "^1.3.0" + function-bind "^1.1.2" + get-intrinsic "^1.2.4" + set-function-length "^1.2.1" + +define-data-property@^1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/define-data-property/-/define-data-property-1.1.4.tgz#894dc141bb7d3060ae4366f6a0107e68fbe48c5e" + integrity sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A== + dependencies: + es-define-property "^1.0.0" + es-errors "^1.3.0" + gopd "^1.0.1" + +es-define-property@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/es-define-property/-/es-define-property-1.0.0.tgz#c7faefbdff8b2696cf5f46921edfb77cc4ba3845" + integrity sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ== + dependencies: + get-intrinsic "^1.2.4" + +es-errors@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/es-errors/-/es-errors-1.3.0.tgz#05f75a25dab98e4fb1dcd5e1472c0546d5057c8f" + integrity sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw== + +events@1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/events/-/events-1.1.1.tgz#9ebdb7635ad099c70dcc4c2a1f5004288e8bd924" + integrity sha512-kEcvvCBByWXGnZy6JUlgAp2gBIUjfCAV6P6TgT1/aaQKcmuAEC4OZTV1I4EWQLz2gxZw76atuVyvHhTxvi0Flw== + +for-each@^0.3.3: + version "0.3.3" + resolved "https://registry.yarnpkg.com/for-each/-/for-each-0.3.3.tgz#69b447e88a0a5d32c3e7084f3f1710034b21376e" + integrity sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw== + dependencies: + is-callable "^1.1.3" + +function-bind@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.2.tgz#2c02d864d97f3ea6c8830c464cbd11ab6eab7a1c" + integrity sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA== + +get-intrinsic@^1.1.3, get-intrinsic@^1.2.4: + version "1.2.4" + resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.2.4.tgz#e385f5a4b5227d449c3eabbad05494ef0abbeadd" + integrity sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ== + dependencies: + es-errors "^1.3.0" + function-bind "^1.1.2" + has-proto "^1.0.1" + has-symbols "^1.0.3" + hasown "^2.0.0" + +gopd@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/gopd/-/gopd-1.0.1.tgz#29ff76de69dac7489b7c0918a5788e56477c332c" + integrity sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA== + dependencies: + get-intrinsic "^1.1.3" + +has-property-descriptors@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz#963ed7d071dc7bf5f084c5bfbe0d1b6222586854" + integrity sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg== + dependencies: + es-define-property "^1.0.0" + +has-proto@^1.0.1: + version "1.0.3" + resolved "https://registry.yarnpkg.com/has-proto/-/has-proto-1.0.3.tgz#b31ddfe9b0e6e9914536a6ab286426d0214f77fd" + integrity sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q== + +has-symbols@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.3.tgz#bb7b2c4349251dce87b125f7bdf874aa7c8b39f8" + integrity sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A== + +has-tostringtag@^1.0.0, has-tostringtag@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/has-tostringtag/-/has-tostringtag-1.0.2.tgz#2cdc42d40bef2e5b4eeab7c01a73c54ce7ab5abc" + integrity sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw== + dependencies: + has-symbols "^1.0.3" + +hasown@^2.0.0: + version "2.0.2" + resolved "https://registry.yarnpkg.com/hasown/-/hasown-2.0.2.tgz#003eaf91be7adc372e84ec59dc37252cedb80003" + integrity sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ== + dependencies: + function-bind "^1.1.2" + +ieee754@1.1.13: + version "1.1.13" + resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.1.13.tgz#ec168558e95aa181fd87d37f55c32bbcb6708b84" + integrity sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg== + +ieee754@^1.1.4: + version "1.2.1" + resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352" + integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA== + +inherits@^2.0.3: + version "2.0.4" + resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" + integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== + +is-arguments@^1.0.4: + version "1.1.1" + resolved "https://registry.yarnpkg.com/is-arguments/-/is-arguments-1.1.1.tgz#15b3f88fda01f2a97fec84ca761a560f123efa9b" + integrity sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA== + dependencies: + call-bind "^1.0.2" + has-tostringtag "^1.0.0" + +is-callable@^1.1.3: + version "1.2.7" + resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.7.tgz#3bc2a85ea742d9e36205dcacdd72ca1fdc51b055" + integrity sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA== + +is-generator-function@^1.0.7: + version "1.0.10" + resolved "https://registry.yarnpkg.com/is-generator-function/-/is-generator-function-1.0.10.tgz#f1558baf1ac17e0deea7c0415c438351ff2b3c72" + integrity sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A== + dependencies: + has-tostringtag "^1.0.0" + +is-typed-array@^1.1.3: + version "1.1.13" + resolved "https://registry.yarnpkg.com/is-typed-array/-/is-typed-array-1.1.13.tgz#d6c5ca56df62334959322d7d7dd1cca50debe229" + integrity sha512-uZ25/bUAlUY5fR4OKT4rZQEBrzQWYV9ZJYGGsUmEJ6thodVJ1HX64ePQ6Z0qPWP+m+Uq6e9UugrE38jeYsDSMw== + dependencies: + which-typed-array "^1.1.14" + +isarray@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" + integrity sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ== + +jmespath@0.16.0: + version "0.16.0" + resolved "https://registry.yarnpkg.com/jmespath/-/jmespath-0.16.0.tgz#b15b0a85dfd4d930d43e69ed605943c802785076" + integrity sha512-9FzQjJ7MATs1tSpnco1K6ayiYE3figslrXA72G2HQ/n76RzvYlofyi5QM+iX4YRs/pu3yzxlVQSST23+dMDknw== + +possible-typed-array-names@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/possible-typed-array-names/-/possible-typed-array-names-1.0.0.tgz#89bb63c6fada2c3e90adc4a647beeeb39cc7bf8f" + integrity sha512-d7Uw+eZoloe0EHDIYoe+bQ5WXnGMOpmiZFTuMWCwpjzzkL2nTjcKiAk4hh8TjnGye2TwWOk3UXucZ+3rbmBa8Q== + +punycode@1.3.2: + version "1.3.2" + resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.3.2.tgz#9653a036fb7c1ee42342f2325cceefea3926c48d" + integrity sha512-RofWgt/7fL5wP1Y7fxE7/EmTLzQVnB0ycyibJ0OOHIlJqTNzglYFxVwETOcIoJqJmpDXJ9xImDv+Fq34F/d4Dw== + +querystring@0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/querystring/-/querystring-0.2.0.tgz#b209849203bb25df820da756e747005878521620" + integrity sha512-X/xY82scca2tau62i9mDyU9K+I+djTMUsvwf7xnUX5GLvVzgJybOJf4Y6o9Zx3oJK/LSXg5tTZBjwzqVPaPO2g== + +sax@1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.1.tgz#7b8e656190b228e81a66aea748480d828cd2d37a" + integrity sha512-8I2a3LovHTOpm7NV5yOyO8IHqgVsfK4+UuySrXU8YXkSRX7k6hCV9b3HrkKCr3nMpgj+0bmocaJJWpvp1oc7ZA== + +sax@>=0.6.0: + version "1.4.1" + resolved "https://registry.yarnpkg.com/sax/-/sax-1.4.1.tgz#44cc8988377f126304d3b3fc1010c733b929ef0f" + integrity sha512-+aWOz7yVScEGoKNd4PA10LZ8sk0A/z5+nXQG5giUO5rprX9jgYsTdov9qCchZiPIZezbZH+jRut8nPodFAX4Jg== + +set-function-length@^1.2.1: + version "1.2.2" + resolved "https://registry.yarnpkg.com/set-function-length/-/set-function-length-1.2.2.tgz#aac72314198eaed975cf77b2c3b6b880695e5449" + integrity sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg== + dependencies: + define-data-property "^1.1.4" + es-errors "^1.3.0" + function-bind "^1.1.2" + get-intrinsic "^1.2.4" + gopd "^1.0.1" + has-property-descriptors "^1.0.2" + +url@0.10.3: + version "0.10.3" + resolved "https://registry.yarnpkg.com/url/-/url-0.10.3.tgz#021e4d9c7705f21bbf37d03ceb58767402774c64" + integrity sha512-hzSUW2q06EqL1gKM/a+obYHLIO6ct2hwPuviqTTOcfFVc61UbfJ2Q32+uGL/HCPxKqrdGB5QUwIe7UqlDgwsOQ== + dependencies: + punycode "1.3.2" + querystring "0.2.0" + +util@^0.12.4: + version "0.12.5" + resolved "https://registry.yarnpkg.com/util/-/util-0.12.5.tgz#5f17a6059b73db61a875668781a1c2b136bd6fbc" + integrity sha512-kZf/K6hEIrWHI6XqOFUiiMa+79wE/D8Q+NCNAWclkyg3b4d2k7s0QGepNjiABc+aR3N1PAyHL7p6UcLY6LmrnA== + dependencies: + inherits "^2.0.3" + is-arguments "^1.0.4" + is-generator-function "^1.0.7" + is-typed-array "^1.1.3" + which-typed-array "^1.1.2" + +uuid@8.0.0: + version "8.0.0" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.0.0.tgz#bc6ccf91b5ff0ac07bbcdbf1c7c4e150db4dbb6c" + integrity sha512-jOXGuXZAWdsTH7eZLtyXMqUb9EcWMGZNbL9YcGBJl4MH4nrxHmZJhEHvyLFrkxo+28uLb/NYRcStH48fnD0Vzw== + +which-typed-array@^1.1.14, which-typed-array@^1.1.2: + version "1.1.15" + resolved "https://registry.yarnpkg.com/which-typed-array/-/which-typed-array-1.1.15.tgz#264859e9b11a649b388bfaaf4f767df1f779b38d" + integrity sha512-oV0jmFtUky6CXfkqehVvBP/LSWJ2sy4vWMioiENyJLePrBO/yKyV9OyJySfAKosh+RYkIl5zJCNZ8/4JncrpdA== + dependencies: + available-typed-arrays "^1.0.7" + call-bind "^1.0.7" + for-each "^0.3.3" + gopd "^1.0.1" + has-tostringtag "^1.0.2" + +xml2js@0.6.2: + version "0.6.2" + resolved "https://registry.yarnpkg.com/xml2js/-/xml2js-0.6.2.tgz#dd0b630083aa09c161e25a4d0901e2b2a929b499" + integrity sha512-T4rieHaC1EXcES0Kxxj4JWgaUQHDk+qwHcYOCFHfiwKz7tOVPLq7Hjq9dM1WCMhylqMEfP7hMcOIChvotiZegA== + dependencies: + sax ">=0.6.0" + xmlbuilder "~11.0.0" + +xmlbuilder@~11.0.0: + version "11.0.1" + resolved "https://registry.yarnpkg.com/xmlbuilder/-/xmlbuilder-11.0.1.tgz#be9bae1c8a046e76b31127726347d0ad7002beb3" + integrity sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA== diff --git a/amplify/backend/function/UpdateShowsMetadata/UpdateShowsMetadata-cloudformation-template.json b/amplify/backend/function/UpdateShowsMetadata/UpdateShowsMetadata-cloudformation-template.json index fed9ed8..835ff92 100644 --- a/amplify/backend/function/UpdateShowsMetadata/UpdateShowsMetadata-cloudformation-template.json +++ b/amplify/backend/function/UpdateShowsMetadata/UpdateShowsMetadata-cloudformation-template.json @@ -16,15 +16,18 @@ "s3Key": { "Type": "String" }, + "apiuniversalratingsGraphQLAPIIdOutput": { + "Type": "String", + "Default": "apiuniversalratingsGraphQLAPIIdOutput" + }, "tmdbApiKey": { "Type": "String" }, "omdbApiKey": { "Type": "String" }, - "apiuniversalratingsGraphQLAPIIdOutput": { - "Type": "String", - "Default": "apiuniversalratingsGraphQLAPIIdOutput" + "awsNodejsConnectionReuseEnabled": { + "Type": "String" } }, "Conditions": { @@ -114,6 +117,9 @@ }, "OMDB_API_KEY": { "Ref": "omdbApiKey" + }, + "AWS_NODEJS_CONNECTION_REUSE_ENABLED": { + "Ref": "awsNodejsConnectionReuseEnabled" } } }, @@ -123,7 +129,7 @@ "Arn" ] }, - "Runtime": "nodejs14.x", + "Runtime": "nodejs18.x", "Layers": [], "Timeout": 25 } diff --git a/amplify/backend/function/UpdateShowsMetadata/function-parameters.json b/amplify/backend/function/UpdateShowsMetadata/function-parameters.json index 04b1bd8..0818b45 100644 --- a/amplify/backend/function/UpdateShowsMetadata/function-parameters.json +++ b/amplify/backend/function/UpdateShowsMetadata/function-parameters.json @@ -16,6 +16,10 @@ { "cloudFormationParameterName": "omdbApiKey", "environmentVariableName": "OMDB_API_KEY" + }, + { + "cloudFormationParameterName": "awsNodejsConnectionReuseEnabled", + "environmentVariableName": "AWS_NODEJS_CONNECTION_REUSE_ENABLED" } ] } \ No newline at end of file diff --git a/amplify/backend/function/UpdateShowsMetadata/src/client/OmdbApiClient.mjs b/amplify/backend/function/UpdateShowsMetadata/src/client/OmdbApiClient.mjs index edbce1e..4857f4e 100644 --- a/amplify/backend/function/UpdateShowsMetadata/src/client/OmdbApiClient.mjs +++ b/amplify/backend/function/UpdateShowsMetadata/src/client/OmdbApiClient.mjs @@ -13,9 +13,10 @@ export default class OmdbApiClient { async fetchExternalRatings(imdbId) { const { data: { imdbRating, Ratings } } = await axios.get(`${this.baseUrl}&i=${imdbId}`); + const parsedImdbRating = Number(imdbRating); return { - imdbRating: imdbRating === 'N/A' ? null : Number(imdbRating), + imdbRating: Number.isNaN(parsedImdbRating) ? null : parsedImdbRating, rtRating: this.findRtRating(Ratings), }; } diff --git a/amplify/backend/function/UpdateShowsMetadata/src/index.js b/amplify/backend/function/UpdateShowsMetadata/src/index.js index 9d2336b..e581063 100644 --- a/amplify/backend/function/UpdateShowsMetadata/src/index.js +++ b/amplify/backend/function/UpdateShowsMetadata/src/index.js @@ -10,7 +10,9 @@ Amplify Params - DO NOT EDIT */ import aws from 'aws-sdk'; import { OmdbApiClient, TmdbApiClient, AlgoliaApiClient } from './client/index.mjs'; +import pLimit from 'p-limit'; +const awsConcurrentRequestLimit = 1024; const dynamoDb = new aws.DynamoDB.DocumentClient({ convertEmptyValues: true }); const algoliaApi = new AlgoliaApiClient('QVUO52LVSK', 'ae8c0c082adf1cd9dace13ea68322713'); const omdbApi = new OmdbApiClient(process.env.OMDB_API_KEY); @@ -50,15 +52,19 @@ function buildExpression({ ExpressionAttributeNames }, isConditionExp) { }, ''); } -function buildMetadataUpdateParams(providerResp, ratingResp) { +function buildMetadataUpdateParams(showId, providerResp, ratingResp) { const params = {}; if (providerResp.status === 'fulfilled') { populateProviderParams(providerResp, params); + } else { + console.error(`Failed to get providers of show "${showId}":`, providerResp?.reason?.message); } if (ratingResp.status === 'fulfilled') { populateRatingParams(ratingResp, params); + } else { + console.error(`Failed to get ratings of show "${showId}":`, ratingResp?.reason?.message); } if (!params.ExpressionAttributeNames) { return; } @@ -70,7 +76,7 @@ function buildMetadataUpdateParams(providerResp, ratingResp) { } async function updateShow({ showId, providerResp, ratingResp }) { - const metadataUpdateParams = buildMetadataUpdateParams(providerResp, ratingResp); + const metadataUpdateParams = buildMetadataUpdateParams(showId, providerResp, ratingResp); if (!metadataUpdateParams) { console.error(`Unable to update show "${showId}". Metadata requests failed.`); @@ -89,8 +95,9 @@ async function updateShow({ showId, providerResp, ratingResp }) { try { await dynamoDb.update(params).promise(); + console.log('Succesfully updated show:', showId); } catch (err) { - console.error(`Failed to update show "${showId}": `, err); + console.error(`Failed to update show "${showId}": ${err.statusCode} - ${err.code}: ${err.message}`); } } @@ -123,8 +130,10 @@ async function fetchShows() { */ export const handler = async () => { const shows = await fetchShows(); - const metadataResponses = await Promise.all(shows.map(fetchShowMetadata)); + // use half of overall limit as requests are made to both omdb and tmdb concurrently + const limitConcurrencyToHalfOfMax = pLimit(Math.floor((awsConcurrentRequestLimit / 2) - 10)); + const metadataResponses = await Promise.all(shows.map((show) => limitConcurrencyToHalfOfMax(() => fetchShowMetadata(show)))); + const limitConcurrencyToMax = pLimit(awsConcurrentRequestLimit - 5); - await Promise.all(metadataResponses.map(updateShow)); // todo: batch update - console.log('All shows updated'); + await Promise.all(metadataResponses.map((response) => limitConcurrencyToMax(() => updateShow(response)))); // todo: batch update }; \ No newline at end of file diff --git a/amplify/backend/function/UpdateShowsMetadata/src/package.json b/amplify/backend/function/UpdateShowsMetadata/src/package.json index 5a6cfa5..e4da6d6 100644 --- a/amplify/backend/function/UpdateShowsMetadata/src/package.json +++ b/amplify/backend/function/UpdateShowsMetadata/src/package.json @@ -7,7 +7,8 @@ "license": "Apache-2.0", "dependencies": { "axios": "^0.26.1", - "aws-sdk": "^2.1111.0" + "aws-sdk": "^2.1111.0", + "p-limit": "^6.1.0" }, "devDependencies": { "@types/aws-lambda": "^8.10.92" diff --git a/amplify/backend/function/UpdateShowsMetadata/src/yarn.lock b/amplify/backend/function/UpdateShowsMetadata/src/yarn.lock index bf1be23..5fcfa6d 100644 --- a/amplify/backend/function/UpdateShowsMetadata/src/yarn.lock +++ b/amplify/backend/function/UpdateShowsMetadata/src/yarn.lock @@ -73,6 +73,13 @@ jmespath@0.16.0: resolved "https://registry.yarnpkg.com/jmespath/-/jmespath-0.16.0.tgz#b15b0a85dfd4d930d43e69ed605943c802785076" integrity sha512-9FzQjJ7MATs1tSpnco1K6ayiYE3figslrXA72G2HQ/n76RzvYlofyi5QM+iX4YRs/pu3yzxlVQSST23+dMDknw== +p-limit@^6.1.0: + version "6.1.0" + resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-6.1.0.tgz#d91f9364d3fdff89b0a45c70d04ad4e0df30a0e8" + integrity sha512-H0jc0q1vOzlEk0TqAKXKZxdl7kX3OFUzCnNVUnq5Pc3DGo0kpeaMuPqxQn235HibwBEb0/pm9dgKTjXy66fBkg== + dependencies: + yocto-queue "^1.1.1" + punycode@1.3.2: version "1.3.2" resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.3.2.tgz#9653a036fb7c1ee42342f2325cceefea3926c48d" @@ -118,3 +125,8 @@ xmlbuilder@~9.0.1: version "9.0.7" resolved "https://registry.yarnpkg.com/xmlbuilder/-/xmlbuilder-9.0.7.tgz#132ee63d2ec5565c557e20f4c22df9aca686b10d" integrity sha1-Ey7mPS7FVlxVfiD0wi35rKaGsQ0= + +yocto-queue@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-1.1.1.tgz#fef65ce3ac9f8a32ceac5a634f74e17e5b232110" + integrity sha512-b4JR1PFR10y1mKjhHY9LaGo6tmrgjit7hxVIeAmyMw3jegXR4dhYqLaQF5zMXZxY7tLpMyJeLjr1C4rLmkVe8g== diff --git a/amplify/backend/function/universalratings5a980c73PostConfirmation/universalratings5a980c73PostConfirmation-cloudformation-template.json b/amplify/backend/function/universalratings5a980c73PostConfirmation/universalratings5a980c73PostConfirmation-cloudformation-template.json index 42a5afb..c6cef78 100644 --- a/amplify/backend/function/universalratings5a980c73PostConfirmation/universalratings5a980c73PostConfirmation-cloudformation-template.json +++ b/amplify/backend/function/universalratings5a980c73PostConfirmation/universalratings5a980c73PostConfirmation-cloudformation-template.json @@ -104,7 +104,7 @@ "Arn" ] }, - "Runtime": "nodejs14.x", + "Runtime": "nodejs18.x", "Timeout": 25, "Code": { "S3Bucket": { diff --git a/amplify/backend/types/amplify-dependent-resources-ref.d.ts b/amplify/backend/types/amplify-dependent-resources-ref.d.ts index 3443f15..72aefe5 100644 --- a/amplify/backend/types/amplify-dependent-resources-ref.d.ts +++ b/amplify/backend/types/amplify-dependent-resources-ref.d.ts @@ -1,47 +1,54 @@ export type AmplifyDependentResourcesAttributes = { - "auth": { - "universalratings5a980c73": { - "IdentityPoolId": "string", - "IdentityPoolName": "string", - "UserPoolId": "string", - "UserPoolArn": "string", - "UserPoolName": "string", - "AppClientIDWeb": "string", - "AppClientID": "string", - "CreatedSNSRole": "string" - }, - "userPoolGroups": { - "AdminGroupRole": "string" - } + "api": { + "universalratings": { + "GraphQLAPIEndpointOutput": "string", + "GraphQLAPIIdOutput": "string" + } + }, + "auth": { + "universalratings5a980c73": { + "AppClientID": "string", + "AppClientIDWeb": "string", + "CreatedSNSRole": "string", + "IdentityPoolId": "string", + "IdentityPoolName": "string", + "UserPoolArn": "string", + "UserPoolId": "string", + "UserPoolName": "string" + }, + "userPoolGroups": { + "AdminGroupRole": "string" + } + }, + "function": { + "ShowAddedDiscordWebhookPublisher": { + "Arn": "string", + "LambdaExecutionRole": "string", + "LambdaExecutionRoleArn": "string", + "Name": "string", + "Region": "string" + }, + "ShowChangedAlgoliaUpdater": { + "Arn": "string", + "LambdaExecutionRole": "string", + "LambdaExecutionRoleArn": "string", + "Name": "string", + "Region": "string" }, - "api": { - "universalratings": { - "GraphQLAPIIdOutput": "string", - "GraphQLAPIEndpointOutput": "string" - } + "UpdateShowsMetadata": { + "Arn": "string", + "CloudWatchEventRule": "string", + "LambdaExecutionRole": "string", + "LambdaExecutionRoleArn": "string", + "Name": "string", + "Region": "string" }, - "function": { - "universalratings5a980c73PostConfirmation": { - "Name": "string", - "Arn": "string", - "LambdaExecutionRole": "string", - "Region": "string", - "LambdaExecutionRoleArn": "string" - }, - "ShowAddedDiscordWebhookPublisher": { - "Name": "string", - "Arn": "string", - "Region": "string", - "LambdaExecutionRole": "string", - "LambdaExecutionRoleArn": "string" - }, - "UpdateShowsMetadata": { - "Name": "string", - "Arn": "string", - "Region": "string", - "LambdaExecutionRole": "string", - "CloudWatchEventRule": "string", - "LambdaExecutionRoleArn": "string" - } + "universalratings5a980c73PostConfirmation": { + "Arn": "string", + "LambdaExecutionRole": "string", + "LambdaExecutionRoleArn": "string", + "Name": "string", + "Region": "string" } + } } \ No newline at end of file diff --git a/amplify/team-provider-info.json b/amplify/team-provider-info.json index 7103362..dc51f8c 100644 --- a/amplify/team-provider-info.json +++ b/amplify/team-provider-info.json @@ -28,11 +28,22 @@ "s3Key": "amplify-builds/ShowAddedDiscordWebhookPublisher-6644444d326144376c6a-build.zip" }, "UpdateShowsMetadata": { + "deploymentBucketName": "amplify-amplify6604b332b57b4-staging-21636-deployment", + "s3Key": "amplify-builds/UpdateShowsMetadata-45493251566163333034-build.zip", "tmdbApiKey": "TMDB_API_KEY_VALUE", "omdbApiKey": "OMDB_API_KEY_VALUE", + "awsNodejsConnectionReuseEnabled": "1" + }, + "ShowChangedAlgoliaUpdater": { + "algoliaAppId": "ALGOLIA_APP_ID_VALUE", + "algoliaApiKey": "ALGOLIA_API_KEY_VALUE", + "secretsPathAmplifyAppId": "d9ooeidl73fpk", "deploymentBucketName": "amplify-amplify6604b332b57b4-staging-21636-deployment", - "s3Key": "amplify-builds/UpdateShowsMetadata-376e52585a7a2f353170-build.zip" + "s3Key": "amplify-builds/ShowChangedAlgoliaUpdater-6447384974344a7a6c77-build.zip" } + }, + "api": { + "universalratings": {} } } } diff --git a/package.json b/package.json index dfba133..0edef18 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "universal-ratings", - "version": "1.21.1", + "version": "1.21.4", "description": "A site for rating TV shows and movies", "repository": { "type": "git", @@ -32,7 +32,6 @@ "aws-amplify": "^4.2.10", "axios": "^0.21.4", "clsx": "^1.1.1", - "graphql-algolia-transformer": "^2.0.0", "lodash.debounce": "^4.0.8", "lru-cache": "^6.0.0", "moment": "^2.29.2", diff --git a/pages/_app.js b/pages/_app.js index b5d5711..fa49d9f 100644 --- a/pages/_app.js +++ b/pages/_app.js @@ -4,7 +4,7 @@ import API, { graphqlOperation } from '@aws-amplify/api'; import { getUser } from '../src/graphql/custom-queries.js'; import { AmplifyAuthContainer, AmplifyAuthenticator } from '@aws-amplify/ui-react'; import { AuthState } from '@aws-amplify/ui-components'; -import amplify from 'aws-amplify'; +import { Amplify as amplify } from 'aws-amplify'; import amplifyConfig from '../src/aws-exports'; import { ThemeProvider } from '../components/ThemeProvider.jsx'; import '../resources/styles/global.css'; diff --git a/pages/index.jsx b/pages/index.jsx index cbe0641..5c54ed8 100644 --- a/pages/index.jsx +++ b/pages/index.jsx @@ -609,6 +609,19 @@ const Index = ({ authedUser }) => { } }; + /** + * Converts a watchlisted show to a rated show, by updating the source and rating. + * + * @param {Object} watchlistedShow - watchlisted show to convert to rated show + * @param {string} showId - ID of the show to update + */ + const convertToRatedShow = (watchlistedShow, showId) => { + API.graphql(graphqlOperation(updateShow, { input: { ...watchlistedShow, id: showId } })) + .catch((err) => { + console.error('Failed to convert watchlisted show to rated show. ', err); + }); + }; + /** * Creates a rated show. * @@ -627,13 +640,14 @@ const Index = ({ authedUser }) => { await maybeAddShowMetadata(show); + const isWatchlisted = show.rating === 0 || watchlistIdx !== -1 || await isInAnyUsersWatchlist(show.id); const ratedShow = { rating, source: rating ? 'UR' : 'WL' }; - if (rating && show.rating === 0) { // unrated WL item - API.graphql(graphqlOperation(updateShow, { input: { ...ratedShow, id: show.id } })); + if (rating && isWatchlisted) { // unrated WL item + convertToRatedShow(ratedShow, show.id); } else { // unrated show API.graphql(graphqlOperation(createShow, { input: { ...show, ...ratedShow } })) .catch((err) => { diff --git a/resources/images/plex.svg b/resources/images/plex.svg index 68da5ec..65697a6 100644 --- a/resources/images/plex.svg +++ b/resources/images/plex.svg @@ -1,4 +1,4 @@ -